deepspace 0.19.0 → 0.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/dist/cli.js +558 -261
- package/dist/cli.js.map +1 -1
- package/dist/feature-installer.js +14 -5
- package/dist/feature-installer.js.map +1 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.js +42 -11
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js.map +1 -1
- package/dist/server.d.ts +12 -7
- package/dist/server.js +63 -19
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +9 -2
- package/dist/worker.js +63 -19
- package/dist/worker.js.map +1 -1
- package/features/admin-page/feature.json +3 -4
- package/features/admin-page/src/AdminPage.tsx +4 -4
- package/features/cron/feature.json +8 -5
- package/features/documents/feature.json +2 -1
- package/features/documents/src/DocumentsEditorPage.tsx +31 -10
- package/features/documents/src/DocumentsListPage.tsx +59 -26
- package/features/documents/src/InviteDialog.tsx +49 -14
- package/features/documents/src/LibrarySidebar.tsx +92 -84
- package/features/documents/src/__tests__/documents-presence-access.test.ts +15 -2
- package/features/documents/src/documents-library-types.ts +6 -0
- package/features/tasks/src/TasksPage.tsx +1 -1
- package/features/tasks/src/tasks-schema.ts +1 -1
- package/package.json +5 -5
- package/features/cron/src/cron.ts +0 -25
|
@@ -316,7 +316,9 @@ function integrateCode(config, targetDir, emit) {
|
|
|
316
316
|
if (insertion.import && !content.includes(insertion.import)) {
|
|
317
317
|
const importIndex = content.search(/^import\s/m);
|
|
318
318
|
content = `${content.slice(0, importIndex < 0 ? 0 : importIndex)}${insertion.import}
|
|
319
|
-
${content.slice(
|
|
319
|
+
${content.slice(
|
|
320
|
+
importIndex < 0 ? 0 : importIndex
|
|
321
|
+
)}`;
|
|
320
322
|
}
|
|
321
323
|
const currentMarkerIndex = content.indexOf(insertion.marker);
|
|
322
324
|
const insertAt = insertion.placement === "before" ? currentMarkerIndex : currentMarkerIndex + insertion.marker.length;
|
|
@@ -470,15 +472,22 @@ ${css}`;
|
|
|
470
472
|
return unresolved;
|
|
471
473
|
}
|
|
472
474
|
function unresolvedRoute(route, config, reason, emit) {
|
|
475
|
+
const roles = routeRolesSuffix(route);
|
|
473
476
|
emit("");
|
|
474
477
|
emit(" Add manually to src/nav.ts:");
|
|
475
|
-
emit(` { path: '${route.path}', label: '${config.name}' },`);
|
|
478
|
+
emit(` { path: '${route.path}', label: '${config.name}'${roles} },`);
|
|
476
479
|
return {
|
|
477
480
|
area: "nav",
|
|
478
481
|
reason,
|
|
479
|
-
steps: [
|
|
482
|
+
steps: [
|
|
483
|
+
`Add { path: '${route.path}', label: '${config.name}'${roles} } to the exported nav array.`
|
|
484
|
+
]
|
|
480
485
|
};
|
|
481
486
|
}
|
|
487
|
+
function routeRolesSuffix(route) {
|
|
488
|
+
const roles = route.roles?.length ? route.roles : route.protected === false ? [] : ["member"];
|
|
489
|
+
return roles.length ? `, roles: [${roles.map((role) => `'${role}' as Role`).join(", ")}]` : "";
|
|
490
|
+
}
|
|
482
491
|
function integrateRoute(config, targetDir, emit) {
|
|
483
492
|
if (!config.route) return [];
|
|
484
493
|
const navPath = join2(targetDir, "src", "nav.ts");
|
|
@@ -502,7 +511,7 @@ function integrateRoute(config, targetDir, emit) {
|
|
|
502
511
|
)
|
|
503
512
|
];
|
|
504
513
|
}
|
|
505
|
-
const roles = config.route
|
|
514
|
+
const roles = routeRolesSuffix(config.route);
|
|
506
515
|
const entry = ` { path: '${routePath}', label: '${config.name}'${roles} },`;
|
|
507
516
|
content = content.replace(NAV_MARKER, `${NAV_MARKER}
|
|
508
517
|
${entry}`);
|
|
@@ -698,7 +707,7 @@ function installFeature(options) {
|
|
|
698
707
|
);
|
|
699
708
|
if (config.instructions?.length) {
|
|
700
709
|
emit("");
|
|
701
|
-
emit("---
|
|
710
|
+
emit("--- Next steps ---");
|
|
702
711
|
emit("");
|
|
703
712
|
config.instructions.forEach((instruction, index) => {
|
|
704
713
|
emit(`${index + 1}. ${instruction}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/commands/feature-installer.ts","../src/cli/lib/package-manager.ts"],"sourcesContent":["/**\n * The feature catalog and installer.\n *\n * This is a separate build entry because `add` loads the copy shipped by the\n * target app's installed `deepspace` package. That keeps feature files,\n * manifest interpretation, and mutations on the app-pinned SDK version while\n * leaving the public command/output contract in add.ts.\n */\n\nimport {\n closeSync,\n copyFileSync,\n existsSync,\n fstatSync,\n mkdirSync,\n mkdtempSync,\n openSync,\n readFileSync,\n readSync,\n readdirSync,\n rmSync,\n writeFileSync,\n} from 'node:fs'\nimport { spawnSync } from 'node:child_process'\nimport { tmpdir } from 'node:os'\nimport { dirname, isAbsolute, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { detectPackageManager } from '../lib/package-manager'\n\nconst NAV_MARKER = '// ── Features add nav items below this line ──'\n\ninterface FeatureFile {\n src: string\n dest: string\n overwrite?: boolean\n}\n\ninterface FeatureSchema {\n exportName: string\n importPath: string\n spreadOperator?: boolean\n}\n\ninterface FeatureAction {\n exportName: string\n registerAs: string\n importPath: string\n}\n\ninterface FeatureCodeInsertion {\n file: string\n marker: string\n insert: string\n placement?: 'before' | 'after'\n import?: string\n}\n\ninterface FeatureWranglerIntegration {\n runWorkerFirst?: boolean\n rateLimits?: Array<{\n name: string\n namespaceId: string\n limit: number\n period: 10 | 60\n }>\n}\n\nexport interface FeatureConfig {\n id: string\n name: string\n category?: string\n description?: string\n details?: string\n internal?: boolean\n files: FeatureFile[]\n ignore?: string[]\n route?: { path?: string; protected?: boolean }\n schema?: FeatureSchema\n actions?: FeatureAction[]\n code?: FeatureCodeInsertion[]\n wrangler?: FeatureWranglerIntegration\n css?: string[]\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n instructions?: string[]\n patterns?: string[]\n agentNotes?: string[]\n example?: string\n [key: string]: unknown\n}\n\nexport interface FeatureCatalogEntry {\n id: string\n name: string\n category: string | null\n description: string | null\n}\n\ntype DependencySection = 'dependencies' | 'devDependencies'\n\ninterface DependencyAddition {\n name: string\n section: DependencySection\n version: unknown\n}\n\ntype IntegrationArea = 'schema' | 'actions' | 'code' | 'css' | 'nav' | 'wrangler'\n\ninterface UnresolvedIntegration {\n area: IntegrationArea\n reason: string\n steps: string[]\n}\n\ninterface InstallData extends Record<string, unknown> {\n feature: string\n dir: string\n installed: boolean\n observedChanges: {\n files: { created: string[]; preexisting: string[]; missing: string[] }\n dependencies: {\n added: DependencyAddition[]\n missingDeclarations: string[]\n installRequested: boolean\n diagnosticTail?: string\n }\n }\n unresolvedIntegrations: UnresolvedIntegration[]\n manifestGuidance: {\n instructions: string[]\n patterns: string[]\n agentNotes: string[]\n }\n}\n\ninterface InstallerAction {\n cwd: string\n argv: string[]\n}\n\nexport type FeatureInstallOutcome =\n | { ok: true; data: InstallData; action?: InstallerAction }\n | {\n ok: false\n code: 'unknown_feature'\n error: string\n data: {\n requestedFeature: string\n suggestedFeature: string | null\n availableFeatures: FeatureCatalogEntry[]\n }\n }\n | {\n ok: false\n code: 'dependency_install_failed' | 'manual_integration_required'\n error: string\n data: InstallData\n }\n\nexport type FeatureInstallerOutput = (line: string, stream?: 'stdout' | 'stderr') => void\n\nexport interface FeatureInstallOptions {\n feature: string\n targetDir: string\n installDependencies: boolean\n emit?: FeatureInstallerOutput\n}\n\n/** Find the package containing this source file or emitted build entry. */\nexport function featurePackageRoot(moduleUrl: string = import.meta.url): string {\n let dir = dirname(fileURLToPath(moduleUrl))\n for (;;) {\n const packagePath = join(dir, 'package.json')\n if (existsSync(packagePath)) {\n try {\n const pkg = JSON.parse(readFileSync(packagePath, 'utf-8')) as { name?: unknown }\n if (pkg.name === 'deepspace') return dir\n } catch {\n // Keep walking: a nearer unrelated package.json is not this package.\n }\n }\n const parent = dirname(dir)\n if (parent === dir) throw new Error('Could not locate the deepspace package root')\n dir = parent\n }\n}\n\nexport function hasFeatureCatalog(packageRoot: string = featurePackageRoot()): boolean {\n return existsSync(join(packageRoot, 'features'))\n}\n\nexport function readFeatureConfig(\n id: string,\n packageRoot: string = featurePackageRoot(),\n): FeatureConfig | null {\n const configPath = join(packageRoot, 'features', id, 'feature.json')\n if (!existsSync(configPath)) return null\n try {\n const config = JSON.parse(readFileSync(configPath, 'utf-8')) as Partial<FeatureConfig>\n if (\n typeof config.id !== 'string' ||\n typeof config.name !== 'string' ||\n !Array.isArray(config.files)\n ) {\n return null\n }\n return config as FeatureConfig\n } catch {\n return null\n }\n}\n\nexport function listFeatureConfigs(packageRoot: string = featurePackageRoot()): FeatureConfig[] {\n const featuresDir = join(packageRoot, 'features')\n if (!existsSync(featuresDir)) return []\n return readdirSync(featuresDir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => readFeatureConfig(entry.name, packageRoot))\n .filter((config): config is FeatureConfig => config !== null && !config.internal)\n .sort((a, b) => a.id.localeCompare(b.id))\n}\n\nexport function featureCatalog(packageRoot: string = featurePackageRoot()): FeatureCatalogEntry[] {\n return listFeatureConfigs(packageRoot).map((config) => ({\n id: config.id,\n name: config.name,\n category: config.category ?? null,\n description: config.description ?? null,\n }))\n}\n\nfunction editDistance(a: string, b: string): number {\n const row = Array.from({ length: b.length + 1 }, (_, index) => index)\n for (let i = 1; i <= a.length; i++) {\n let previous = row[0]\n row[0] = i\n for (let j = 1; j <= b.length; j++) {\n const current = row[j]\n row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1))\n previous = current\n }\n }\n return row[b.length]\n}\n\nexport function suggestFeature(\n featureId: string,\n packageRoot: string = featurePackageRoot(),\n): string | null {\n let closest: string | null = null\n let closestDistance = Number.POSITIVE_INFINITY\n for (const feature of listFeatureConfigs(packageRoot)) {\n const distance = editDistance(featureId, feature.id)\n if (distance < closestDistance) {\n closest = feature.id\n closestDistance = distance\n }\n }\n const threshold = Math.max(2, Math.floor(featureId.length / 3))\n return closest !== null && closestDistance <= threshold ? closest : null\n}\n\nfunction resolveTargetDir(raw: string): string {\n return isAbsolute(raw) ? raw : resolve(raw)\n}\n\nexport function resolveFeatureDestPath(fileDest: string, route: FeatureConfig['route']): string {\n const pages = 'src/pages/'\n const appPages = 'src/pages/(app)/'\n if (!fileDest.startsWith(pages) || fileDest.startsWith(appPages)) return fileDest\n const rest = fileDest.slice(pages.length)\n return route?.protected === false ? `${appPages}${rest}` : `${appPages}(protected)/${rest}`\n}\n\nfunction copyFile(src: string, dest: string): void {\n mkdirSync(dirname(dest), { recursive: true })\n copyFileSync(src, dest)\n}\n\nfunction copyFileWithImportShift(src: string, dest: string, levels: number): void {\n mkdirSync(dirname(dest), { recursive: true })\n const prefix = '../'.repeat(levels)\n const shifted = readFileSync(src, 'utf-8').replace(\n /(from\\s+|import\\s+|import\\()(['\"])(\\.\\.\\/)/g,\n `$1$2${prefix}$3`,\n )\n writeFileSync(dest, shifted)\n}\n\nfunction installFiles(\n packageRoot: string,\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): {\n copied: number\n skipped: number\n files: { created: string[]; preexisting: string[]; missing: string[] }\n} {\n let copied = 0\n let skipped = 0\n const created: string[] = []\n const preexisting: string[] = []\n const missing: string[] = []\n\n for (const file of config.files) {\n const srcPath = join(packageRoot, 'features', config.id, file.src)\n const resolvedDest = resolveFeatureDestPath(file.dest, config.route)\n const destPath = join(targetDir, resolvedDest)\n const existed = existsSync(destPath)\n if (existed) preexisting.push(resolvedDest)\n\n if (!existsSync(srcPath)) {\n emit(` Warning: source not found: ${file.src}`, 'stderr')\n if (!existed) missing.push(resolvedDest)\n continue\n }\n\n if (existed && !file.overwrite) {\n emit(` Exists (skipped): ${resolvedDest}`)\n skipped++\n continue\n }\n\n const wasRedirected = resolvedDest !== file.dest\n const shiftLevels = resolvedDest.split('/').length - file.dest.split('/').length\n if (wasRedirected) copyFileWithImportShift(srcPath, destPath, shiftLevels)\n else copyFile(srcPath, destPath)\n emit(` ${existed ? 'Overwrote' : 'Copied'}: ${resolvedDest}`)\n if (!existed) created.push(resolvedDest)\n copied++\n }\n\n return { copied, skipped, files: { created, preexisting, missing } }\n}\n\nfunction unresolvedSchema(\n schema: FeatureSchema,\n reason: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration {\n const entry = schema.spreadOperator ? `...${schema.exportName}` : schema.exportName\n emit('')\n emit(' Add manually to src/schemas.ts:')\n emit(` import { ${schema.exportName} } from '${schema.importPath}'`)\n emit(` // then add ${entry} to the schemas array`)\n return {\n area: 'schema',\n reason,\n steps: [\n `Add import { ${schema.exportName} } from '${schema.importPath}' to src/schemas.ts.`,\n `Add ${entry} to the exported schemas array.`,\n ],\n }\n}\n\nfunction integrateSchema(\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration[] {\n if (!config.schema) return []\n const schemasPath = join(targetDir, 'src', 'schemas.ts')\n if (!existsSync(schemasPath)) {\n return [unresolvedSchema(config.schema, 'src/schemas.ts was not found.', emit)]\n }\n\n let content = readFileSync(schemasPath, 'utf-8')\n const { exportName, importPath, spreadOperator } = config.schema\n const importPattern = new RegExp(`^import\\\\s+\\\\{[^}]*\\\\b${exportName}\\\\b`, 'm')\n if (importPattern.test(content)) {\n emit(` Schema already present: ${exportName}`)\n return []\n }\n if (\n !content.includes('export const schemas') ||\n !/export const schemas:\\s*CollectionSchema\\[\\]\\s*=\\s*\\[/.test(content)\n ) {\n const reason = 'src/schemas.ts has an unexpected structure.'\n emit(` Could not auto-integrate schema (${reason})`)\n return [unresolvedSchema(config.schema, reason, emit)]\n }\n\n const importLine = `import { ${exportName} } from '${importPath}'`\n const withImport = content.replace(\n /export const schemas/,\n `${importLine}\\n\\nexport const schemas`,\n )\n const schemaEntry = spreadOperator ? `...${exportName}` : exportName\n const withEntry = withImport.replace(\n /export const schemas:\\s*CollectionSchema\\[\\]\\s*=\\s*\\[/,\n `export const schemas: CollectionSchema[] = [\\n ${schemaEntry},`,\n )\n if (withImport === content || withEntry === withImport) {\n const reason = 'src/schemas.ts could not be updated safely.'\n emit(` Could not auto-integrate schema (${reason})`)\n return [unresolvedSchema(config.schema, reason, emit)]\n }\n content = withEntry\n writeFileSync(schemasPath, content)\n emit(` Schema integrated: ${exportName} -> schemas.ts`)\n return []\n}\n\nfunction unresolvedActions(\n actions: FeatureAction[],\n reason: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration {\n emit('')\n emit(' Add manually to src/actions/index.ts:')\n for (const action of actions) {\n emit(` import { ${action.exportName} } from '${action.importPath}'`)\n emit(` // then add '${action.registerAs}': ${action.exportName} to the actions object`)\n }\n return {\n area: 'actions',\n reason,\n steps: actions.flatMap((action) => [\n `Add import { ${action.exportName} } from '${action.importPath}' to src/actions/index.ts.`,\n `Register '${action.registerAs}': ${action.exportName} in the exported actions object.`,\n ]),\n }\n}\n\nfunction integrateActions(\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration[] {\n if (!config.actions?.length) return []\n const actionsPath = join(targetDir, 'src', 'actions', 'index.ts')\n if (!existsSync(actionsPath)) {\n return [unresolvedActions(config.actions, 'src/actions/index.ts was not found.', emit)]\n }\n\n let content = readFileSync(actionsPath, 'utf-8')\n const registryPattern =\n /export const actions:\\s*Record<string,\\s*ActionHandler(?:<[^>]*>)?>\\s*=\\s*\\{/\n if (!registryPattern.test(content)) {\n const reason = 'src/actions/index.ts has an unexpected structure.'\n emit(` Could not auto-integrate actions (${reason})`)\n return [unresolvedActions(config.actions, reason, emit)]\n }\n\n let integrated = 0\n const unresolved: UnresolvedIntegration[] = []\n for (const action of config.actions) {\n const importPattern = new RegExp(`^import\\\\s+\\\\{[^}]*\\\\b${action.exportName}\\\\b`, 'm')\n if (importPattern.test(content)) {\n emit(` Action already present: ${action.registerAs}`)\n continue\n }\n const importLine = `import { ${action.exportName} } from '${action.importPath}'`\n const withImport = content.replace(\n /export const actions:/,\n `${importLine}\\n\\nexport const actions:`,\n )\n const registryEntry = ` '${action.registerAs}': ${action.exportName},`\n const withEntry = withImport.replace(registryPattern, (match) => `${match}\\n${registryEntry}`)\n if (withImport === content || withEntry === withImport) {\n emit(` Could not integrate action: ${action.registerAs}`)\n unresolved.push(\n unresolvedActions(\n [action],\n `Action '${action.registerAs}' could not be registered safely.`,\n emit,\n ),\n )\n continue\n }\n content = withEntry\n integrated++\n emit(` Action integrated: ${action.registerAs} (${action.exportName})`)\n }\n if (integrated > 0) writeFileSync(actionsPath, content)\n return unresolved\n}\n\nfunction integrateCode(\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration[] {\n const unresolved: UnresolvedIntegration[] = []\n for (const insertion of config.code ?? []) {\n const targetPath = join(targetDir, insertion.file)\n if (!existsSync(targetPath)) {\n unresolved.push({\n area: 'code',\n reason: `${insertion.file} was not found.`,\n steps: [`Add ${insertion.insert.trim()} to ${insertion.file}.`],\n })\n continue\n }\n\n const original = readFileSync(targetPath, 'utf-8')\n if (original.includes(insertion.insert.trim())) {\n emit(` Integration already present: ${insertion.file}`)\n continue\n }\n const markerIndex = original.indexOf(insertion.marker)\n if (markerIndex < 0) {\n unresolved.push({\n area: 'code',\n reason: `${insertion.file} does not contain the expected insertion point.`,\n steps: [\n ...(insertion.import ? [`Add ${insertion.import} to ${insertion.file}.`] : []),\n `Add ${insertion.insert.trim()} to ${insertion.file}.`,\n ],\n })\n continue\n }\n\n let content = original\n if (insertion.import && !content.includes(insertion.import)) {\n const importIndex = content.search(/^import\\s/m)\n content = `${content.slice(0, importIndex < 0 ? 0 : importIndex)}${insertion.import}\\n${\n content.slice(importIndex < 0 ? 0 : importIndex)\n }`\n }\n const currentMarkerIndex = content.indexOf(insertion.marker)\n const insertAt =\n insertion.placement === 'before'\n ? currentMarkerIndex\n : currentMarkerIndex + insertion.marker.length\n const prefix = insertion.placement === 'before' ? '' : '\\n'\n const suffix = insertion.placement === 'before' ? '\\n' : ''\n content = `${content.slice(0, insertAt)}${prefix}${insertion.insert}${suffix}${content.slice(insertAt)}`\n writeFileSync(targetPath, content)\n emit(` Integrated: ${insertion.file}`)\n }\n return unresolved\n}\n\nfunction integrateIgnore(config: FeatureConfig, targetDir: string): void {\n if (!config.ignore?.length) return\n const targetPath = join(targetDir, '.gitignore')\n const current = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : ''\n const present = new Set(current.split(/\\r?\\n/).map((line) => line.trim()))\n const missing = config.ignore.filter((pattern) => !present.has(pattern))\n if (missing.length === 0) return\n const separator = current === '' || current.endsWith('\\n') ? '' : '\\n'\n writeFileSync(targetPath, `${current}${separator}${missing.join('\\n')}\\n`)\n}\n\nfunction integrateWrangler(\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration[] {\n const runWorkerFirst = config.wrangler?.runWorkerFirst === true\n const rateLimits = config.wrangler?.rateLimits ?? []\n if (!runWorkerFirst && rateLimits.length === 0) return []\n const targetPath = join(targetDir, 'wrangler.toml')\n const steps = [\n ...(runWorkerFirst ? ['Set assets.run_worker_first = true in wrangler.toml.'] : []),\n ...rateLimits.map((binding) => `Add the ${binding.name} rate-limit binding to wrangler.toml.`),\n ]\n if (!existsSync(targetPath)) {\n return [{ area: 'wrangler', reason: 'wrangler.toml was not found.', steps }]\n }\n\n let source = readFileSync(targetPath, 'utf-8')\n if (runWorkerFirst && !/^\\[assets\\]\\s*$/m.test(source)) {\n return [{ area: 'wrangler', reason: 'wrangler.toml has no [assets] section.', steps }]\n }\n\n let changed = false\n if (runWorkerFirst) {\n const updated = setRunWorkerFirst(source)\n changed ||= updated !== source\n source = updated\n }\n\n const scopes = ['', ...wranglerEnvironmentNames(source)]\n for (const scope of scopes) {\n for (const binding of rateLimits) {\n if (hasRateLimitBinding(source, scope, binding.name)) continue\n source = `${source.trimEnd()}\\n\\n${rateLimitBindingBlock(scope, binding)}\\n`\n changed = true\n }\n }\n\n if (changed) {\n writeFileSync(targetPath, source)\n emit(' Integrated: wrangler.toml')\n } else emit(' Integration already present: wrangler.toml')\n return []\n}\n\nfunction setRunWorkerFirst(source: string): string {\n const headers = [...source.matchAll(/^\\[(?:env\\.[^\\]]+\\.)?assets\\]\\s*$/gm)]\n for (const header of headers.reverse()) {\n const start = header.index\n const bodyStart = start + header[0].length\n const nextHeader = source.slice(bodyStart).search(/^\\[\\[?[^\\]]+\\]\\]?\\s*$/m)\n const end = nextHeader < 0 ? source.length : bodyStart + nextHeader\n const section = source.slice(start, end)\n if (/^\\s*run_worker_first\\s*=\\s*true(?:\\s*#.*)?$/m.test(section)) continue\n // Quoted strings are consumed atomically so a `]` inside a route string\n // cannot truncate the array match.\n const setting =\n /^(\\s*)run_worker_first\\s*=\\s*(?:false|\\[(?:[^\\]\"']|\"(?:[^\"\\\\]|\\\\[\\s\\S])*\"|'[^']*')*\\])(\\s*(?:#.*)?)$/m\n const updated = setting.test(section)\n ? section.replace(setting, '$1run_worker_first = true$2')\n : section.replace(/^\\[(?:env\\.[^\\]]+\\.)?assets\\]\\s*$/m, '$&\\nrun_worker_first = true')\n source = source.slice(0, start) + updated + source.slice(end)\n }\n return source\n}\n\nfunction wranglerEnvironmentNames(source: string): string[] {\n return [\n ...new Set(\n [...source.matchAll(/^\\[\\[?env\\.([^.\\]]+)(?:\\.|\\]\\]?\\s*$)/gm)].map((match) => match[1]),\n ),\n ]\n}\n\nfunction hasRateLimitBinding(source: string, scope: string, name: string): boolean {\n const header = scope ? `[[env.${scope}.ratelimits]]` : '[[ratelimits]]'\n // Tolerate hand-reformatted spacing and either TOML string quoting so a\n // reinstall never appends a duplicate block Wrangler would reject.\n const escaped = name.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const namePattern = new RegExp(\n `^\\\\s*name\\\\s*=\\\\s*(?:\"${escaped}\"|'${escaped}')\\\\s*(?:#.*)?$`,\n 'm',\n )\n let offset = 0\n while ((offset = source.indexOf(header, offset)) >= 0) {\n const end = source.slice(offset + header.length).search(/^\\[/m)\n const blockEnd = end < 0 ? source.length : offset + header.length + end\n if (namePattern.test(source.slice(offset, blockEnd))) return true\n offset += header.length\n }\n return false\n}\n\nfunction rateLimitBindingBlock(\n scope: string,\n binding: NonNullable<FeatureWranglerIntegration['rateLimits']>[number],\n): string {\n const header = scope ? `[[env.${scope}.ratelimits]]` : '[[ratelimits]]'\n return [\n header,\n `name = ${JSON.stringify(binding.name)}`,\n `namespace_id = ${JSON.stringify(binding.namespaceId)}`,\n `simple = { limit = ${binding.limit}, period = ${binding.period} }`,\n ].join('\\n')\n}\n\nfunction integrateCss(\n packageRoot: string,\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration[] {\n if (!config.css?.length) return []\n const stylesPath = join(targetDir, 'src', 'styles.css')\n if (!existsSync(stylesPath)) {\n emit(' Warning: Cannot integrate CSS — src/styles.css not found')\n return [\n {\n area: 'css',\n reason: 'src/styles.css was not found.',\n steps: config.css.map(\n (cssFile) =>\n `Append node_modules/deepspace/features/${config.id}/${cssFile} to src/styles.css.`,\n ),\n },\n ]\n }\n\n let styles = readFileSync(stylesPath, 'utf-8')\n let integrated = 0\n const unresolved: UnresolvedIntegration[] = []\n for (const cssFile of config.css) {\n const srcPath = join(packageRoot, 'features', config.id, cssFile)\n if (!existsSync(srcPath)) {\n emit(` Warning: CSS source not found: ${cssFile}`, 'stderr')\n unresolved.push({\n area: 'css',\n reason: `Feature CSS source '${cssFile}' was not found in the installed package.`,\n steps: ['Reinstall the current deepspace package, then retry this feature installation.'],\n })\n continue\n }\n const css = readFileSync(srcPath, 'utf-8')\n const fingerprint = css.match(/\\.([\\w-]+)\\s*\\{|@keyframes\\s+([\\w-]+)/)\n const marker = fingerprint?.[1] ?? fingerprint?.[2]\n if (marker && styles.includes(marker)) {\n emit(` CSS already present: ${cssFile} (found .${marker})`)\n continue\n }\n styles += `\\n${css}`\n integrated++\n emit(` CSS integrated: ${cssFile} -> styles.css`)\n }\n if (integrated > 0) writeFileSync(stylesPath, styles)\n return unresolved\n}\n\nfunction unresolvedRoute(\n route: NonNullable<FeatureConfig['route']>,\n config: FeatureConfig,\n reason: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration {\n emit('')\n emit(' Add manually to src/nav.ts:')\n emit(` { path: '${route.path}', label: '${config.name}' },`)\n return {\n area: 'nav',\n reason,\n steps: [`Add { path: '${route.path}', label: '${config.name}' } to the exported nav array.`],\n }\n}\n\nfunction integrateRoute(\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration[] {\n if (!config.route) return []\n const navPath = join(targetDir, 'src', 'nav.ts')\n if (!existsSync(navPath)) {\n return [unresolvedRoute(config.route, config, 'src/nav.ts was not found.', emit)]\n }\n let content = readFileSync(navPath, 'utf-8')\n const routePath = config.route.path\n if (content.includes(`'${routePath}'`)) {\n emit(` Nav already present: ${routePath}`)\n return []\n }\n if (!content.includes(NAV_MARKER)) {\n emit(' Could not auto-wire nav (marker not found in nav.ts)')\n return [\n unresolvedRoute(\n config.route,\n config,\n 'The feature insertion marker was not found in src/nav.ts.',\n emit,\n ),\n ]\n }\n const roles = config.route.protected === false ? '' : \", roles: ['member' as Role]\"\n const entry = ` { path: '${routePath}', label: '${config.name}'${roles} },`\n content = content.replace(NAV_MARKER, `${NAV_MARKER}\\n${entry}`)\n writeFileSync(navPath, content)\n emit(` Nav wired: ${routePath} (${config.name})`)\n emit(' Route: automatic (file-based routing via src/pages/)')\n return []\n}\n\nconst INSTALL_DIAGNOSTIC_BYTES = 16 * 1024\n\ninterface PackageInstallRun {\n error?: Error\n status: number | null\n diagnosticTail?: string\n}\n\nfunction readDiagnosticTail(outputFd: number): string | undefined {\n const size = fstatSync(outputFd).size\n if (size === 0) return undefined\n const length = Math.min(size, INSTALL_DIAGNOSTIC_BYTES)\n const buffer = Buffer.alloc(length)\n readSync(outputFd, buffer, 0, length, size - length)\n const tail = buffer.toString('utf-8').trim()\n if (!tail) return undefined\n return size > length ? `[last ${length} bytes]\\n${tail}` : tail\n}\n\nfunction runPackageInstall(\n packageManager: string,\n targetDir: string,\n outputVisible: boolean,\n): PackageInstallRun {\n const options = {\n cwd: targetDir,\n shell: process.platform === 'win32',\n }\n if (outputVisible) {\n const child = spawnSync(packageManager, ['install'], { ...options, stdio: 'inherit' })\n return { error: child.error, status: child.status }\n }\n\n // Keep stdout reserved for the command's one-line JSON envelope. Both child\n // streams share a temporary file, then only a fixed-size tail enters memory.\n const captureDir = mkdtempSync(join(tmpdir(), 'deepspace-add-install-'))\n let outputFd: number | undefined\n try {\n outputFd = openSync(join(captureDir, 'output.log'), 'w+')\n const child = spawnSync(packageManager, ['install'], {\n ...options,\n stdio: ['ignore', outputFd, outputFd],\n })\n return {\n error: child.error,\n status: child.status,\n diagnosticTail: readDiagnosticTail(outputFd),\n }\n } finally {\n if (outputFd !== undefined) closeSync(outputFd)\n rmSync(captureDir, { recursive: true, force: true })\n }\n}\n\nfunction integrateDependencies(\n config: FeatureConfig,\n targetDir: string,\n runInstall: boolean,\n outputVisible: boolean,\n emit: FeatureInstallerOutput,\n): {\n added: DependencyAddition[]\n installRan: boolean\n installError?: string\n diagnosticTail?: string\n packageManager: string\n} {\n const packageManager = detectPackageManager(targetDir)\n const declarations: Array<{\n section: DependencySection\n values: Record<string, string> | undefined\n }> = [\n { section: 'dependencies', values: config.dependencies },\n { section: 'devDependencies', values: config.devDependencies },\n ]\n if (declarations.every(({ values }) => !values || Object.keys(values).length === 0)) {\n return { added: [], installRan: false, packageManager }\n }\n\n const packagePath = join(targetDir, 'package.json')\n if (!existsSync(packagePath)) {\n emit(' Could not patch package.json (file not found)')\n return { added: [], installRan: false, packageManager }\n }\n\n let pkg: Record<string, unknown> & {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n }\n try {\n pkg = JSON.parse(readFileSync(packagePath, 'utf-8')) as typeof pkg\n } catch (error) {\n emit(\n ` Could not patch package.json (parse error: ${error instanceof Error ? error.message : String(error)})`,\n )\n return { added: [], installRan: false, packageManager }\n }\n\n const added: DependencyAddition[] = []\n const skipped: string[] = []\n for (const { section, values } of declarations) {\n if (!values) continue\n pkg[section] ??= {}\n for (const [name, version] of Object.entries(values)) {\n if (pkg.dependencies?.[name] || pkg.devDependencies?.[name]) {\n if (!added.some((dependency) => dependency.name === name)) skipped.push(name)\n continue\n }\n pkg[section]![name] = version\n added.push({ name, section, version })\n }\n }\n\n if (added.length === 0) {\n emit(' Dependencies already present')\n } else {\n writeFileSync(packagePath, `${JSON.stringify(pkg, null, 2)}\\n`, 'utf-8')\n emit(` Added to package.json: ${added.map(({ name }) => name).join(', ')}`)\n if (skipped.length) emit(` Already present (kept your version): ${skipped.join(', ')}`)\n }\n\n if (!runInstall) return { added, installRan: false, packageManager }\n emit('')\n emit(` Installing dependencies with ${packageManager}...`)\n const child = runPackageInstall(packageManager, targetDir, outputVisible)\n if (child.error) {\n const error = child.error as NodeJS.ErrnoException\n const reason = error.code === 'ENOENT' ? `${packageManager} not found on PATH` : error.message\n return {\n added,\n installRan: false,\n installError: `Could not launch ${packageManager} (${reason}).`,\n diagnosticTail: child.diagnosticTail,\n packageManager,\n }\n }\n if (child.status !== 0) {\n const diagnostic = child.diagnosticTail ? `\\nDiagnostic tail:\\n${child.diagnosticTail}` : ''\n return {\n added,\n installRan: false,\n installError: `${packageManager} install did not complete (exit ${child.status ?? 'unknown'}).${diagnostic}`,\n diagnosticTail: child.diagnosticTail,\n packageManager,\n }\n }\n return { added, installRan: true, packageManager }\n}\n\nfunction dependencySnapshot(targetDir: string): {\n dependencies: Record<string, unknown>\n devDependencies: Record<string, unknown>\n} {\n try {\n const pkg = JSON.parse(readFileSync(join(targetDir, 'package.json'), 'utf-8')) as {\n dependencies?: Record<string, unknown>\n devDependencies?: Record<string, unknown>\n }\n return {\n dependencies: pkg.dependencies ?? {},\n devDependencies: pkg.devDependencies ?? {},\n }\n } catch {\n return { dependencies: {}, devDependencies: {} }\n }\n}\n\nfunction installedNavFeatures(packageRoot: string, targetDir: string): string[] {\n return listFeatureConfigs(packageRoot)\n .filter((feature) => feature.category === 'nav' && feature.files.length > 0)\n .filter((feature) => {\n const signature = resolveFeatureDestPath(feature.files[0].dest, feature.route)\n return existsSync(join(targetDir, signature))\n })\n .map((feature) => feature.id)\n}\n\nfunction warnNavConflicts(\n packageRoot: string,\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): void {\n if (config.category !== 'nav') return\n const present = installedNavFeatures(packageRoot, targetDir)\n if (present.length <= 1) return\n emit('')\n emit(`⚠ Multiple navigation features present (${present.join(', ')}).`)\n emit(' sidebar, topbar, and tree are mutually exclusive — keep one and remove the')\n emit(' others to avoid competing navigation in your layout.')\n}\n\nexport function installFeature(options: FeatureInstallOptions): FeatureInstallOutcome {\n const packageRoot = featurePackageRoot()\n const targetDir = resolveTargetDir(options.targetDir)\n const config = readFeatureConfig(options.feature, packageRoot)\n if (!config) {\n const suggestion = suggestFeature(options.feature, packageRoot)\n return {\n ok: false,\n code: 'unknown_feature',\n error: [\n `Unknown feature: ${options.feature}`,\n ...(suggestion ? [`Did you mean \"${suggestion}\"?`] : []),\n ].join('\\n'),\n data: {\n requestedFeature: options.feature,\n suggestedFeature: suggestion,\n availableFeatures: featureCatalog(packageRoot),\n },\n }\n }\n\n const emit: FeatureInstallerOutput = options.emit ?? (() => undefined)\n emit('')\n emit(`Installing: ${config.name}`)\n emit(` ${config.description ?? ''}`)\n emit(` Target: ${targetDir}`)\n\n const installedFiles = installFiles(packageRoot, config, targetDir, emit)\n integrateIgnore(config, targetDir)\n emit('')\n emit(\n `Copied ${installedFiles.copied} file(s)${\n installedFiles.skipped > 0 ? `, skipped ${installedFiles.skipped} existing` : ''\n }`,\n )\n\n const unresolvedIntegrations = [\n ...integrateSchema(config, targetDir, emit),\n ...integrateActions(config, targetDir, emit),\n ...integrateWrangler(config, targetDir, emit),\n ...integrateCode(config, targetDir, emit),\n ...integrateCss(packageRoot, config, targetDir, emit),\n ...integrateRoute(config, targetDir, emit),\n ]\n const dependencyResult = integrateDependencies(\n config,\n targetDir,\n options.installDependencies,\n options.emit !== undefined,\n emit,\n )\n\n if (config.instructions?.length) {\n emit('')\n emit('--- Manual wiring needed ---')\n emit('')\n config.instructions.forEach((instruction, index) => {\n emit(`${index + 1}. ${instruction}`)\n emit('')\n })\n }\n if (config.patterns?.length) {\n emit('--- Key patterns ---')\n emit('')\n config.patterns.forEach((pattern) => emit(`- ${pattern}`))\n emit('')\n }\n warnNavConflicts(packageRoot, config, targetDir, emit)\n\n const dependenciesAfter = dependencySnapshot(targetDir)\n const declaredDependencies = [\n ...Object.keys(config.dependencies ?? {}),\n ...Object.keys(config.devDependencies ?? {}),\n ]\n const missingDeclarations = declaredDependencies.filter(\n (name) =>\n !(name in dependenciesAfter.dependencies) && !(name in dependenciesAfter.devDependencies),\n )\n const installed =\n installedFiles.files.missing.length === 0 &&\n missingDeclarations.length === 0 &&\n unresolvedIntegrations.length === 0 &&\n dependencyResult.installError === undefined\n const dependencyInstallRequired =\n installed &&\n dependencyResult.added.length > 0 &&\n !options.installDependencies &&\n !dependencyResult.installRan\n\n const data: InstallData = {\n feature: config.id,\n dir: targetDir,\n installed,\n observedChanges: {\n files: installedFiles.files,\n dependencies: {\n added: dependencyResult.added,\n missingDeclarations,\n installRequested: options.installDependencies,\n ...(dependencyResult.diagnosticTail\n ? { diagnosticTail: dependencyResult.diagnosticTail }\n : {}),\n },\n },\n unresolvedIntegrations,\n manifestGuidance: {\n instructions: config.instructions ?? [],\n patterns: config.patterns ?? [],\n agentNotes: config.agentNotes ?? [],\n },\n }\n if (dependencyResult.installError) {\n return {\n ok: false,\n code: 'dependency_install_failed',\n error: dependencyResult.installError,\n data,\n }\n }\n if (unresolvedIntegrations.length > 0) {\n const count = unresolvedIntegrations.length\n return {\n ok: false,\n code: 'manual_integration_required',\n error: `${count} app integration${count === 1 ? '' : 's'} require manual completion. Follow the unresolved integration steps, then run your checks.`,\n data,\n }\n }\n\n return {\n ok: true,\n data,\n ...(dependencyInstallRequired\n ? {\n action: {\n cwd: targetDir,\n argv: [dependencyResult.packageManager, 'install'],\n },\n }\n : {}),\n }\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nexport type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun'\n\n/** Resolve the repository's package manager from its explicit packageManager\n * field first, then its lockfile. npm is the conservative fallback. */\nexport function detectPackageManager(appDir: string): PackageManager {\n try {\n const pkg = JSON.parse(readFileSync(join(appDir, 'package.json'), 'utf-8')) as {\n packageManager?: unknown\n }\n const name = typeof pkg.packageManager === 'string' ? pkg.packageManager.split('@')[0] : ''\n if (name === 'npm' || name === 'pnpm' || name === 'yarn' || name === 'bun') return name\n } catch {\n // Missing/malformed package.json falls through to lockfile detection.\n }\n if (existsSync(join(appDir, 'pnpm-lock.yaml'))) return 'pnpm'\n if (existsSync(join(appDir, 'yarn.lock'))) return 'yarn'\n if (existsSync(join(appDir, 'bun.lock')) || existsSync(join(appDir, 'bun.lockb'))) return 'bun'\n return 'npm'\n}\n\nexport function installCommand(appDir: string): string {\n return `${detectPackageManager(appDir)} install`\n}\n"],"mappings":"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","names":["existsSync","readFileSync","join","join","existsSync","readFileSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/feature-installer.ts","../src/cli/lib/package-manager.ts"],"sourcesContent":["/**\n * The feature catalog and installer.\n *\n * This is a separate build entry because `add` loads the copy shipped by the\n * target app's installed `deepspace` package. That keeps feature files,\n * manifest interpretation, and mutations on the app-pinned SDK version while\n * leaving the public command/output contract in add.ts.\n */\n\nimport {\n closeSync,\n copyFileSync,\n existsSync,\n fstatSync,\n mkdirSync,\n mkdtempSync,\n openSync,\n readFileSync,\n readSync,\n readdirSync,\n rmSync,\n writeFileSync,\n} from 'node:fs'\nimport { spawnSync } from 'node:child_process'\nimport { tmpdir } from 'node:os'\nimport { dirname, isAbsolute, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { detectPackageManager } from '../lib/package-manager'\n\nconst NAV_MARKER = '// ── Features add nav items below this line ──'\n\ninterface FeatureFile {\n src: string\n dest: string\n overwrite?: boolean\n}\n\ninterface FeatureSchema {\n exportName: string\n importPath: string\n spreadOperator?: boolean\n}\n\ninterface FeatureAction {\n exportName: string\n registerAs: string\n importPath: string\n}\n\ninterface FeatureCodeInsertion {\n file: string\n marker: string\n insert: string\n placement?: 'before' | 'after'\n import?: string\n}\n\ninterface FeatureWranglerIntegration {\n runWorkerFirst?: boolean\n rateLimits?: Array<{\n name: string\n namespaceId: string\n limit: number\n period: 10 | 60\n }>\n}\n\nexport interface FeatureConfig {\n id: string\n name: string\n category?: string\n description?: string\n details?: string\n internal?: boolean\n files: FeatureFile[]\n ignore?: string[]\n route?: {\n path?: string\n protected?: boolean\n roles?: Array<'viewer' | 'member' | 'admin'>\n }\n schema?: FeatureSchema\n actions?: FeatureAction[]\n code?: FeatureCodeInsertion[]\n wrangler?: FeatureWranglerIntegration\n css?: string[]\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n instructions?: string[]\n patterns?: string[]\n agentNotes?: string[]\n example?: string\n [key: string]: unknown\n}\n\nexport interface FeatureCatalogEntry {\n id: string\n name: string\n category: string | null\n description: string | null\n}\n\ntype DependencySection = 'dependencies' | 'devDependencies'\n\ninterface DependencyAddition {\n name: string\n section: DependencySection\n version: unknown\n}\n\ntype IntegrationArea = 'schema' | 'actions' | 'code' | 'css' | 'nav' | 'wrangler'\n\ninterface UnresolvedIntegration {\n area: IntegrationArea\n reason: string\n steps: string[]\n}\n\ninterface InstallData extends Record<string, unknown> {\n feature: string\n dir: string\n installed: boolean\n observedChanges: {\n files: { created: string[]; preexisting: string[]; missing: string[] }\n dependencies: {\n added: DependencyAddition[]\n missingDeclarations: string[]\n installRequested: boolean\n diagnosticTail?: string\n }\n }\n unresolvedIntegrations: UnresolvedIntegration[]\n manifestGuidance: {\n instructions: string[]\n patterns: string[]\n agentNotes: string[]\n }\n}\n\ninterface InstallerAction {\n cwd: string\n argv: string[]\n}\n\nexport type FeatureInstallOutcome =\n | { ok: true; data: InstallData; action?: InstallerAction }\n | {\n ok: false\n code: 'unknown_feature'\n error: string\n data: {\n requestedFeature: string\n suggestedFeature: string | null\n availableFeatures: FeatureCatalogEntry[]\n }\n }\n | {\n ok: false\n code: 'dependency_install_failed' | 'manual_integration_required'\n error: string\n data: InstallData\n }\n\nexport type FeatureInstallerOutput = (line: string, stream?: 'stdout' | 'stderr') => void\n\nexport interface FeatureInstallOptions {\n feature: string\n targetDir: string\n installDependencies: boolean\n emit?: FeatureInstallerOutput\n}\n\n/** Find the package containing this source file or emitted build entry. */\nexport function featurePackageRoot(moduleUrl: string = import.meta.url): string {\n let dir = dirname(fileURLToPath(moduleUrl))\n for (;;) {\n const packagePath = join(dir, 'package.json')\n if (existsSync(packagePath)) {\n try {\n const pkg = JSON.parse(readFileSync(packagePath, 'utf-8')) as { name?: unknown }\n if (pkg.name === 'deepspace') return dir\n } catch {\n // Keep walking: a nearer unrelated package.json is not this package.\n }\n }\n const parent = dirname(dir)\n if (parent === dir) throw new Error('Could not locate the deepspace package root')\n dir = parent\n }\n}\n\nexport function hasFeatureCatalog(packageRoot: string = featurePackageRoot()): boolean {\n return existsSync(join(packageRoot, 'features'))\n}\n\nexport function readFeatureConfig(\n id: string,\n packageRoot: string = featurePackageRoot(),\n): FeatureConfig | null {\n const configPath = join(packageRoot, 'features', id, 'feature.json')\n if (!existsSync(configPath)) return null\n try {\n const config = JSON.parse(readFileSync(configPath, 'utf-8')) as Partial<FeatureConfig>\n if (\n typeof config.id !== 'string' ||\n typeof config.name !== 'string' ||\n !Array.isArray(config.files)\n ) {\n return null\n }\n return config as FeatureConfig\n } catch {\n return null\n }\n}\n\nexport function listFeatureConfigs(packageRoot: string = featurePackageRoot()): FeatureConfig[] {\n const featuresDir = join(packageRoot, 'features')\n if (!existsSync(featuresDir)) return []\n return readdirSync(featuresDir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => readFeatureConfig(entry.name, packageRoot))\n .filter((config): config is FeatureConfig => config !== null && !config.internal)\n .sort((a, b) => a.id.localeCompare(b.id))\n}\n\nexport function featureCatalog(packageRoot: string = featurePackageRoot()): FeatureCatalogEntry[] {\n return listFeatureConfigs(packageRoot).map((config) => ({\n id: config.id,\n name: config.name,\n category: config.category ?? null,\n description: config.description ?? null,\n }))\n}\n\nfunction editDistance(a: string, b: string): number {\n const row = Array.from({ length: b.length + 1 }, (_, index) => index)\n for (let i = 1; i <= a.length; i++) {\n let previous = row[0]\n row[0] = i\n for (let j = 1; j <= b.length; j++) {\n const current = row[j]\n row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1))\n previous = current\n }\n }\n return row[b.length]\n}\n\nexport function suggestFeature(\n featureId: string,\n packageRoot: string = featurePackageRoot(),\n): string | null {\n let closest: string | null = null\n let closestDistance = Number.POSITIVE_INFINITY\n for (const feature of listFeatureConfigs(packageRoot)) {\n const distance = editDistance(featureId, feature.id)\n if (distance < closestDistance) {\n closest = feature.id\n closestDistance = distance\n }\n }\n const threshold = Math.max(2, Math.floor(featureId.length / 3))\n return closest !== null && closestDistance <= threshold ? closest : null\n}\n\nfunction resolveTargetDir(raw: string): string {\n return isAbsolute(raw) ? raw : resolve(raw)\n}\n\nexport function resolveFeatureDestPath(fileDest: string, route: FeatureConfig['route']): string {\n const pages = 'src/pages/'\n const appPages = 'src/pages/(app)/'\n if (!fileDest.startsWith(pages) || fileDest.startsWith(appPages)) return fileDest\n const rest = fileDest.slice(pages.length)\n return route?.protected === false ? `${appPages}${rest}` : `${appPages}(protected)/${rest}`\n}\n\nfunction copyFile(src: string, dest: string): void {\n mkdirSync(dirname(dest), { recursive: true })\n copyFileSync(src, dest)\n}\n\nfunction copyFileWithImportShift(src: string, dest: string, levels: number): void {\n mkdirSync(dirname(dest), { recursive: true })\n const prefix = '../'.repeat(levels)\n const shifted = readFileSync(src, 'utf-8').replace(\n /(from\\s+|import\\s+|import\\()(['\"])(\\.\\.\\/)/g,\n `$1$2${prefix}$3`,\n )\n writeFileSync(dest, shifted)\n}\n\nfunction installFiles(\n packageRoot: string,\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): {\n copied: number\n skipped: number\n files: { created: string[]; preexisting: string[]; missing: string[] }\n} {\n let copied = 0\n let skipped = 0\n const created: string[] = []\n const preexisting: string[] = []\n const missing: string[] = []\n\n for (const file of config.files) {\n const srcPath = join(packageRoot, 'features', config.id, file.src)\n const resolvedDest = resolveFeatureDestPath(file.dest, config.route)\n const destPath = join(targetDir, resolvedDest)\n const existed = existsSync(destPath)\n if (existed) preexisting.push(resolvedDest)\n\n if (!existsSync(srcPath)) {\n emit(` Warning: source not found: ${file.src}`, 'stderr')\n if (!existed) missing.push(resolvedDest)\n continue\n }\n\n if (existed && !file.overwrite) {\n emit(` Exists (skipped): ${resolvedDest}`)\n skipped++\n continue\n }\n\n const wasRedirected = resolvedDest !== file.dest\n const shiftLevels = resolvedDest.split('/').length - file.dest.split('/').length\n if (wasRedirected) copyFileWithImportShift(srcPath, destPath, shiftLevels)\n else copyFile(srcPath, destPath)\n emit(` ${existed ? 'Overwrote' : 'Copied'}: ${resolvedDest}`)\n if (!existed) created.push(resolvedDest)\n copied++\n }\n\n return { copied, skipped, files: { created, preexisting, missing } }\n}\n\nfunction unresolvedSchema(\n schema: FeatureSchema,\n reason: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration {\n const entry = schema.spreadOperator ? `...${schema.exportName}` : schema.exportName\n emit('')\n emit(' Add manually to src/schemas.ts:')\n emit(` import { ${schema.exportName} } from '${schema.importPath}'`)\n emit(` // then add ${entry} to the schemas array`)\n return {\n area: 'schema',\n reason,\n steps: [\n `Add import { ${schema.exportName} } from '${schema.importPath}' to src/schemas.ts.`,\n `Add ${entry} to the exported schemas array.`,\n ],\n }\n}\n\nfunction integrateSchema(\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration[] {\n if (!config.schema) return []\n const schemasPath = join(targetDir, 'src', 'schemas.ts')\n if (!existsSync(schemasPath)) {\n return [unresolvedSchema(config.schema, 'src/schemas.ts was not found.', emit)]\n }\n\n let content = readFileSync(schemasPath, 'utf-8')\n const { exportName, importPath, spreadOperator } = config.schema\n const importPattern = new RegExp(`^import\\\\s+\\\\{[^}]*\\\\b${exportName}\\\\b`, 'm')\n if (importPattern.test(content)) {\n emit(` Schema already present: ${exportName}`)\n return []\n }\n if (\n !content.includes('export const schemas') ||\n !/export const schemas:\\s*CollectionSchema\\[\\]\\s*=\\s*\\[/.test(content)\n ) {\n const reason = 'src/schemas.ts has an unexpected structure.'\n emit(` Could not auto-integrate schema (${reason})`)\n return [unresolvedSchema(config.schema, reason, emit)]\n }\n\n const importLine = `import { ${exportName} } from '${importPath}'`\n const withImport = content.replace(\n /export const schemas/,\n `${importLine}\\n\\nexport const schemas`,\n )\n const schemaEntry = spreadOperator ? `...${exportName}` : exportName\n const withEntry = withImport.replace(\n /export const schemas:\\s*CollectionSchema\\[\\]\\s*=\\s*\\[/,\n `export const schemas: CollectionSchema[] = [\\n ${schemaEntry},`,\n )\n if (withImport === content || withEntry === withImport) {\n const reason = 'src/schemas.ts could not be updated safely.'\n emit(` Could not auto-integrate schema (${reason})`)\n return [unresolvedSchema(config.schema, reason, emit)]\n }\n content = withEntry\n writeFileSync(schemasPath, content)\n emit(` Schema integrated: ${exportName} -> schemas.ts`)\n return []\n}\n\nfunction unresolvedActions(\n actions: FeatureAction[],\n reason: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration {\n emit('')\n emit(' Add manually to src/actions/index.ts:')\n for (const action of actions) {\n emit(` import { ${action.exportName} } from '${action.importPath}'`)\n emit(` // then add '${action.registerAs}': ${action.exportName} to the actions object`)\n }\n return {\n area: 'actions',\n reason,\n steps: actions.flatMap((action) => [\n `Add import { ${action.exportName} } from '${action.importPath}' to src/actions/index.ts.`,\n `Register '${action.registerAs}': ${action.exportName} in the exported actions object.`,\n ]),\n }\n}\n\nfunction integrateActions(\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration[] {\n if (!config.actions?.length) return []\n const actionsPath = join(targetDir, 'src', 'actions', 'index.ts')\n if (!existsSync(actionsPath)) {\n return [unresolvedActions(config.actions, 'src/actions/index.ts was not found.', emit)]\n }\n\n let content = readFileSync(actionsPath, 'utf-8')\n const registryPattern =\n /export const actions:\\s*Record<string,\\s*ActionHandler(?:<[^>]*>)?>\\s*=\\s*\\{/\n if (!registryPattern.test(content)) {\n const reason = 'src/actions/index.ts has an unexpected structure.'\n emit(` Could not auto-integrate actions (${reason})`)\n return [unresolvedActions(config.actions, reason, emit)]\n }\n\n let integrated = 0\n const unresolved: UnresolvedIntegration[] = []\n for (const action of config.actions) {\n const importPattern = new RegExp(`^import\\\\s+\\\\{[^}]*\\\\b${action.exportName}\\\\b`, 'm')\n if (importPattern.test(content)) {\n emit(` Action already present: ${action.registerAs}`)\n continue\n }\n const importLine = `import { ${action.exportName} } from '${action.importPath}'`\n const withImport = content.replace(\n /export const actions:/,\n `${importLine}\\n\\nexport const actions:`,\n )\n const registryEntry = ` '${action.registerAs}': ${action.exportName},`\n const withEntry = withImport.replace(registryPattern, (match) => `${match}\\n${registryEntry}`)\n if (withImport === content || withEntry === withImport) {\n emit(` Could not integrate action: ${action.registerAs}`)\n unresolved.push(\n unresolvedActions(\n [action],\n `Action '${action.registerAs}' could not be registered safely.`,\n emit,\n ),\n )\n continue\n }\n content = withEntry\n integrated++\n emit(` Action integrated: ${action.registerAs} (${action.exportName})`)\n }\n if (integrated > 0) writeFileSync(actionsPath, content)\n return unresolved\n}\n\nfunction integrateCode(\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration[] {\n const unresolved: UnresolvedIntegration[] = []\n for (const insertion of config.code ?? []) {\n const targetPath = join(targetDir, insertion.file)\n if (!existsSync(targetPath)) {\n unresolved.push({\n area: 'code',\n reason: `${insertion.file} was not found.`,\n steps: [`Add ${insertion.insert.trim()} to ${insertion.file}.`],\n })\n continue\n }\n\n const original = readFileSync(targetPath, 'utf-8')\n if (original.includes(insertion.insert.trim())) {\n emit(` Integration already present: ${insertion.file}`)\n continue\n }\n const markerIndex = original.indexOf(insertion.marker)\n if (markerIndex < 0) {\n unresolved.push({\n area: 'code',\n reason: `${insertion.file} does not contain the expected insertion point.`,\n steps: [\n ...(insertion.import ? [`Add ${insertion.import} to ${insertion.file}.`] : []),\n `Add ${insertion.insert.trim()} to ${insertion.file}.`,\n ],\n })\n continue\n }\n\n let content = original\n if (insertion.import && !content.includes(insertion.import)) {\n const importIndex = content.search(/^import\\s/m)\n content = `${content.slice(0, importIndex < 0 ? 0 : importIndex)}${insertion.import}\\n${content.slice(\n importIndex < 0 ? 0 : importIndex,\n )}`\n }\n const currentMarkerIndex = content.indexOf(insertion.marker)\n const insertAt =\n insertion.placement === 'before'\n ? currentMarkerIndex\n : currentMarkerIndex + insertion.marker.length\n const prefix = insertion.placement === 'before' ? '' : '\\n'\n const suffix = insertion.placement === 'before' ? '\\n' : ''\n content = `${content.slice(0, insertAt)}${prefix}${insertion.insert}${suffix}${content.slice(insertAt)}`\n writeFileSync(targetPath, content)\n emit(` Integrated: ${insertion.file}`)\n }\n return unresolved\n}\n\nfunction integrateIgnore(config: FeatureConfig, targetDir: string): void {\n if (!config.ignore?.length) return\n const targetPath = join(targetDir, '.gitignore')\n const current = existsSync(targetPath) ? readFileSync(targetPath, 'utf-8') : ''\n const present = new Set(current.split(/\\r?\\n/).map((line) => line.trim()))\n const missing = config.ignore.filter((pattern) => !present.has(pattern))\n if (missing.length === 0) return\n const separator = current === '' || current.endsWith('\\n') ? '' : '\\n'\n writeFileSync(targetPath, `${current}${separator}${missing.join('\\n')}\\n`)\n}\n\nfunction integrateWrangler(\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration[] {\n const runWorkerFirst = config.wrangler?.runWorkerFirst === true\n const rateLimits = config.wrangler?.rateLimits ?? []\n if (!runWorkerFirst && rateLimits.length === 0) return []\n const targetPath = join(targetDir, 'wrangler.toml')\n const steps = [\n ...(runWorkerFirst ? ['Set assets.run_worker_first = true in wrangler.toml.'] : []),\n ...rateLimits.map((binding) => `Add the ${binding.name} rate-limit binding to wrangler.toml.`),\n ]\n if (!existsSync(targetPath)) {\n return [{ area: 'wrangler', reason: 'wrangler.toml was not found.', steps }]\n }\n\n let source = readFileSync(targetPath, 'utf-8')\n if (runWorkerFirst && !/^\\[assets\\]\\s*$/m.test(source)) {\n return [{ area: 'wrangler', reason: 'wrangler.toml has no [assets] section.', steps }]\n }\n\n let changed = false\n if (runWorkerFirst) {\n const updated = setRunWorkerFirst(source)\n changed ||= updated !== source\n source = updated\n }\n\n const scopes = ['', ...wranglerEnvironmentNames(source)]\n for (const scope of scopes) {\n for (const binding of rateLimits) {\n if (hasRateLimitBinding(source, scope, binding.name)) continue\n source = `${source.trimEnd()}\\n\\n${rateLimitBindingBlock(scope, binding)}\\n`\n changed = true\n }\n }\n\n if (changed) {\n writeFileSync(targetPath, source)\n emit(' Integrated: wrangler.toml')\n } else emit(' Integration already present: wrangler.toml')\n return []\n}\n\nfunction setRunWorkerFirst(source: string): string {\n const headers = [...source.matchAll(/^\\[(?:env\\.[^\\]]+\\.)?assets\\]\\s*$/gm)]\n for (const header of headers.reverse()) {\n const start = header.index\n const bodyStart = start + header[0].length\n const nextHeader = source.slice(bodyStart).search(/^\\[\\[?[^\\]]+\\]\\]?\\s*$/m)\n const end = nextHeader < 0 ? source.length : bodyStart + nextHeader\n const section = source.slice(start, end)\n if (/^\\s*run_worker_first\\s*=\\s*true(?:\\s*#.*)?$/m.test(section)) continue\n // Quoted strings are consumed atomically so a `]` inside a route string\n // cannot truncate the array match.\n const setting =\n /^(\\s*)run_worker_first\\s*=\\s*(?:false|\\[(?:[^\\]\"']|\"(?:[^\"\\\\]|\\\\[\\s\\S])*\"|'[^']*')*\\])(\\s*(?:#.*)?)$/m\n const updated = setting.test(section)\n ? section.replace(setting, '$1run_worker_first = true$2')\n : section.replace(/^\\[(?:env\\.[^\\]]+\\.)?assets\\]\\s*$/m, '$&\\nrun_worker_first = true')\n source = source.slice(0, start) + updated + source.slice(end)\n }\n return source\n}\n\nfunction wranglerEnvironmentNames(source: string): string[] {\n return [\n ...new Set(\n [...source.matchAll(/^\\[\\[?env\\.([^.\\]]+)(?:\\.|\\]\\]?\\s*$)/gm)].map((match) => match[1]),\n ),\n ]\n}\n\nfunction hasRateLimitBinding(source: string, scope: string, name: string): boolean {\n const header = scope ? `[[env.${scope}.ratelimits]]` : '[[ratelimits]]'\n // Tolerate hand-reformatted spacing and either TOML string quoting so a\n // reinstall never appends a duplicate block Wrangler would reject.\n const escaped = name.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n const namePattern = new RegExp(\n `^\\\\s*name\\\\s*=\\\\s*(?:\"${escaped}\"|'${escaped}')\\\\s*(?:#.*)?$`,\n 'm',\n )\n let offset = 0\n while ((offset = source.indexOf(header, offset)) >= 0) {\n const end = source.slice(offset + header.length).search(/^\\[/m)\n const blockEnd = end < 0 ? source.length : offset + header.length + end\n if (namePattern.test(source.slice(offset, blockEnd))) return true\n offset += header.length\n }\n return false\n}\n\nfunction rateLimitBindingBlock(\n scope: string,\n binding: NonNullable<FeatureWranglerIntegration['rateLimits']>[number],\n): string {\n const header = scope ? `[[env.${scope}.ratelimits]]` : '[[ratelimits]]'\n return [\n header,\n `name = ${JSON.stringify(binding.name)}`,\n `namespace_id = ${JSON.stringify(binding.namespaceId)}`,\n `simple = { limit = ${binding.limit}, period = ${binding.period} }`,\n ].join('\\n')\n}\n\nfunction integrateCss(\n packageRoot: string,\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration[] {\n if (!config.css?.length) return []\n const stylesPath = join(targetDir, 'src', 'styles.css')\n if (!existsSync(stylesPath)) {\n emit(' Warning: Cannot integrate CSS — src/styles.css not found')\n return [\n {\n area: 'css',\n reason: 'src/styles.css was not found.',\n steps: config.css.map(\n (cssFile) =>\n `Append node_modules/deepspace/features/${config.id}/${cssFile} to src/styles.css.`,\n ),\n },\n ]\n }\n\n let styles = readFileSync(stylesPath, 'utf-8')\n let integrated = 0\n const unresolved: UnresolvedIntegration[] = []\n for (const cssFile of config.css) {\n const srcPath = join(packageRoot, 'features', config.id, cssFile)\n if (!existsSync(srcPath)) {\n emit(` Warning: CSS source not found: ${cssFile}`, 'stderr')\n unresolved.push({\n area: 'css',\n reason: `Feature CSS source '${cssFile}' was not found in the installed package.`,\n steps: ['Reinstall the current deepspace package, then retry this feature installation.'],\n })\n continue\n }\n const css = readFileSync(srcPath, 'utf-8')\n const fingerprint = css.match(/\\.([\\w-]+)\\s*\\{|@keyframes\\s+([\\w-]+)/)\n const marker = fingerprint?.[1] ?? fingerprint?.[2]\n if (marker && styles.includes(marker)) {\n emit(` CSS already present: ${cssFile} (found .${marker})`)\n continue\n }\n styles += `\\n${css}`\n integrated++\n emit(` CSS integrated: ${cssFile} -> styles.css`)\n }\n if (integrated > 0) writeFileSync(stylesPath, styles)\n return unresolved\n}\n\nfunction unresolvedRoute(\n route: NonNullable<FeatureConfig['route']>,\n config: FeatureConfig,\n reason: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration {\n const roles = routeRolesSuffix(route)\n emit('')\n emit(' Add manually to src/nav.ts:')\n emit(` { path: '${route.path}', label: '${config.name}'${roles} },`)\n return {\n area: 'nav',\n reason,\n steps: [\n `Add { path: '${route.path}', label: '${config.name}'${roles} } to the exported nav array.`,\n ],\n }\n}\n\nfunction routeRolesSuffix(route: NonNullable<FeatureConfig['route']>): string {\n const roles = route.roles?.length ? route.roles : route.protected === false ? [] : ['member']\n return roles.length ? `, roles: [${roles.map((role) => `'${role}' as Role`).join(', ')}]` : ''\n}\n\nfunction integrateRoute(\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): UnresolvedIntegration[] {\n if (!config.route) return []\n const navPath = join(targetDir, 'src', 'nav.ts')\n if (!existsSync(navPath)) {\n return [unresolvedRoute(config.route, config, 'src/nav.ts was not found.', emit)]\n }\n let content = readFileSync(navPath, 'utf-8')\n const routePath = config.route.path\n if (content.includes(`'${routePath}'`)) {\n emit(` Nav already present: ${routePath}`)\n return []\n }\n if (!content.includes(NAV_MARKER)) {\n emit(' Could not auto-wire nav (marker not found in nav.ts)')\n return [\n unresolvedRoute(\n config.route,\n config,\n 'The feature insertion marker was not found in src/nav.ts.',\n emit,\n ),\n ]\n }\n const roles = routeRolesSuffix(config.route)\n const entry = ` { path: '${routePath}', label: '${config.name}'${roles} },`\n content = content.replace(NAV_MARKER, `${NAV_MARKER}\\n${entry}`)\n writeFileSync(navPath, content)\n emit(` Nav wired: ${routePath} (${config.name})`)\n emit(' Route: automatic (file-based routing via src/pages/)')\n return []\n}\n\nconst INSTALL_DIAGNOSTIC_BYTES = 16 * 1024\n\ninterface PackageInstallRun {\n error?: Error\n status: number | null\n diagnosticTail?: string\n}\n\nfunction readDiagnosticTail(outputFd: number): string | undefined {\n const size = fstatSync(outputFd).size\n if (size === 0) return undefined\n const length = Math.min(size, INSTALL_DIAGNOSTIC_BYTES)\n const buffer = Buffer.alloc(length)\n readSync(outputFd, buffer, 0, length, size - length)\n const tail = buffer.toString('utf-8').trim()\n if (!tail) return undefined\n return size > length ? `[last ${length} bytes]\\n${tail}` : tail\n}\n\nfunction runPackageInstall(\n packageManager: string,\n targetDir: string,\n outputVisible: boolean,\n): PackageInstallRun {\n const options = {\n cwd: targetDir,\n shell: process.platform === 'win32',\n }\n if (outputVisible) {\n const child = spawnSync(packageManager, ['install'], { ...options, stdio: 'inherit' })\n return { error: child.error, status: child.status }\n }\n\n // Keep stdout reserved for the command's one-line JSON envelope. Both child\n // streams share a temporary file, then only a fixed-size tail enters memory.\n const captureDir = mkdtempSync(join(tmpdir(), 'deepspace-add-install-'))\n let outputFd: number | undefined\n try {\n outputFd = openSync(join(captureDir, 'output.log'), 'w+')\n const child = spawnSync(packageManager, ['install'], {\n ...options,\n stdio: ['ignore', outputFd, outputFd],\n })\n return {\n error: child.error,\n status: child.status,\n diagnosticTail: readDiagnosticTail(outputFd),\n }\n } finally {\n if (outputFd !== undefined) closeSync(outputFd)\n rmSync(captureDir, { recursive: true, force: true })\n }\n}\n\nfunction integrateDependencies(\n config: FeatureConfig,\n targetDir: string,\n runInstall: boolean,\n outputVisible: boolean,\n emit: FeatureInstallerOutput,\n): {\n added: DependencyAddition[]\n installRan: boolean\n installError?: string\n diagnosticTail?: string\n packageManager: string\n} {\n const packageManager = detectPackageManager(targetDir)\n const declarations: Array<{\n section: DependencySection\n values: Record<string, string> | undefined\n }> = [\n { section: 'dependencies', values: config.dependencies },\n { section: 'devDependencies', values: config.devDependencies },\n ]\n if (declarations.every(({ values }) => !values || Object.keys(values).length === 0)) {\n return { added: [], installRan: false, packageManager }\n }\n\n const packagePath = join(targetDir, 'package.json')\n if (!existsSync(packagePath)) {\n emit(' Could not patch package.json (file not found)')\n return { added: [], installRan: false, packageManager }\n }\n\n let pkg: Record<string, unknown> & {\n dependencies?: Record<string, string>\n devDependencies?: Record<string, string>\n }\n try {\n pkg = JSON.parse(readFileSync(packagePath, 'utf-8')) as typeof pkg\n } catch (error) {\n emit(\n ` Could not patch package.json (parse error: ${error instanceof Error ? error.message : String(error)})`,\n )\n return { added: [], installRan: false, packageManager }\n }\n\n const added: DependencyAddition[] = []\n const skipped: string[] = []\n for (const { section, values } of declarations) {\n if (!values) continue\n pkg[section] ??= {}\n for (const [name, version] of Object.entries(values)) {\n if (pkg.dependencies?.[name] || pkg.devDependencies?.[name]) {\n if (!added.some((dependency) => dependency.name === name)) skipped.push(name)\n continue\n }\n pkg[section]![name] = version\n added.push({ name, section, version })\n }\n }\n\n if (added.length === 0) {\n emit(' Dependencies already present')\n } else {\n writeFileSync(packagePath, `${JSON.stringify(pkg, null, 2)}\\n`, 'utf-8')\n emit(` Added to package.json: ${added.map(({ name }) => name).join(', ')}`)\n if (skipped.length) emit(` Already present (kept your version): ${skipped.join(', ')}`)\n }\n\n if (!runInstall) return { added, installRan: false, packageManager }\n emit('')\n emit(` Installing dependencies with ${packageManager}...`)\n const child = runPackageInstall(packageManager, targetDir, outputVisible)\n if (child.error) {\n const error = child.error as NodeJS.ErrnoException\n const reason = error.code === 'ENOENT' ? `${packageManager} not found on PATH` : error.message\n return {\n added,\n installRan: false,\n installError: `Could not launch ${packageManager} (${reason}).`,\n diagnosticTail: child.diagnosticTail,\n packageManager,\n }\n }\n if (child.status !== 0) {\n const diagnostic = child.diagnosticTail ? `\\nDiagnostic tail:\\n${child.diagnosticTail}` : ''\n return {\n added,\n installRan: false,\n installError: `${packageManager} install did not complete (exit ${child.status ?? 'unknown'}).${diagnostic}`,\n diagnosticTail: child.diagnosticTail,\n packageManager,\n }\n }\n return { added, installRan: true, packageManager }\n}\n\nfunction dependencySnapshot(targetDir: string): {\n dependencies: Record<string, unknown>\n devDependencies: Record<string, unknown>\n} {\n try {\n const pkg = JSON.parse(readFileSync(join(targetDir, 'package.json'), 'utf-8')) as {\n dependencies?: Record<string, unknown>\n devDependencies?: Record<string, unknown>\n }\n return {\n dependencies: pkg.dependencies ?? {},\n devDependencies: pkg.devDependencies ?? {},\n }\n } catch {\n return { dependencies: {}, devDependencies: {} }\n }\n}\n\nfunction installedNavFeatures(packageRoot: string, targetDir: string): string[] {\n return listFeatureConfigs(packageRoot)\n .filter((feature) => feature.category === 'nav' && feature.files.length > 0)\n .filter((feature) => {\n const signature = resolveFeatureDestPath(feature.files[0].dest, feature.route)\n return existsSync(join(targetDir, signature))\n })\n .map((feature) => feature.id)\n}\n\nfunction warnNavConflicts(\n packageRoot: string,\n config: FeatureConfig,\n targetDir: string,\n emit: FeatureInstallerOutput,\n): void {\n if (config.category !== 'nav') return\n const present = installedNavFeatures(packageRoot, targetDir)\n if (present.length <= 1) return\n emit('')\n emit(`⚠ Multiple navigation features present (${present.join(', ')}).`)\n emit(' sidebar, topbar, and tree are mutually exclusive — keep one and remove the')\n emit(' others to avoid competing navigation in your layout.')\n}\n\nexport function installFeature(options: FeatureInstallOptions): FeatureInstallOutcome {\n const packageRoot = featurePackageRoot()\n const targetDir = resolveTargetDir(options.targetDir)\n const config = readFeatureConfig(options.feature, packageRoot)\n if (!config) {\n const suggestion = suggestFeature(options.feature, packageRoot)\n return {\n ok: false,\n code: 'unknown_feature',\n error: [\n `Unknown feature: ${options.feature}`,\n ...(suggestion ? [`Did you mean \"${suggestion}\"?`] : []),\n ].join('\\n'),\n data: {\n requestedFeature: options.feature,\n suggestedFeature: suggestion,\n availableFeatures: featureCatalog(packageRoot),\n },\n }\n }\n\n const emit: FeatureInstallerOutput = options.emit ?? (() => undefined)\n emit('')\n emit(`Installing: ${config.name}`)\n emit(` ${config.description ?? ''}`)\n emit(` Target: ${targetDir}`)\n\n const installedFiles = installFiles(packageRoot, config, targetDir, emit)\n integrateIgnore(config, targetDir)\n emit('')\n emit(\n `Copied ${installedFiles.copied} file(s)${\n installedFiles.skipped > 0 ? `, skipped ${installedFiles.skipped} existing` : ''\n }`,\n )\n\n const unresolvedIntegrations = [\n ...integrateSchema(config, targetDir, emit),\n ...integrateActions(config, targetDir, emit),\n ...integrateWrangler(config, targetDir, emit),\n ...integrateCode(config, targetDir, emit),\n ...integrateCss(packageRoot, config, targetDir, emit),\n ...integrateRoute(config, targetDir, emit),\n ]\n const dependencyResult = integrateDependencies(\n config,\n targetDir,\n options.installDependencies,\n options.emit !== undefined,\n emit,\n )\n\n if (config.instructions?.length) {\n emit('')\n emit('--- Next steps ---')\n emit('')\n config.instructions.forEach((instruction, index) => {\n emit(`${index + 1}. ${instruction}`)\n emit('')\n })\n }\n if (config.patterns?.length) {\n emit('--- Key patterns ---')\n emit('')\n config.patterns.forEach((pattern) => emit(`- ${pattern}`))\n emit('')\n }\n warnNavConflicts(packageRoot, config, targetDir, emit)\n\n const dependenciesAfter = dependencySnapshot(targetDir)\n const declaredDependencies = [\n ...Object.keys(config.dependencies ?? {}),\n ...Object.keys(config.devDependencies ?? {}),\n ]\n const missingDeclarations = declaredDependencies.filter(\n (name) =>\n !(name in dependenciesAfter.dependencies) && !(name in dependenciesAfter.devDependencies),\n )\n const installed =\n installedFiles.files.missing.length === 0 &&\n missingDeclarations.length === 0 &&\n unresolvedIntegrations.length === 0 &&\n dependencyResult.installError === undefined\n const dependencyInstallRequired =\n installed &&\n dependencyResult.added.length > 0 &&\n !options.installDependencies &&\n !dependencyResult.installRan\n\n const data: InstallData = {\n feature: config.id,\n dir: targetDir,\n installed,\n observedChanges: {\n files: installedFiles.files,\n dependencies: {\n added: dependencyResult.added,\n missingDeclarations,\n installRequested: options.installDependencies,\n ...(dependencyResult.diagnosticTail\n ? { diagnosticTail: dependencyResult.diagnosticTail }\n : {}),\n },\n },\n unresolvedIntegrations,\n manifestGuidance: {\n instructions: config.instructions ?? [],\n patterns: config.patterns ?? [],\n agentNotes: config.agentNotes ?? [],\n },\n }\n if (dependencyResult.installError) {\n return {\n ok: false,\n code: 'dependency_install_failed',\n error: dependencyResult.installError,\n data,\n }\n }\n if (unresolvedIntegrations.length > 0) {\n const count = unresolvedIntegrations.length\n return {\n ok: false,\n code: 'manual_integration_required',\n error: `${count} app integration${count === 1 ? '' : 's'} require manual completion. Follow the unresolved integration steps, then run your checks.`,\n data,\n }\n }\n\n return {\n ok: true,\n data,\n ...(dependencyInstallRequired\n ? {\n action: {\n cwd: targetDir,\n argv: [dependencyResult.packageManager, 'install'],\n },\n }\n : {}),\n }\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\n\nexport type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun'\n\n/** Resolve the repository's package manager from its explicit packageManager\n * field first, then its lockfile. npm is the conservative fallback. */\nexport function detectPackageManager(appDir: string): PackageManager {\n try {\n const pkg = JSON.parse(readFileSync(join(appDir, 'package.json'), 'utf-8')) as {\n packageManager?: unknown\n }\n const name = typeof pkg.packageManager === 'string' ? pkg.packageManager.split('@')[0] : ''\n if (name === 'npm' || name === 'pnpm' || name === 'yarn' || name === 'bun') return name\n } catch {\n // Missing/malformed package.json falls through to lockfile detection.\n }\n if (existsSync(join(appDir, 'pnpm-lock.yaml'))) return 'pnpm'\n if (existsSync(join(appDir, 'yarn.lock'))) return 'yarn'\n if (existsSync(join(appDir, 'bun.lock')) || existsSync(join(appDir, 'bun.lockb'))) return 'bun'\n return 'npm'\n}\n\nexport function installCommand(appDir: string): string {\n return `${detectPackageManager(appDir)} install`\n}\n"],"mappings":"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","names":["existsSync","readFileSync","join","join","existsSync","readFileSync"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -76,7 +76,7 @@ interface RolePermissions {
|
|
|
76
76
|
create: boolean;
|
|
77
77
|
update: PermissionLevel;
|
|
78
78
|
delete: PermissionLevel;
|
|
79
|
-
/** If set, only these columns can be updated by this role. */
|
|
79
|
+
/** If set, only these caller-supplied columns can be created or updated by this role. */
|
|
80
80
|
writableFields?: string[];
|
|
81
81
|
}
|
|
82
82
|
interface CollectionSchema {
|
|
@@ -2766,12 +2766,15 @@ interface User extends UserProfile {
|
|
|
2766
2766
|
*/
|
|
2767
2767
|
interface RoomUser {
|
|
2768
2768
|
id: string;
|
|
2769
|
-
|
|
2769
|
+
/** Present only in the admin user-management view. */
|
|
2770
|
+
email?: string;
|
|
2770
2771
|
name: string;
|
|
2771
2772
|
imageUrl?: string;
|
|
2772
2773
|
role: string;
|
|
2773
|
-
|
|
2774
|
-
|
|
2774
|
+
/** Present only in the admin user-management view. */
|
|
2775
|
+
createdAt?: string;
|
|
2776
|
+
/** Present only in the admin user-management view. */
|
|
2777
|
+
lastSeenAt?: string;
|
|
2775
2778
|
}
|
|
2776
2779
|
type RoomConnectionState = 'connecting' | 'connected' | 'disconnected';
|
|
2777
2780
|
/** A server-rejected optimistic write. */
|
|
@@ -3168,7 +3171,8 @@ declare function useYjsText(collection: string, recordId: string, fieldName: str
|
|
|
3168
3171
|
*/
|
|
3169
3172
|
interface UserInfo {
|
|
3170
3173
|
id: string;
|
|
3171
|
-
|
|
3174
|
+
/** Available to admins; ordinary collaborators receive public identity only. */
|
|
3175
|
+
email?: string;
|
|
3172
3176
|
name: string;
|
|
3173
3177
|
imageUrl?: string;
|
|
3174
3178
|
role: string;
|
package/dist/index.js
CHANGED
|
@@ -3429,28 +3429,44 @@ function useJobs(roomId) {
|
|
|
3429
3429
|
// src/client/storage/hooks/usePresenceRoom.ts
|
|
3430
3430
|
import { useState as useState11, useEffect as useEffect14, useCallback as useCallback13, useRef as useRef12 } from "react";
|
|
3431
3431
|
function usePresenceRoom(scopeId) {
|
|
3432
|
+
const auth = useRecordAuth();
|
|
3433
|
+
const tokenProvider = auth?.getAuthToken ?? getAuthToken;
|
|
3432
3434
|
const [peers, setPeers] = useState11([]);
|
|
3433
3435
|
const [connected, setConnected] = useState11(false);
|
|
3434
3436
|
const wsRef = useRef12(null);
|
|
3435
3437
|
useEffect14(() => {
|
|
3438
|
+
setConnected(false);
|
|
3439
|
+
setPeers([]);
|
|
3436
3440
|
let ws = null;
|
|
3437
3441
|
let reconnectTimer = null;
|
|
3438
3442
|
let alive = true;
|
|
3443
|
+
let browserOnline = typeof navigator === "undefined" || navigator.onLine;
|
|
3444
|
+
let connecting = false;
|
|
3439
3445
|
const connect = async () => {
|
|
3440
|
-
if (!alive) return;
|
|
3441
|
-
|
|
3446
|
+
if (!alive || !browserOnline || connecting) return;
|
|
3447
|
+
if (ws?.readyState === WebSocket.CONNECTING || ws?.readyState === WebSocket.OPEN) return;
|
|
3448
|
+
connecting = true;
|
|
3449
|
+
let token;
|
|
3450
|
+
try {
|
|
3451
|
+
token = await tokenProvider();
|
|
3452
|
+
} finally {
|
|
3453
|
+
connecting = false;
|
|
3454
|
+
}
|
|
3455
|
+
if (!alive || !browserOnline) return;
|
|
3442
3456
|
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
3443
3457
|
const baseUrl = `${protocol}//${window.location.host}`;
|
|
3444
3458
|
const url = new URL(`/ws/presence/${encodeURIComponent(scopeId)}`, baseUrl);
|
|
3445
3459
|
if (token) url.searchParams.set("token", token);
|
|
3446
3460
|
wsLog("connecting", `presence:${scopeId}`);
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3461
|
+
const socket = new WebSocket(url.toString());
|
|
3462
|
+
ws = socket;
|
|
3463
|
+
wsRef.current = socket;
|
|
3464
|
+
socket.onopen = () => {
|
|
3465
|
+
if (ws !== socket) return socket.close();
|
|
3450
3466
|
wsLog("connected", `presence:${scopeId}`);
|
|
3451
3467
|
setConnected(true);
|
|
3452
3468
|
};
|
|
3453
|
-
|
|
3469
|
+
socket.onmessage = (event) => {
|
|
3454
3470
|
dispatch(event.data, {
|
|
3455
3471
|
[MSG.PRESENCE_SYNC]: (p) => {
|
|
3456
3472
|
setPeers(p.peers);
|
|
@@ -3471,19 +3487,34 @@ function usePresenceRoom(scopeId) {
|
|
|
3471
3487
|
}
|
|
3472
3488
|
});
|
|
3473
3489
|
};
|
|
3474
|
-
|
|
3490
|
+
socket.onclose = () => {
|
|
3491
|
+
if (ws !== socket) return;
|
|
3475
3492
|
wsLog("disconnected", `presence:${scopeId}`);
|
|
3493
|
+
ws = null;
|
|
3476
3494
|
wsRef.current = null;
|
|
3477
3495
|
setConnected(false);
|
|
3478
|
-
if (alive) reconnectTimer = setTimeout(connect, 1e3);
|
|
3496
|
+
if (alive && browserOnline) reconnectTimer = setTimeout(connect, 1e3);
|
|
3479
3497
|
};
|
|
3480
|
-
|
|
3498
|
+
socket.onerror = () => socket.close();
|
|
3481
3499
|
};
|
|
3482
|
-
|
|
3500
|
+
const handleOffline = () => {
|
|
3501
|
+
browserOnline = false;
|
|
3502
|
+
setConnected(false);
|
|
3503
|
+
ws?.close();
|
|
3504
|
+
};
|
|
3505
|
+
const handleOnline = () => {
|
|
3506
|
+
browserOnline = true;
|
|
3507
|
+
void connect();
|
|
3508
|
+
};
|
|
3509
|
+
window.addEventListener("offline", handleOffline);
|
|
3510
|
+
window.addEventListener("online", handleOnline);
|
|
3511
|
+
void connect();
|
|
3483
3512
|
return () => {
|
|
3484
3513
|
wsLog("closing", `presence:${scopeId}`);
|
|
3485
3514
|
alive = false;
|
|
3486
3515
|
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
3516
|
+
window.removeEventListener("offline", handleOffline);
|
|
3517
|
+
window.removeEventListener("online", handleOnline);
|
|
3487
3518
|
if (ws) {
|
|
3488
3519
|
ws.onclose = null;
|
|
3489
3520
|
ws.onmessage = null;
|
|
@@ -3492,7 +3523,7 @@ function usePresenceRoom(scopeId) {
|
|
|
3492
3523
|
}
|
|
3493
3524
|
wsRef.current = null;
|
|
3494
3525
|
};
|
|
3495
|
-
}, [scopeId]);
|
|
3526
|
+
}, [scopeId, tokenProvider]);
|
|
3496
3527
|
const updateState = useCallback13((state) => {
|
|
3497
3528
|
const ws = wsRef.current;
|
|
3498
3529
|
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|