Compare commits
4 Commits
38c1c68924
...
fix/db-mem
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf9bcba1ac | ||
| 255ba46a4d | |||
| 10285933a0 | |||
| 543388e18b |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mosaic/gateway",
|
"name": "@mosaic/gateway",
|
||||||
"version": "0.1.0",
|
"version": "0.0.6",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.mosaicstack.dev/mosaic/mosaic-stack.git",
|
"url": "https://git.mosaicstack.dev/mosaic/mosaic-stack.git",
|
||||||
|
|||||||
@@ -22,12 +22,13 @@
|
|||||||
- `pnpm --filter @mosaic/mosaic test -- __tests__/update-checker.test.ts`
|
- `pnpm --filter @mosaic/mosaic test -- __tests__/update-checker.test.ts`
|
||||||
- `pnpm exec eslint --no-warn-ignored packages/mosaic/src/runtime/update-checker.ts packages/mosaic/src/cli.ts packages/mosaic/src/index.ts packages/mosaic/__tests__/update-checker.test.ts packages/cli/src/cli.ts`
|
- `pnpm exec eslint --no-warn-ignored packages/mosaic/src/runtime/update-checker.ts packages/mosaic/src/cli.ts packages/mosaic/src/index.ts packages/mosaic/__tests__/update-checker.test.ts packages/cli/src/cli.ts`
|
||||||
- `pnpm --filter @mosaic/mosaic lint`
|
- `pnpm --filter @mosaic/mosaic lint`
|
||||||
|
- pre-push hooks: `typecheck`, `lint`, `format:check`
|
||||||
|
|
||||||
## Review
|
## Review
|
||||||
|
|
||||||
- Manual review of the updater diff caught and fixed a cache regression where fallback results would lose the resolved package target on subsequent cached checks.
|
- Manual review of the updater diff caught and fixed a cache regression where fallback results would lose the resolved package target on subsequent cached checks.
|
||||||
|
|
||||||
## Blockers / Risks
|
## Risks / Notes
|
||||||
|
|
||||||
- `pnpm --filter @mosaic/mosaic typecheck` fails in this worktree on pre-existing unresolved workspace imports such as `@mosaic/quality-rails`, `@mosaic/prdy`, and `@mosaic/types`; this is broader than the updater change.
|
- Direct `pnpm --filter @mosaic/mosaic typecheck` and `pnpm --filter @mosaic/cli ...` checks were not representative in this worktree because `packages/cli` is excluded from `pnpm-workspace.yaml` and the standalone package check lacked the built workspace dependency graph.
|
||||||
- `packages/cli` is excluded from `pnpm-workspace.yaml`, so package-script lint/typecheck are not runnable via normal workspace filters in this worktree.
|
- The repo's pre-push hooks provided the authoritative validation path here and passed: root `typecheck`, `lint`, and `format:check`.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mosaic/config",
|
"name": "@mosaic/config",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.mosaicstack.dev/mosaic/mosaic-stack.git",
|
"url": "https://git.mosaicstack.dev/mosaic/mosaic-stack.git",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mosaic/db",
|
"name": "@mosaic/db",
|
||||||
"version": "0.0.2",
|
"version": "0.0.3",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.mosaicstack.dev/mosaic/mosaic-stack.git",
|
"url": "https://git.mosaicstack.dev/mosaic/mosaic-stack.git",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mosaic/memory",
|
"name": "@mosaic/memory",
|
||||||
"version": "0.0.2",
|
"version": "0.0.3",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.mosaicstack.dev/mosaic/mosaic-stack.git",
|
"url": "https://git.mosaicstack.dev/mosaic/mosaic-stack.git",
|
||||||
|
|||||||
@@ -2,14 +2,34 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|||||||
import { semverLt, formatUpdateNotice } from '../src/runtime/update-checker.js';
|
import { semverLt, formatUpdateNotice } from '../src/runtime/update-checker.js';
|
||||||
import type { UpdateCheckResult } from '../src/runtime/update-checker.js';
|
import type { UpdateCheckResult } from '../src/runtime/update-checker.js';
|
||||||
|
|
||||||
const { execSyncMock } = vi.hoisted(() => ({
|
const { execSyncMock, cacheFiles } = vi.hoisted(() => ({
|
||||||
execSyncMock: vi.fn(),
|
execSyncMock: vi.fn(),
|
||||||
|
cacheFiles: new Map<string, string>(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('node:child_process', () => ({
|
vi.mock('node:child_process', () => ({
|
||||||
execSync: execSyncMock,
|
execSync: execSyncMock,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
vi.mock('node:fs', () => ({
|
||||||
|
existsSync: vi.fn((path: string) => cacheFiles.has(path)),
|
||||||
|
mkdirSync: vi.fn(),
|
||||||
|
readFileSync: vi.fn((path: string) => {
|
||||||
|
const value = cacheFiles.get(path);
|
||||||
|
if (value === undefined) {
|
||||||
|
throw new Error(`ENOENT: ${path}`);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}),
|
||||||
|
writeFileSync: vi.fn((path: string, content: string) => {
|
||||||
|
cacheFiles.set(path, content);
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('node:os', () => ({
|
||||||
|
homedir: vi.fn(() => '/mock-home'),
|
||||||
|
}));
|
||||||
|
|
||||||
async function importUpdateChecker() {
|
async function importUpdateChecker() {
|
||||||
vi.resetModules();
|
vi.resetModules();
|
||||||
return import('../src/runtime/update-checker.js');
|
return import('../src/runtime/update-checker.js');
|
||||||
@@ -40,6 +60,7 @@ describe('semverLt', () => {
|
|||||||
describe('formatUpdateNotice', () => {
|
describe('formatUpdateNotice', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
execSyncMock.mockReset();
|
execSyncMock.mockReset();
|
||||||
|
cacheFiles.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns empty string when up to date', () => {
|
it('returns empty string when up to date', () => {
|
||||||
@@ -124,4 +145,51 @@ describe('formatUpdateNotice', () => {
|
|||||||
expect(result.latest).toBe('0.0.17');
|
expect(result.latest).toBe('0.0.17');
|
||||||
expect(notice).toContain('@mosaic/mosaic@latest');
|
expect(notice).toContain('@mosaic/mosaic@latest');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not reuse a cached modern-package result for a legacy install', async () => {
|
||||||
|
let installedPackage = '@mosaic/mosaic';
|
||||||
|
|
||||||
|
execSyncMock.mockImplementation((command: string) => {
|
||||||
|
if (command.includes('ls -g --depth=0 --json')) {
|
||||||
|
return JSON.stringify({
|
||||||
|
dependencies:
|
||||||
|
installedPackage === '@mosaic/mosaic'
|
||||||
|
? { '@mosaic/mosaic': { version: '0.0.17' } }
|
||||||
|
: { '@mosaic/cli': { version: '0.0.16' } },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.includes('view @mosaic/mosaic version')) {
|
||||||
|
return installedPackage === '@mosaic/mosaic' ? '0.0.18' : '0.0.17';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.includes('view @mosaic/cli version')) {
|
||||||
|
throw new Error('not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Unexpected command: ${command}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const { checkForUpdate } = await importUpdateChecker();
|
||||||
|
|
||||||
|
const modernResult = checkForUpdate();
|
||||||
|
installedPackage = '@mosaic/cli';
|
||||||
|
const legacyResult = checkForUpdate();
|
||||||
|
|
||||||
|
expect(modernResult.currentPackage).toBe('@mosaic/mosaic');
|
||||||
|
expect(modernResult.targetPackage).toBe('@mosaic/mosaic');
|
||||||
|
expect(modernResult.latest).toBe('0.0.18');
|
||||||
|
|
||||||
|
expect(legacyResult.currentPackage).toBe('@mosaic/cli');
|
||||||
|
expect(legacyResult.targetPackage).toBe('@mosaic/mosaic');
|
||||||
|
expect(legacyResult.latest).toBe('0.0.17');
|
||||||
|
expect(execSyncMock).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('view @mosaic/cli version'),
|
||||||
|
expect.any(Object),
|
||||||
|
);
|
||||||
|
expect(execSyncMock).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining('view @mosaic/mosaic version'),
|
||||||
|
expect.any(Object),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mosaic/mosaic",
|
"name": "@mosaic/mosaic",
|
||||||
"version": "0.0.18",
|
"version": "0.0.19",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.mosaicstack.dev/mosaic/mosaic-stack.git",
|
"url": "https://git.mosaicstack.dev/mosaic/mosaic-stack.git",
|
||||||
|
|||||||
@@ -131,18 +131,20 @@ export function semverLt(a: string, b: string): boolean {
|
|||||||
/** Cache stores only the latest registry version (the expensive network call).
|
/** Cache stores only the latest registry version (the expensive network call).
|
||||||
* The installed version is always checked fresh — it's a local `npm ls`. */
|
* The installed version is always checked fresh — it's a local `npm ls`. */
|
||||||
interface RegistryCache {
|
interface RegistryCache {
|
||||||
|
currentPackage?: string;
|
||||||
latest: string;
|
latest: string;
|
||||||
targetPackage?: string;
|
targetPackage?: string;
|
||||||
checkedAt: string;
|
checkedAt: string;
|
||||||
registry: string;
|
registry: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readCache(): RegistryCache | null {
|
function readCache(currentPackage: string): RegistryCache | null {
|
||||||
try {
|
try {
|
||||||
if (!existsSync(CACHE_FILE)) return null;
|
if (!existsSync(CACHE_FILE)) return null;
|
||||||
const raw = JSON.parse(readFileSync(CACHE_FILE, 'utf-8')) as RegistryCache;
|
const raw = JSON.parse(readFileSync(CACHE_FILE, 'utf-8')) as RegistryCache;
|
||||||
const age = Date.now() - new Date(raw.checkedAt).getTime();
|
const age = Date.now() - new Date(raw.checkedAt).getTime();
|
||||||
if (age > CACHE_TTL_MS) return null;
|
if (age > CACHE_TTL_MS) return null;
|
||||||
|
if ((raw.currentPackage || '') !== currentPackage) return null;
|
||||||
return raw;
|
return raw;
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
@@ -223,7 +225,7 @@ export function checkForUpdate(options?: { skipCache?: boolean }): UpdateCheckRe
|
|||||||
let checkedAt: string;
|
let checkedAt: string;
|
||||||
|
|
||||||
if (!options?.skipCache) {
|
if (!options?.skipCache) {
|
||||||
const cached = readCache();
|
const cached = readCache(currentInfo.name);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
latestInfo = {
|
latestInfo = {
|
||||||
name: cached.targetPackage || MODERN_PKG,
|
name: cached.targetPackage || MODERN_PKG,
|
||||||
@@ -234,6 +236,7 @@ export function checkForUpdate(options?: { skipCache?: boolean }): UpdateCheckRe
|
|||||||
latestInfo = getLatestVersion(currentInfo.name);
|
latestInfo = getLatestVersion(currentInfo.name);
|
||||||
checkedAt = new Date().toISOString();
|
checkedAt = new Date().toISOString();
|
||||||
writeCache({
|
writeCache({
|
||||||
|
currentPackage: currentInfo.name,
|
||||||
latest: latestInfo.version,
|
latest: latestInfo.version,
|
||||||
targetPackage: latestInfo.name,
|
targetPackage: latestInfo.name,
|
||||||
checkedAt,
|
checkedAt,
|
||||||
@@ -244,6 +247,7 @@ export function checkForUpdate(options?: { skipCache?: boolean }): UpdateCheckRe
|
|||||||
latestInfo = getLatestVersion(currentInfo.name);
|
latestInfo = getLatestVersion(currentInfo.name);
|
||||||
checkedAt = new Date().toISOString();
|
checkedAt = new Date().toISOString();
|
||||||
writeCache({
|
writeCache({
|
||||||
|
currentPackage: currentInfo.name,
|
||||||
latest: latestInfo.version,
|
latest: latestInfo.version,
|
||||||
targetPackage: latestInfo.name,
|
targetPackage: latestInfo.name,
|
||||||
checkedAt,
|
checkedAt,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mosaic/queue",
|
"name": "@mosaic/queue",
|
||||||
"version": "0.0.2",
|
"version": "0.0.3",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.mosaicstack.dev/mosaic/mosaic-stack.git",
|
"url": "https://git.mosaicstack.dev/mosaic/mosaic-stack.git",
|
||||||
|
|||||||
Reference in New Issue
Block a user