@pattern-stack/codegen 0.17.2 → 0.19.0
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/README.md +157 -2
- package/consumer-skills/codegen/SKILL.md +32 -0
- package/consumer-skills/entities/SKILL.md +2 -0
- package/dist/{chunk-I6UXRJ3Q.js → chunk-43SBT72G.js} +4 -4
- package/dist/{chunk-T6SCOJF4.js → chunk-7LKAMLV4.js} +4 -4
- package/dist/{chunk-IOQMMH6C.js → chunk-F7KN3U6U.js} +122 -8
- package/dist/chunk-F7KN3U6U.js.map +1 -0
- package/dist/{chunk-CZQUOIDY.js → chunk-J7JMVS2B.js} +4 -4
- package/dist/{chunk-KSTZIULO.js → chunk-K2I6XIK5.js} +4 -4
- package/dist/{chunk-ATVGYF3D.js → chunk-PKDS6QIJ.js} +7 -7
- package/dist/{chunk-KZDHMZ45.js → chunk-SNH35CNA.js} +8 -8
- package/dist/runtime/base-classes/index.js +24 -24
- package/dist/runtime/subsystems/analytics/analytics.module.js +2 -2
- package/dist/runtime/subsystems/analytics/index.js +4 -4
- package/dist/runtime/subsystems/auth/auth.module.js +3 -3
- package/dist/runtime/subsystems/auth/index.js +14 -14
- package/dist/runtime/subsystems/bridge/bridge-delivery.drizzle-backend.js +2 -2
- package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js +1 -1
- package/dist/runtime/subsystems/bridge/bridge.module.js +5 -5
- package/dist/runtime/subsystems/bridge/index.js +13 -13
- package/dist/runtime/subsystems/cache/cache.module.js +1 -1
- package/dist/runtime/subsystems/cache/index.js +3 -3
- package/dist/runtime/subsystems/index.js +94 -94
- package/dist/runtime/subsystems/integration/build-change-source.js +2 -2
- package/dist/runtime/subsystems/integration/index.js +36 -36
- package/dist/runtime/subsystems/integration/integration.module.js +4 -4
- package/dist/src/cli/index.js +1552 -254
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.js +12 -12
- package/package.json +1 -1
- package/src/config/locations.mjs +0 -6
- package/src/config/paths.mjs +0 -13
- package/templates/entity/new/prompt.js +12 -88
- package/dist/chunk-IOQMMH6C.js.map +0 -1
- package/templates/entity/new/frontend/_inject-entities-entry.ejs.t +0 -7
- package/templates/entity/new/frontend/_inject-entities-import.ejs.t +0 -7
- package/templates/entity/new/frontend/collections/_ensure-anchor-collections.ejs.t +0 -10
- package/templates/entity/new/frontend/collections/_inject-index.ejs.t +0 -9
- package/templates/entity/new/frontend/collections/_inject-schema-import.ejs.t +0 -9
- package/templates/entity/new/frontend/collections/collection.ejs.t +0 -86
- package/templates/entity/new/frontend/collections/collections-base.ejs.t +0 -35
- package/templates/entity/new/frontend/entity/collection.ejs.t +0 -173
- package/templates/entity/new/frontend/entity/combined.ejs.t +0 -505
- package/templates/entity/new/frontend/entity/fields.ejs.t +0 -105
- package/templates/entity/new/frontend/entity/hooks.ejs.t +0 -74
- package/templates/entity/new/frontend/entity/index.ejs.t +0 -22
- package/templates/entity/new/frontend/entity/mutation-hooks.ejs.t +0 -85
- package/templates/entity/new/frontend/entity/mutations.ejs.t +0 -39
- package/templates/entity/new/frontend/entity/types.ejs.t +0 -60
- package/templates/entity/new/frontend/generated/_inject-index-export.ejs.t +0 -7
- package/templates/entity/new/frontend/generated/_inject-index-import.ejs.t +0 -7
- package/templates/entity/new/frontend/generated/_inject-index-registry.ejs.t +0 -7
- package/templates/entity/new/frontend/store/_inject-collection-import.ejs.t +0 -9
- package/templates/entity/new/frontend/store/_inject-collections.ejs.t +0 -9
- package/templates/entity/new/frontend/store/_inject-entity.ejs.t +0 -9
- package/templates/entity/new/frontend/store/_inject-import.ejs.t +0 -9
- package/templates/entity/new/frontend/store/_inject-lookups.ejs.t +0 -9
- package/templates/entity/new/frontend/store/_inject-resolve.ejs.t +0 -10
- package/templates/entity/new/frontend/store/hooks.ejs.t +0 -73
- package/templates/entity/new/frontend/unified-entity.ejs.t +0 -29
- /package/dist/{chunk-I6UXRJ3Q.js.map → chunk-43SBT72G.js.map} +0 -0
- /package/dist/{chunk-T6SCOJF4.js.map → chunk-7LKAMLV4.js.map} +0 -0
- /package/dist/{chunk-CZQUOIDY.js.map → chunk-J7JMVS2B.js.map} +0 -0
- /package/dist/{chunk-KSTZIULO.js.map → chunk-K2I6XIK5.js.map} +0 -0
- /package/dist/{chunk-ATVGYF3D.js.map → chunk-PKDS6QIJ.js.map} +0 -0
- /package/dist/{chunk-KZDHMZ45.js.map → chunk-SNH35CNA.js.map} +0 -0
package/dist/src/cli/index.js
CHANGED
|
@@ -17,11 +17,13 @@ import {
|
|
|
17
17
|
getManifestDir,
|
|
18
18
|
getOrchestrationPatternNames,
|
|
19
19
|
getPendingSuggestions,
|
|
20
|
+
isActiveProvider,
|
|
20
21
|
isManifestStale,
|
|
21
22
|
loadAppPatterns,
|
|
22
23
|
loadEntities,
|
|
23
24
|
loadEntitiesFromYaml,
|
|
24
25
|
loadEntityFromYaml,
|
|
26
|
+
loadEntityRegistry,
|
|
25
27
|
loadEventFromYaml,
|
|
26
28
|
loadJunctionFromYaml,
|
|
27
29
|
loadProvidersFromYaml,
|
|
@@ -36,35 +38,35 @@ import {
|
|
|
36
38
|
validateOrchestrationProject,
|
|
37
39
|
validateProviders,
|
|
38
40
|
writeManifest
|
|
39
|
-
} from "../../chunk-
|
|
41
|
+
} from "../../chunk-F7KN3U6U.js";
|
|
40
42
|
import "../../chunk-KVOWSC5S.js";
|
|
41
|
-
import "../../chunk-
|
|
43
|
+
import "../../chunk-PKDS6QIJ.js";
|
|
44
|
+
import "../../chunk-PRWIX6UW.js";
|
|
42
45
|
import "../../chunk-YK5JEVLX.js";
|
|
43
46
|
import "../../chunk-EO2QPOKH.js";
|
|
44
|
-
import "../../chunk-
|
|
47
|
+
import "../../chunk-SQDOBLBP.js";
|
|
48
|
+
import "../../chunk-TDEHU73T.js";
|
|
49
|
+
import "../../chunk-LG57S2SC.js";
|
|
45
50
|
import "../../chunk-XWBK3XJK.js";
|
|
51
|
+
import "../../chunk-S7C6TIIF.js";
|
|
52
|
+
import "../../chunk-MZ6GV4YF.js";
|
|
53
|
+
import "../../chunk-HNWZFNKP.js";
|
|
46
54
|
import "../../chunk-AHV4GDYM.js";
|
|
47
|
-
import "../../chunk-
|
|
55
|
+
import "../../chunk-43SBT72G.js";
|
|
56
|
+
import "../../chunk-4MF3HKJA.js";
|
|
57
|
+
import "../../chunk-TIZXQU26.js";
|
|
48
58
|
import "../../chunk-JEINYUJH.js";
|
|
49
59
|
import {
|
|
50
60
|
isDivisibleCursor
|
|
51
61
|
} from "../../chunk-5TK7MEN4.js";
|
|
52
62
|
import "../../chunk-4KNXX6TI.js";
|
|
53
63
|
import "../../chunk-3CJFPU6Q.js";
|
|
54
|
-
import "../../chunk-TDEHU73T.js";
|
|
55
|
-
import "../../chunk-S7C6TIIF.js";
|
|
56
|
-
import "../../chunk-MZ6GV4YF.js";
|
|
57
|
-
import "../../chunk-LG57S2SC.js";
|
|
58
|
-
import "../../chunk-HNWZFNKP.js";
|
|
59
|
-
import "../../chunk-I6UXRJ3Q.js";
|
|
60
|
-
import "../../chunk-TIZXQU26.js";
|
|
61
|
-
import "../../chunk-4MF3HKJA.js";
|
|
62
64
|
import "../../chunk-U64T4YZE.js";
|
|
63
65
|
import "../../chunk-2E224ZSN.js";
|
|
64
66
|
|
|
65
67
|
// src/cli/index.ts
|
|
66
68
|
import { readFileSync as readFileSync6 } from "fs";
|
|
67
|
-
import { join as
|
|
69
|
+
import { join as join18 } from "path";
|
|
68
70
|
import { Builtins, Cli, Command as Command13 } from "clipanion";
|
|
69
71
|
|
|
70
72
|
// src/cli/noun-module.ts
|
|
@@ -1099,7 +1101,7 @@ var icons = {
|
|
|
1099
1101
|
|
|
1100
1102
|
// src/cli/commands/entity.ts
|
|
1101
1103
|
import fs10 from "fs";
|
|
1102
|
-
import
|
|
1104
|
+
import path14 from "path";
|
|
1103
1105
|
import { Command as Command2, Option as Option2 } from "clipanion";
|
|
1104
1106
|
|
|
1105
1107
|
// src/cli/shared/hygen.ts
|
|
@@ -3847,8 +3849,8 @@ function generateIntegrationAggregator(surface, entries) {
|
|
|
3847
3849
|
);
|
|
3848
3850
|
const importLines = sorted.map((e) => {
|
|
3849
3851
|
const cls = assemblyModuleClass(e.entityName, e.provider);
|
|
3850
|
-
const
|
|
3851
|
-
return `import { ${cls} } from '${
|
|
3852
|
+
const path36 = `./modules/${e.provider}/${e.entityName}-integration.module`;
|
|
3853
|
+
return `import { ${cls} } from '${path36}';`;
|
|
3852
3854
|
}).join("\n");
|
|
3853
3855
|
const membersInline = moduleClasses.join(", ");
|
|
3854
3856
|
return `${generatedBanner(`surface: ${surface}`)}
|
|
@@ -4414,8 +4416,9 @@ function emitAdapters(opts) {
|
|
|
4414
4416
|
};
|
|
4415
4417
|
const entitiesBySurface = collectEntitiesBySurface(opts.entities);
|
|
4416
4418
|
const entityByName = new Map(opts.entities.map((e) => [e.entity.name, e]));
|
|
4419
|
+
const activeProviders = opts.providers.filter((p) => isActiveProvider(p.definition));
|
|
4417
4420
|
const bySurface = /* @__PURE__ */ new Map();
|
|
4418
|
-
for (const { definition } of
|
|
4421
|
+
for (const { definition } of activeProviders) {
|
|
4419
4422
|
for (const surface of definition.surfaces) {
|
|
4420
4423
|
if (!SURFACE_REGISTRY[surface]) {
|
|
4421
4424
|
result.skippedSurfaces.push({
|
|
@@ -4430,7 +4433,7 @@ function emitAdapters(opts) {
|
|
|
4430
4433
|
bySurface.set(surface, list);
|
|
4431
4434
|
}
|
|
4432
4435
|
}
|
|
4433
|
-
const defBySlug = new Map(
|
|
4436
|
+
const defBySlug = new Map(activeProviders.map((p) => [p.definition.slug, p.definition]));
|
|
4434
4437
|
for (const [surface, slugs] of bySurface) {
|
|
4435
4438
|
const surfaceDir = join8(opts.outputRoot, surface);
|
|
4436
4439
|
const adaptersDir = join8(surfaceDir, "adapters");
|
|
@@ -4472,9 +4475,9 @@ function emitAdapters(opts) {
|
|
|
4472
4475
|
[aggregatorPath, generateSurfaceAggregator(surface, slugs, mode)],
|
|
4473
4476
|
[typedViewPath, generateTypedView(surface, slugs, entitiesBySurface.get(surface) ?? [])]
|
|
4474
4477
|
];
|
|
4475
|
-
for (const [
|
|
4476
|
-
if (!opts.dryRun) writeIfChanged(
|
|
4477
|
-
result.written.push(
|
|
4478
|
+
for (const [path36, content] of files) {
|
|
4479
|
+
if (!opts.dryRun) writeIfChanged(path36, content);
|
|
4480
|
+
result.written.push(path36);
|
|
4478
4481
|
}
|
|
4479
4482
|
if (opts.backendSrcAbs) {
|
|
4480
4483
|
const aliases = opts.aliases ?? {};
|
|
@@ -4792,6 +4795,7 @@ function generateProviderModules(opts) {
|
|
|
4792
4795
|
const mode = opts.mode ?? "package";
|
|
4793
4796
|
const written = [];
|
|
4794
4797
|
for (const { definition, filePath } of loaded) {
|
|
4798
|
+
if (!isActiveProvider(definition)) continue;
|
|
4795
4799
|
const sourceYaml = relativeSource(filePath);
|
|
4796
4800
|
const content = generateProviderModule(definition, sourceYaml, mode);
|
|
4797
4801
|
const outPath = join9(
|
|
@@ -4819,15 +4823,1199 @@ function relativeSource(filePath) {
|
|
|
4819
4823
|
return slash === -1 ? filePath : `definitions/providers/${filePath.slice(slash + 1)}`;
|
|
4820
4824
|
}
|
|
4821
4825
|
|
|
4822
|
-
// src/
|
|
4826
|
+
// src/emitters/frontend/emit-base.ts
|
|
4827
|
+
import { join as join10 } from "path";
|
|
4828
|
+
|
|
4829
|
+
// src/emitters/frontend/types.ts
|
|
4830
|
+
function sortEntities(entities) {
|
|
4831
|
+
return [...entities].sort((a, b) => a.name.localeCompare(b.name));
|
|
4832
|
+
}
|
|
4833
|
+
function resolveSyncMode(entity, config) {
|
|
4834
|
+
return entity.sync ?? config.globalSyncMode;
|
|
4835
|
+
}
|
|
4836
|
+
|
|
4837
|
+
// src/emitters/frontend/emit-utils.ts
|
|
4838
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
4839
|
+
import { dirname as dirname3 } from "path";
|
|
4840
|
+
function generatedBanner3(sourceDesc) {
|
|
4841
|
+
return `// @generated by @pattern-stack/codegen from ${sourceDesc} \u2014 DO NOT EDIT.
|
|
4842
|
+
// Hand edits are overwritten on re-emit. Regenerate with \`bun run codegen\`.`;
|
|
4843
|
+
}
|
|
4844
|
+
function withBanner(sourceDesc, body) {
|
|
4845
|
+
return `${generatedBanner3(sourceDesc)}
|
|
4846
|
+
|
|
4847
|
+
${body}`;
|
|
4848
|
+
}
|
|
4849
|
+
function writeFile2(outPath, content) {
|
|
4850
|
+
mkdirSync3(dirname3(outPath), { recursive: true });
|
|
4851
|
+
writeFileSync3(outPath, content);
|
|
4852
|
+
}
|
|
4853
|
+
|
|
4854
|
+
// src/emitters/frontend/emit-base.ts
|
|
4855
|
+
var SOURCE_DESC = "the entity set";
|
|
4856
|
+
function buildQueryClientFile() {
|
|
4857
|
+
const body = `import { QueryClient } from '@tanstack/react-query';
|
|
4858
|
+
|
|
4859
|
+
/**
|
|
4860
|
+
* Shared QueryClient for REST-backed (\`api\` sync mode) collections.
|
|
4861
|
+
*
|
|
4862
|
+
* Replaceable: swap this for your app's own QueryClient if you already create
|
|
4863
|
+
* one \u2014 every generated collection imports \`queryClient\` from this module.
|
|
4864
|
+
*/
|
|
4865
|
+
export const queryClient = new QueryClient({
|
|
4866
|
+
defaultOptions: {
|
|
4867
|
+
queries: {
|
|
4868
|
+
staleTime: 60 * 1000, // 60s
|
|
4869
|
+
gcTime: 5 * 60 * 1000, // 5m
|
|
4870
|
+
},
|
|
4871
|
+
},
|
|
4872
|
+
});
|
|
4873
|
+
`;
|
|
4874
|
+
return withBanner(SOURCE_DESC, body);
|
|
4875
|
+
}
|
|
4876
|
+
function buildConfigFile(ctx) {
|
|
4877
|
+
const entities = sortEntities(ctx.entities);
|
|
4878
|
+
const entityNameUnion = entities.length > 0 ? entities.map((e) => `'${e.name}'`).join(" | ") : "string";
|
|
4879
|
+
const defaultEntries = entities.map((e) => ` ${e.name}: { mode: '${resolveSyncMode(e, ctx.config)}' },`).join("\n");
|
|
4880
|
+
const body = `/**
|
|
4881
|
+
* Per-entity sync configuration + runtime overrides.
|
|
4882
|
+
*
|
|
4883
|
+
* \`mode\` selects the collection backing for an entity:
|
|
4884
|
+
* - 'electric' \u2192 real-time shape sync (electricCollectionOptions)
|
|
4885
|
+
* - 'api' \u2192 REST via TanStack Query (queryCollectionOptions)
|
|
4886
|
+
*
|
|
4887
|
+
* The offline mode (Electric + Dexie) is deferred \u2014 see
|
|
4888
|
+
* docs/specs/2026-06-04-frontend-pipeline-rebuild.md OQ-6.
|
|
4889
|
+
*/
|
|
4890
|
+
|
|
4891
|
+
export type SyncMode = 'api' | 'electric';
|
|
4892
|
+
|
|
4893
|
+
export type EntityName = ${entityNameUnion};
|
|
4894
|
+
|
|
4895
|
+
export interface EntitySyncConfig {
|
|
4896
|
+
mode: SyncMode;
|
|
4897
|
+
}
|
|
4898
|
+
|
|
4899
|
+
/** Resolved per-entity sync modes (per-entity \`sync:\` over global default). */
|
|
4900
|
+
export const defaultConfig: Record<EntityName, EntitySyncConfig> = {
|
|
4901
|
+
${defaultEntries}
|
|
4902
|
+
};
|
|
4903
|
+
|
|
4904
|
+
/** Runtime overrides, layered over \`defaultConfig\`. */
|
|
4905
|
+
const overrides: Partial<Record<EntityName, EntitySyncConfig>> = {};
|
|
4906
|
+
|
|
4907
|
+
/** Resolve an entity's effective sync mode (override wins over default). */
|
|
4908
|
+
export function getSyncMode(entity: EntityName): SyncMode {
|
|
4909
|
+
return (overrides[entity] ?? defaultConfig[entity]).mode;
|
|
4910
|
+
}
|
|
4911
|
+
|
|
4912
|
+
/** Override an entity's sync config at runtime. */
|
|
4913
|
+
export function setEntityConfig(entity: EntityName, config: EntitySyncConfig): void {
|
|
4914
|
+
overrides[entity] = config;
|
|
4915
|
+
}
|
|
4916
|
+
`;
|
|
4917
|
+
return withBanner(SOURCE_DESC, body);
|
|
4918
|
+
}
|
|
4919
|
+
function emitBase(ctx, outDir) {
|
|
4920
|
+
const written = [];
|
|
4921
|
+
const queryClientPath = join10(outDir, "query-client.ts");
|
|
4922
|
+
writeFile2(queryClientPath, buildQueryClientFile());
|
|
4923
|
+
written.push(queryClientPath);
|
|
4924
|
+
const configPath = join10(outDir, "config.ts");
|
|
4925
|
+
writeFile2(configPath, buildConfigFile(ctx));
|
|
4926
|
+
written.push(configPath);
|
|
4927
|
+
return written;
|
|
4928
|
+
}
|
|
4929
|
+
|
|
4930
|
+
// src/emitters/frontend/emit-api.ts
|
|
4931
|
+
import { join as join11 } from "path";
|
|
4932
|
+
var SOURCE_DESC_SET = "the entity set";
|
|
4933
|
+
function updateVerb(architecture) {
|
|
4934
|
+
return architecture === "clean-lite-ps" ? "PATCH" : "PUT";
|
|
4935
|
+
}
|
|
4936
|
+
function buildClientFile(ctx) {
|
|
4937
|
+
const { config } = ctx;
|
|
4938
|
+
const importLines = [];
|
|
4939
|
+
if (config.apiBaseUrlImport) {
|
|
4940
|
+
importLines.push(`import { API_BASE_URL } from '${config.apiBaseUrlImport}';`);
|
|
4941
|
+
}
|
|
4942
|
+
if (config.authFunction) {
|
|
4943
|
+
importLines.push(`import { ${config.authFunction} } from '${config.authImport}';`);
|
|
4944
|
+
}
|
|
4945
|
+
const baseUrlConst = config.apiBaseUrlImport ? "const BASE_URL = API_BASE_URL;" : `const BASE_URL = '${config.apiUrl}';`;
|
|
4946
|
+
const authHeaderBlock = config.authFunction ? ` const headers: Record<string, string> = {
|
|
4947
|
+
'Content-Type': 'application/json',
|
|
4948
|
+
Authorization: ${config.authFunction}(),
|
|
4949
|
+
};` : ` const headers: Record<string, string> = {
|
|
4950
|
+
'Content-Type': 'application/json',
|
|
4951
|
+
};`;
|
|
4952
|
+
const imports = importLines.length > 0 ? `${importLines.join("\n")}
|
|
4953
|
+
|
|
4954
|
+
` : "";
|
|
4955
|
+
const body = `${imports}${baseUrlConst}
|
|
4956
|
+
|
|
4957
|
+
/**
|
|
4958
|
+
* Base REST transport for \`api\` sync-mode collections and entity api clients.
|
|
4959
|
+
* Throws on non-2xx; returns parsed JSON, or \`undefined\` for 204 No Content.
|
|
4960
|
+
*/
|
|
4961
|
+
export async function request<T>(
|
|
4962
|
+
method: string,
|
|
4963
|
+
path: string,
|
|
4964
|
+
body?: unknown,
|
|
4965
|
+
): Promise<T> {
|
|
4966
|
+
${authHeaderBlock}
|
|
4967
|
+
|
|
4968
|
+
const res = await fetch(\`\${BASE_URL}\${path}\`, {
|
|
4969
|
+
method,
|
|
4970
|
+
headers,
|
|
4971
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
4972
|
+
});
|
|
4973
|
+
|
|
4974
|
+
if (!res.ok) {
|
|
4975
|
+
throw new Error(\`\${method} \${path} \u2192 \${res.status} \${res.statusText}\`);
|
|
4976
|
+
}
|
|
4977
|
+
|
|
4978
|
+
if (res.status === 204) {
|
|
4979
|
+
return undefined as T;
|
|
4980
|
+
}
|
|
4981
|
+
|
|
4982
|
+
return res.json() as Promise<T>;
|
|
4983
|
+
}
|
|
4984
|
+
`;
|
|
4985
|
+
return withBanner(SOURCE_DESC_SET, body);
|
|
4986
|
+
}
|
|
4987
|
+
function buildEntityApiFile(entity, ctx) {
|
|
4988
|
+
const { config } = ctx;
|
|
4989
|
+
const { camelName, plural, className, name } = entity;
|
|
4990
|
+
const verb = updateVerb(config.architecture);
|
|
4991
|
+
const body = `import { request } from './client';
|
|
4992
|
+
import type { ${className} } from '${config.dbEntitiesImport}/${name}';
|
|
4993
|
+
|
|
4994
|
+
export const ${camelName}Api = {
|
|
4995
|
+
list: (): Promise<${className}[]> => request<${className}[]>('GET', '/${plural}'),
|
|
4996
|
+
|
|
4997
|
+
get: (id: string): Promise<${className}> =>
|
|
4998
|
+
request<${className}>('GET', \`/${plural}/\${id}\`),
|
|
4999
|
+
|
|
5000
|
+
create: (data: Partial<${className}>): Promise<${className}> =>
|
|
5001
|
+
request<${className}>('POST', '/${plural}', data),
|
|
5002
|
+
|
|
5003
|
+
update: (id: string, data: Partial<${className}>): Promise<${className}> =>
|
|
5004
|
+
request<${className}>('${verb}', \`/${plural}/\${id}\`, data),
|
|
5005
|
+
|
|
5006
|
+
delete: (id: string): Promise<void> =>
|
|
5007
|
+
request<void>('DELETE', \`/${plural}/\${id}\`),
|
|
5008
|
+
};
|
|
5009
|
+
`;
|
|
5010
|
+
return withBanner(`entities/${name}.yaml`, body);
|
|
5011
|
+
}
|
|
5012
|
+
function buildApiIndexFile(ctx) {
|
|
5013
|
+
const entities = sortEntities(ctx.entities);
|
|
5014
|
+
const lines = [
|
|
5015
|
+
"export * from './client';",
|
|
5016
|
+
...entities.map((e) => `export * from './${e.name}';`)
|
|
5017
|
+
];
|
|
5018
|
+
return withBanner(SOURCE_DESC_SET, `${lines.join("\n")}
|
|
5019
|
+
`);
|
|
5020
|
+
}
|
|
5021
|
+
function emitApi(ctx, outDir) {
|
|
5022
|
+
const apiDir = join11(outDir, "api");
|
|
5023
|
+
const entities = sortEntities(ctx.entities);
|
|
5024
|
+
const written = [];
|
|
5025
|
+
const clientPath = join11(apiDir, "client.ts");
|
|
5026
|
+
writeFile2(clientPath, buildClientFile(ctx));
|
|
5027
|
+
written.push(clientPath);
|
|
5028
|
+
for (const entity of entities) {
|
|
5029
|
+
const entityPath = join11(apiDir, `${entity.name}.ts`);
|
|
5030
|
+
writeFile2(entityPath, buildEntityApiFile(entity, ctx));
|
|
5031
|
+
written.push(entityPath);
|
|
5032
|
+
}
|
|
5033
|
+
const indexPath = join11(apiDir, "index.ts");
|
|
5034
|
+
writeFile2(indexPath, buildApiIndexFile(ctx));
|
|
5035
|
+
written.push(indexPath);
|
|
5036
|
+
return written;
|
|
5037
|
+
}
|
|
5038
|
+
|
|
5039
|
+
// src/emitters/frontend/emit-collections.ts
|
|
5040
|
+
import { join as join12 } from "path";
|
|
5041
|
+
var SOURCE_DESC_SET2 = "the entity set";
|
|
5042
|
+
function baseUrlExpr(base, plural, apiBaseUrlImport) {
|
|
5043
|
+
return apiBaseUrlImport ? `\`\${API_BASE_URL}/${plural}\`` : `\`${base}/${plural}\``;
|
|
5044
|
+
}
|
|
5045
|
+
var SSR_ORIGIN_EXPR = "typeof window !== 'undefined' ? window.location.origin : ''";
|
|
5046
|
+
function buildElectricCollection(entity, ctx) {
|
|
5047
|
+
const { config } = ctx;
|
|
5048
|
+
const { camelName, plural, name } = entity;
|
|
5049
|
+
const imports = [
|
|
5050
|
+
"import { electricCollectionOptions } from '@tanstack/electric-db-collection';",
|
|
5051
|
+
"import { createCollection } from '@tanstack/react-db';"
|
|
5052
|
+
];
|
|
5053
|
+
if (config.columnMapper) {
|
|
5054
|
+
imports.push(`import { ${config.columnMapper} } from '@electric-sql/client';`);
|
|
5055
|
+
}
|
|
5056
|
+
if (config.authFunction) {
|
|
5057
|
+
imports.push(`import { ${config.authFunction} } from '${config.authImport}';`);
|
|
5058
|
+
}
|
|
5059
|
+
if (config.apiBaseUrlImport) {
|
|
5060
|
+
imports.push(`import { API_BASE_URL } from '${config.apiBaseUrlImport}';`);
|
|
5061
|
+
}
|
|
5062
|
+
imports.push(`import { ${camelName}Schema } from '${config.dbEntitiesImport}/${name}';`);
|
|
5063
|
+
let urlBlock;
|
|
5064
|
+
if (config.useTableParam) {
|
|
5065
|
+
urlBlock = ` url: new URL(
|
|
5066
|
+
'${config.shapeUrl}',
|
|
5067
|
+
${SSR_ORIGIN_EXPR},
|
|
5068
|
+
).toString(),
|
|
5069
|
+
params: {
|
|
5070
|
+
table: '${plural}',
|
|
5071
|
+
},`;
|
|
5072
|
+
} else {
|
|
5073
|
+
const shapeUrl = baseUrlExpr(config.shapeUrl, plural, config.apiBaseUrlImport);
|
|
5074
|
+
urlBlock = ` url: new URL(
|
|
5075
|
+
${shapeUrl},
|
|
5076
|
+
${SSR_ORIGIN_EXPR},
|
|
5077
|
+
).toString(),`;
|
|
5078
|
+
}
|
|
5079
|
+
const headersBlock = config.authFunction ? `
|
|
5080
|
+
headers: {
|
|
5081
|
+
Authorization: ${config.authFunction}(),
|
|
5082
|
+
},` : "";
|
|
5083
|
+
const parserEntries = Object.entries(config.parsers).map(([type, fn]) => ` ${type}: ${fn},`).join("\n");
|
|
5084
|
+
const parserBlock = parserEntries ? `
|
|
5085
|
+
parser: {
|
|
5086
|
+
${parserEntries}
|
|
5087
|
+
},` : `
|
|
5088
|
+
parser: {},`;
|
|
5089
|
+
let columnMapperBlock = "";
|
|
5090
|
+
if (config.columnMapper) {
|
|
5091
|
+
const mapperExpr = config.columnMapperNeedsCall ? `${config.columnMapper}()` : config.columnMapper;
|
|
5092
|
+
columnMapperBlock = `
|
|
5093
|
+
columnMapper: ${mapperExpr},`;
|
|
5094
|
+
}
|
|
5095
|
+
const body = `${imports.join("\n")}
|
|
5096
|
+
|
|
5097
|
+
export const ${camelName}Collection = createCollection(
|
|
5098
|
+
electricCollectionOptions({
|
|
5099
|
+
id: '${plural}',
|
|
5100
|
+
shapeOptions: {
|
|
5101
|
+
${urlBlock}${headersBlock}${parserBlock}${columnMapperBlock}
|
|
5102
|
+
},
|
|
5103
|
+
schema: ${camelName}Schema,
|
|
5104
|
+
getKey: (item) => item.id,
|
|
5105
|
+
}),
|
|
5106
|
+
);
|
|
5107
|
+
`;
|
|
5108
|
+
return withBanner(`entities/${name}.yaml`, body);
|
|
5109
|
+
}
|
|
5110
|
+
function buildApiCollection(entity, ctx) {
|
|
5111
|
+
const { config } = ctx;
|
|
5112
|
+
const { camelName, plural, name } = entity;
|
|
5113
|
+
const imports = [
|
|
5114
|
+
"import { queryCollectionOptions } from '@tanstack/query-db-collection';",
|
|
5115
|
+
"import { createCollection } from '@tanstack/react-db';",
|
|
5116
|
+
"import { queryClient } from '../query-client';",
|
|
5117
|
+
`import { ${camelName}Api } from '../api/${name}';`,
|
|
5118
|
+
`import { ${camelName}Schema } from '${config.dbEntitiesImport}/${name}';`
|
|
5119
|
+
];
|
|
5120
|
+
const body = `${imports.join("\n")}
|
|
5121
|
+
|
|
5122
|
+
export const ${camelName}Collection = createCollection(
|
|
5123
|
+
queryCollectionOptions({
|
|
5124
|
+
id: '${plural}',
|
|
5125
|
+
queryKey: ['${plural}'],
|
|
5126
|
+
queryClient,
|
|
5127
|
+
queryFn: () => ${camelName}Api.list(),
|
|
5128
|
+
getKey: (item) => item.id,
|
|
5129
|
+
schema: ${camelName}Schema,
|
|
5130
|
+
}),
|
|
5131
|
+
);
|
|
5132
|
+
`;
|
|
5133
|
+
return withBanner(`entities/${name}.yaml`, body);
|
|
5134
|
+
}
|
|
5135
|
+
function buildCollectionFile(entity, ctx) {
|
|
5136
|
+
const mode = resolveSyncMode(entity, ctx.config);
|
|
5137
|
+
return mode === "api" ? buildApiCollection(entity, ctx) : buildElectricCollection(entity, ctx);
|
|
5138
|
+
}
|
|
5139
|
+
function buildCollectionsIndexFile(ctx) {
|
|
5140
|
+
const entities = sortEntities(ctx.entities);
|
|
5141
|
+
const lines = entities.map((e) => `export * from './${e.name}';`);
|
|
5142
|
+
return withBanner(SOURCE_DESC_SET2, `${lines.join("\n")}
|
|
5143
|
+
`);
|
|
5144
|
+
}
|
|
5145
|
+
function emitCollections(ctx, outDir) {
|
|
5146
|
+
const collectionsDir = join12(outDir, "collections");
|
|
5147
|
+
const entities = sortEntities(ctx.entities);
|
|
5148
|
+
const written = [];
|
|
5149
|
+
for (const entity of entities) {
|
|
5150
|
+
const filePath = join12(collectionsDir, `${entity.name}.ts`);
|
|
5151
|
+
writeFile2(filePath, buildCollectionFile(entity, ctx));
|
|
5152
|
+
written.push(filePath);
|
|
5153
|
+
}
|
|
5154
|
+
const indexPath = join12(collectionsDir, "index.ts");
|
|
5155
|
+
writeFile2(indexPath, buildCollectionsIndexFile(ctx));
|
|
5156
|
+
written.push(indexPath);
|
|
5157
|
+
return written;
|
|
5158
|
+
}
|
|
5159
|
+
|
|
5160
|
+
// src/emitters/frontend/emit-entities.ts
|
|
5161
|
+
import { join as join13 } from "path";
|
|
5162
|
+
var SOURCE_DESC_SET3 = "the entity set";
|
|
5163
|
+
function buildEntityHooksFile(entity, ctx) {
|
|
5164
|
+
const { camelName, className, name } = entity;
|
|
5165
|
+
const body = `import { createEntityHooks } from '@pattern-stack/frontend-patterns';
|
|
5166
|
+
import { ${camelName}Collection } from '../collections/${name}';
|
|
5167
|
+
import { ${camelName}Api } from '../api/${name}';
|
|
5168
|
+
import { getSyncMode } from '../config';
|
|
5169
|
+
import type { ${className} } from '${ctx.config.dbEntitiesImport}/${name}';
|
|
5170
|
+
|
|
5171
|
+
/**
|
|
5172
|
+
* Typed hooks for ${className}, wired via the framework factory.
|
|
5173
|
+
*
|
|
5174
|
+
* \`localFirst\` is resolved at call time from the entity's runtime sync mode
|
|
5175
|
+
* (\`getSyncMode('${name}')\`) \u2014 \`api\` mode is confirmed-write, everything else
|
|
5176
|
+
* is local-first (optimistic).
|
|
5177
|
+
*/
|
|
5178
|
+
export const ${camelName}Hooks = createEntityHooks<${className}>({
|
|
5179
|
+
name: '${name}',
|
|
5180
|
+
collection: ${camelName}Collection,
|
|
5181
|
+
api: ${camelName}Api,
|
|
5182
|
+
localFirst: () => getSyncMode('${name}') !== 'api',
|
|
5183
|
+
});
|
|
5184
|
+
|
|
5185
|
+
// Per-entity hook re-exports for direct imports.
|
|
5186
|
+
export const {
|
|
5187
|
+
useList: use${className}List,
|
|
5188
|
+
useGet: use${className},
|
|
5189
|
+
useCreate: useCreate${className},
|
|
5190
|
+
useUpdate: useUpdate${className},
|
|
5191
|
+
useDelete: useDelete${className},
|
|
5192
|
+
keys: ${camelName}Keys,
|
|
5193
|
+
} = ${camelName}Hooks;
|
|
5194
|
+
`;
|
|
5195
|
+
return withBanner(`entities/${name}.yaml`, body);
|
|
5196
|
+
}
|
|
5197
|
+
function buildEntitiesIndexFile(ctx) {
|
|
5198
|
+
const entities = sortEntities(ctx.entities);
|
|
5199
|
+
const blocks = entities.map((e) => {
|
|
5200
|
+
const { camelName, className, name } = e;
|
|
5201
|
+
return `export {
|
|
5202
|
+
${camelName}Hooks,
|
|
5203
|
+
use${className}List,
|
|
5204
|
+
use${className},
|
|
5205
|
+
useCreate${className},
|
|
5206
|
+
useUpdate${className},
|
|
5207
|
+
useDelete${className},
|
|
5208
|
+
${camelName}Keys,
|
|
5209
|
+
} from './${name}';`;
|
|
5210
|
+
});
|
|
5211
|
+
return withBanner(SOURCE_DESC_SET3, `${blocks.join("\n\n")}
|
|
5212
|
+
`);
|
|
5213
|
+
}
|
|
5214
|
+
function emitEntities(ctx, outDir) {
|
|
5215
|
+
const entitiesDir = join13(outDir, "entities");
|
|
5216
|
+
const entities = sortEntities(ctx.entities);
|
|
5217
|
+
const written = [];
|
|
5218
|
+
for (const entity of entities) {
|
|
5219
|
+
const filePath = join13(entitiesDir, `${entity.name}.ts`);
|
|
5220
|
+
writeFile2(filePath, buildEntityHooksFile(entity, ctx));
|
|
5221
|
+
written.push(filePath);
|
|
5222
|
+
}
|
|
5223
|
+
const indexPath = join13(entitiesDir, "index.ts");
|
|
5224
|
+
writeFile2(indexPath, buildEntitiesIndexFile(ctx));
|
|
5225
|
+
written.push(indexPath);
|
|
5226
|
+
return written;
|
|
5227
|
+
}
|
|
5228
|
+
|
|
5229
|
+
// src/emitters/frontend/emit-store.ts
|
|
5230
|
+
import { join as join14 } from "path";
|
|
5231
|
+
var SOURCE_DESC_SET4 = "the entity set";
|
|
5232
|
+
var CAMEL = (s) => s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
5233
|
+
function resolvableRels(entity, ctx) {
|
|
5234
|
+
const parsed = ctx.parsed.get(entity.name);
|
|
5235
|
+
if (!parsed) return [];
|
|
5236
|
+
const registryByName = new Map(ctx.entities.map((e) => [e.name, e]));
|
|
5237
|
+
const out = [];
|
|
5238
|
+
for (const rel2 of parsed.relationships.values()) {
|
|
5239
|
+
if (rel2.type !== "belongs_to") continue;
|
|
5240
|
+
const target = registryByName.get(rel2.target);
|
|
5241
|
+
if (!target) continue;
|
|
5242
|
+
out.push({
|
|
5243
|
+
propertyName: CAMEL(rel2.name),
|
|
5244
|
+
fieldNameCamel: CAMEL(fkField(rel2)),
|
|
5245
|
+
target
|
|
5246
|
+
});
|
|
5247
|
+
}
|
|
5248
|
+
out.sort((a, b) => a.propertyName.localeCompare(b.propertyName));
|
|
5249
|
+
return out;
|
|
5250
|
+
}
|
|
5251
|
+
function fkField(rel2) {
|
|
5252
|
+
return rel2.foreignKey && rel2.foreignKey.length > 0 ? rel2.foreignKey : `${rel2.target}_id`;
|
|
5253
|
+
}
|
|
5254
|
+
function buildStoreIndexFile(ctx) {
|
|
5255
|
+
const entities = sortEntities(ctx.entities);
|
|
5256
|
+
const hookImports = entities.map((e) => `import { ${e.camelName}Hooks } from '../entities/${e.name}';`).join("\n");
|
|
5257
|
+
const collectionImports = entities.map((e) => `import { ${e.camelName}Collection } from '../collections/${e.name}';`).join("\n");
|
|
5258
|
+
const entityEntries = entities.map((e) => ` ${e.plural}: ${e.camelName}Hooks,`).join("\n");
|
|
5259
|
+
const collectionEntries = entities.map((e) => ` ${e.plural}: ${e.camelName}Collection,`).join("\n");
|
|
5260
|
+
const body = `import { createStore } from '@pattern-stack/frontend-patterns';
|
|
5261
|
+
|
|
5262
|
+
${hookImports}
|
|
5263
|
+
|
|
5264
|
+
${collectionImports}
|
|
5265
|
+
|
|
5266
|
+
/**
|
|
5267
|
+
* The application store \u2014 unified access to every entity.
|
|
5268
|
+
*
|
|
5269
|
+
* Entities and collections are keyed by their plural name:
|
|
5270
|
+
* store.${entities[0]?.plural ?? "things"}.useList()
|
|
5271
|
+
* store.resolve.<entity>(id)
|
|
5272
|
+
* store.lookups.build()
|
|
5273
|
+
*/
|
|
5274
|
+
export const store = createStore({
|
|
5275
|
+
entities: {
|
|
5276
|
+
${entityEntries}
|
|
5277
|
+
},
|
|
5278
|
+
collections: {
|
|
5279
|
+
${collectionEntries}
|
|
5280
|
+
},
|
|
5281
|
+
});
|
|
5282
|
+
|
|
5283
|
+
/** Store type for the \`useStore\` hook. */
|
|
5284
|
+
export type AppStore = typeof store;
|
|
5285
|
+
`;
|
|
5286
|
+
return withBanner(SOURCE_DESC_SET4, body);
|
|
5287
|
+
}
|
|
5288
|
+
function buildResolversFile(ctx) {
|
|
5289
|
+
const entities = sortEntities(ctx.entities);
|
|
5290
|
+
const collectionImports = entities.map((e) => `import { ${e.camelName}Collection } from '../collections/${e.name}';`).join("\n");
|
|
5291
|
+
const typeImports = entities.map((e) => `import type { ${e.className} } from '${ctx.config.dbEntitiesImport}/${e.name}';`).join("\n");
|
|
5292
|
+
const resolverIface = entities.map(
|
|
5293
|
+
(e) => ` ${e.camelName}: (id: string | null | undefined) => ${e.className} | undefined;`
|
|
5294
|
+
).join("\n");
|
|
5295
|
+
const resolverImpls = entities.map(
|
|
5296
|
+
(e) => ` ${e.camelName}: (id) => {
|
|
5297
|
+
if (!id) return undefined;
|
|
5298
|
+
return ${e.camelName}Collection.state.get(id) as ${e.className} | undefined;
|
|
5299
|
+
},`
|
|
5300
|
+
).join("\n");
|
|
5301
|
+
const refBlocks = [];
|
|
5302
|
+
for (const e of entities) {
|
|
5303
|
+
const rels = resolvableRels(e, ctx);
|
|
5304
|
+
if (rels.length === 0) continue;
|
|
5305
|
+
const refFields = rels.map(
|
|
5306
|
+
(r) => ` ${r.propertyName}: ${r.target.className} | undefined;`
|
|
5307
|
+
).join("\n");
|
|
5308
|
+
const hydrateFields = rels.map(
|
|
5309
|
+
(r) => ` ${r.propertyName}: resolvers.${r.target.camelName}(entity.${r.fieldNameCamel}),`
|
|
5310
|
+
).join("\n");
|
|
5311
|
+
refBlocks.push(`/** Resolved FK references for ${e.className}. */
|
|
5312
|
+
export interface ${e.className}Refs {
|
|
5313
|
+
${refFields}
|
|
5314
|
+
}
|
|
5315
|
+
|
|
5316
|
+
/** Hydrate a ${e.className} with its resolved FK references. */
|
|
5317
|
+
export function resolve${e.className}Refs(
|
|
5318
|
+
entity: ${e.className},
|
|
5319
|
+
resolvers: Resolvers,
|
|
5320
|
+
): ${e.className} & ${e.className}Refs {
|
|
5321
|
+
return {
|
|
5322
|
+
...entity,
|
|
5323
|
+
${hydrateFields}
|
|
5324
|
+
};
|
|
5325
|
+
}`);
|
|
5326
|
+
}
|
|
5327
|
+
const refsSection = refBlocks.length > 0 ? `
|
|
5328
|
+
// ${"=".repeat(73)}
|
|
5329
|
+
// WithResolved helpers \u2014 hydrate entities with resolved FKs
|
|
5330
|
+
// ${"=".repeat(73)}
|
|
5331
|
+
|
|
5332
|
+
${refBlocks.join("\n\n")}
|
|
5333
|
+
` : "";
|
|
5334
|
+
const body = `${collectionImports}
|
|
5335
|
+
${typeImports}
|
|
5336
|
+
|
|
5337
|
+
/**
|
|
5338
|
+
* FK resolvers \u2014 resolve a foreign-key id to the full entity object via the
|
|
5339
|
+
* backing collection's local state (\`O(1)\` \`Map.get\`).
|
|
5340
|
+
*
|
|
5341
|
+
* Usage:
|
|
5342
|
+
* const ${entities[0]?.camelName ?? "thing"} = resolvers.${entities[0]?.camelName ?? "thing"}(other.${entities[0]?.camelName ?? "thing"}Id);
|
|
5343
|
+
*/
|
|
5344
|
+
export interface Resolvers {
|
|
5345
|
+
${resolverIface}
|
|
5346
|
+
}
|
|
5347
|
+
|
|
5348
|
+
/** Build the resolver table over the generated collections. */
|
|
5349
|
+
export function createResolvers(): Resolvers {
|
|
5350
|
+
return {
|
|
5351
|
+
${resolverImpls}
|
|
5352
|
+
};
|
|
5353
|
+
}
|
|
5354
|
+
${refsSection}`;
|
|
5355
|
+
return withBanner(SOURCE_DESC_SET4, body);
|
|
5356
|
+
}
|
|
5357
|
+
function buildLookupsFile(ctx) {
|
|
5358
|
+
const entities = sortEntities(ctx.entities);
|
|
5359
|
+
const collectionImports = entities.map((e) => `import { ${e.camelName}Collection } from '../collections/${e.name}';`).join("\n");
|
|
5360
|
+
const typeImports = entities.map((e) => `import type { ${e.className} } from '${ctx.config.dbEntitiesImport}/${e.name}';`).join("\n");
|
|
5361
|
+
const lookupIface = entities.map((e) => ` ${e.plural}: Map<string, ${e.className}>;`).join("\n");
|
|
5362
|
+
const lookupBuild = entities.map(
|
|
5363
|
+
(e) => ` ${e.plural}: new Map(
|
|
5364
|
+
Array.from(${e.camelName}Collection.state.values()).map((item) => [
|
|
5365
|
+
(item as ${e.className}).id as string,
|
|
5366
|
+
item as ${e.className},
|
|
5367
|
+
]),
|
|
5368
|
+
),`
|
|
5369
|
+
).join("\n");
|
|
5370
|
+
const body = `${collectionImports}
|
|
5371
|
+
${typeImports}
|
|
5372
|
+
|
|
5373
|
+
/** All entity lookup maps, keyed by plural entity name (id \u2192 entity). */
|
|
5374
|
+
export interface EntityLookups {
|
|
5375
|
+
${lookupIface}
|
|
5376
|
+
}
|
|
5377
|
+
|
|
5378
|
+
/** Build fresh lookup maps from current collection state. */
|
|
5379
|
+
export function buildLookups(): EntityLookups {
|
|
5380
|
+
return {
|
|
5381
|
+
${lookupBuild}
|
|
5382
|
+
};
|
|
5383
|
+
}
|
|
5384
|
+
|
|
5385
|
+
/** Caching lookup factory: \`build()\` (re)computes, \`current\` reads, \`clear()\` resets. */
|
|
5386
|
+
export function createLookups() {
|
|
5387
|
+
let cache: EntityLookups | null = null;
|
|
5388
|
+
return {
|
|
5389
|
+
build: (): EntityLookups => {
|
|
5390
|
+
cache = buildLookups();
|
|
5391
|
+
return cache;
|
|
5392
|
+
},
|
|
5393
|
+
get current(): EntityLookups | null {
|
|
5394
|
+
return cache;
|
|
5395
|
+
},
|
|
5396
|
+
clear: (): void => {
|
|
5397
|
+
cache = null;
|
|
5398
|
+
},
|
|
5399
|
+
};
|
|
5400
|
+
}
|
|
5401
|
+
`;
|
|
5402
|
+
return withBanner(SOURCE_DESC_SET4, body);
|
|
5403
|
+
}
|
|
5404
|
+
function buildStoreModuleIndexFile(ctx) {
|
|
5405
|
+
const entities = sortEntities(ctx.entities);
|
|
5406
|
+
const lines = [
|
|
5407
|
+
"export { store, type AppStore } from './index';",
|
|
5408
|
+
"export { createResolvers, type Resolvers } from './resolvers';",
|
|
5409
|
+
"export { buildLookups, createLookups, type EntityLookups } from './lookups';"
|
|
5410
|
+
];
|
|
5411
|
+
const refExports = entities.filter((e) => resolvableRels(e, ctx).length > 0).map(
|
|
5412
|
+
(e) => `export { resolve${e.className}Refs, type ${e.className}Refs } from './resolvers';`
|
|
5413
|
+
);
|
|
5414
|
+
if (refExports.length > 0) {
|
|
5415
|
+
lines.push("", ...refExports);
|
|
5416
|
+
}
|
|
5417
|
+
return withBanner(SOURCE_DESC_SET4, `${lines.join("\n")}
|
|
5418
|
+
`);
|
|
5419
|
+
}
|
|
5420
|
+
function emitStore(ctx, outDir) {
|
|
5421
|
+
const storeDir = join14(outDir, "store");
|
|
5422
|
+
const written = [];
|
|
5423
|
+
const files = [
|
|
5424
|
+
["index.ts", buildStoreIndexFile(ctx)],
|
|
5425
|
+
["resolvers.ts", buildResolversFile(ctx)],
|
|
5426
|
+
["lookups.ts", buildLookupsFile(ctx)],
|
|
5427
|
+
["module-index.ts", buildStoreModuleIndexFile(ctx)]
|
|
5428
|
+
];
|
|
5429
|
+
for (const [fileName, content] of files) {
|
|
5430
|
+
const filePath = join14(storeDir, fileName);
|
|
5431
|
+
writeFile2(filePath, content);
|
|
5432
|
+
written.push(filePath);
|
|
5433
|
+
}
|
|
5434
|
+
return written;
|
|
5435
|
+
}
|
|
5436
|
+
|
|
5437
|
+
// src/emitters/frontend/emit-fields.ts
|
|
5438
|
+
import { join as join15 } from "path";
|
|
5439
|
+
|
|
5440
|
+
// src/emitters/frontend/field-meta.ts
|
|
5441
|
+
var CAMEL2 = (s) => s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
5442
|
+
function formatLabel(fieldName) {
|
|
5443
|
+
return fieldName.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
5444
|
+
}
|
|
5445
|
+
function inferUiType(field) {
|
|
5446
|
+
if (field.ui.type) return field.ui.type;
|
|
5447
|
+
if (Array.isArray(field.choices) && field.choices.length > 0) return "enum";
|
|
5448
|
+
if (field.foreignKey) return "reference";
|
|
5449
|
+
const nameLower = field.name.toLowerCase();
|
|
5450
|
+
if (nameLower.includes("email")) return "email";
|
|
5451
|
+
if (nameLower.includes("url") || nameLower.includes("website")) return "url";
|
|
5452
|
+
if (nameLower.includes("password")) return "password";
|
|
5453
|
+
if (nameLower.includes("price") || nameLower.includes("amount") || nameLower.includes("cost") || nameLower.includes("value") || nameLower.includes("revenue")) {
|
|
5454
|
+
return "money";
|
|
5455
|
+
}
|
|
5456
|
+
if (nameLower.includes("percent") || nameLower.includes("rate")) {
|
|
5457
|
+
return "percentage";
|
|
5458
|
+
}
|
|
5459
|
+
switch (field.type) {
|
|
5460
|
+
case "string":
|
|
5461
|
+
return field.constraints.maxLength && field.constraints.maxLength > 500 ? "textarea" : "text";
|
|
5462
|
+
case "integer":
|
|
5463
|
+
case "decimal":
|
|
5464
|
+
return "number";
|
|
5465
|
+
case "boolean":
|
|
5466
|
+
return "boolean";
|
|
5467
|
+
case "uuid":
|
|
5468
|
+
return "text";
|
|
5469
|
+
case "date":
|
|
5470
|
+
return "date";
|
|
5471
|
+
case "datetime":
|
|
5472
|
+
return "datetime";
|
|
5473
|
+
case "json":
|
|
5474
|
+
return "json";
|
|
5475
|
+
default:
|
|
5476
|
+
return "text";
|
|
5477
|
+
}
|
|
5478
|
+
}
|
|
5479
|
+
function inferUiImportance(field) {
|
|
5480
|
+
if (field.ui.importance) return field.ui.importance;
|
|
5481
|
+
const nameLower = field.name.toLowerCase();
|
|
5482
|
+
if (["id", "created_at", "updated_at", "deleted_at"].includes(nameLower)) {
|
|
5483
|
+
return "tertiary";
|
|
5484
|
+
}
|
|
5485
|
+
if (field.foreignKey && nameLower.endsWith("_id")) return "secondary";
|
|
5486
|
+
if (field.required) return "primary";
|
|
5487
|
+
if (nameLower.includes("name") || nameLower.includes("title")) return "primary";
|
|
5488
|
+
return "secondary";
|
|
5489
|
+
}
|
|
5490
|
+
function isEntityRefField(field) {
|
|
5491
|
+
if (field.type === "entity_ref") return true;
|
|
5492
|
+
return field.name.endsWith("_entity_type") || field.name.endsWith("_entity_id");
|
|
5493
|
+
}
|
|
5494
|
+
function deriveFieldMeta(field) {
|
|
5495
|
+
const hasChoices = Array.isArray(field.choices) && field.choices.length > 0;
|
|
5496
|
+
const meta = {
|
|
5497
|
+
field: CAMEL2(field.name),
|
|
5498
|
+
label: field.ui.label ?? formatLabel(field.name),
|
|
5499
|
+
type: inferUiType(field),
|
|
5500
|
+
importance: inferUiImportance(field),
|
|
5501
|
+
sortable: field.ui.sortable ?? false,
|
|
5502
|
+
filterable: field.ui.filterable ?? false
|
|
5503
|
+
};
|
|
5504
|
+
if (hasChoices) meta.choices = field.choices;
|
|
5505
|
+
if (field.foreignKey) meta.reference = field.foreignKey.table;
|
|
5506
|
+
return meta;
|
|
5507
|
+
}
|
|
5508
|
+
|
|
5509
|
+
// src/emitters/frontend/emit-fields.ts
|
|
5510
|
+
var SOURCE_DESC_SET5 = "the entity set";
|
|
5511
|
+
function buildFieldMetaTypeFile() {
|
|
5512
|
+
const body = `/**
|
|
5513
|
+
* Field metadata types for DataGrid, forms, and admin surfaces.
|
|
5514
|
+
*/
|
|
5515
|
+
|
|
5516
|
+
export type FieldType =
|
|
5517
|
+
| 'text'
|
|
5518
|
+
| 'textarea'
|
|
5519
|
+
| 'number'
|
|
5520
|
+
| 'boolean'
|
|
5521
|
+
| 'date'
|
|
5522
|
+
| 'datetime'
|
|
5523
|
+
| 'email'
|
|
5524
|
+
| 'url'
|
|
5525
|
+
| 'password'
|
|
5526
|
+
| 'money'
|
|
5527
|
+
| 'percentage'
|
|
5528
|
+
| 'json'
|
|
5529
|
+
| 'enum'
|
|
5530
|
+
| 'reference'
|
|
5531
|
+
| 'entity';
|
|
5532
|
+
|
|
5533
|
+
export type FieldImportance = 'primary' | 'secondary' | 'tertiary';
|
|
5534
|
+
|
|
5535
|
+
export interface FieldMeta<T = unknown> {
|
|
5536
|
+
/** Property key on the entity (\`keyof T\` for typed access). */
|
|
5537
|
+
field: keyof T & string;
|
|
5538
|
+
label: string;
|
|
5539
|
+
type: FieldType;
|
|
5540
|
+
importance: FieldImportance;
|
|
5541
|
+
sortable?: boolean;
|
|
5542
|
+
filterable?: boolean;
|
|
5543
|
+
format?: Record<string, unknown>;
|
|
5544
|
+
choices?: string[];
|
|
5545
|
+
reference?: string;
|
|
5546
|
+
}
|
|
5547
|
+
`;
|
|
5548
|
+
return withBanner(SOURCE_DESC_SET5, body);
|
|
5549
|
+
}
|
|
5550
|
+
function hasTimestamps(parsed) {
|
|
5551
|
+
return parsed?.behaviors.includes("timestamps") ?? false;
|
|
5552
|
+
}
|
|
5553
|
+
function displayFields(parsed) {
|
|
5554
|
+
if (!parsed) return [];
|
|
5555
|
+
const out = [];
|
|
5556
|
+
for (const field of parsed.fields.values()) {
|
|
5557
|
+
if (field.name === "id") continue;
|
|
5558
|
+
if (isEntityRefField(field)) continue;
|
|
5559
|
+
out.push(deriveFieldMeta(field));
|
|
5560
|
+
}
|
|
5561
|
+
return out;
|
|
5562
|
+
}
|
|
5563
|
+
function renderFieldMeta(meta) {
|
|
5564
|
+
const lines = [
|
|
5565
|
+
` field: '${meta.field}',`,
|
|
5566
|
+
` label: '${meta.label}',`,
|
|
5567
|
+
` type: '${meta.type}' as FieldType,`,
|
|
5568
|
+
` importance: '${meta.importance}' as FieldImportance,`
|
|
5569
|
+
];
|
|
5570
|
+
if (meta.sortable) lines.push(" sortable: true,");
|
|
5571
|
+
if (meta.filterable) lines.push(" filterable: true,");
|
|
5572
|
+
if (meta.choices) lines.push(` choices: ${JSON.stringify(meta.choices)},`);
|
|
5573
|
+
if (meta.reference) lines.push(` reference: '${meta.reference}',`);
|
|
5574
|
+
return ` ${meta.field}: {
|
|
5575
|
+
${lines.join("\n")}
|
|
5576
|
+
},`;
|
|
5577
|
+
}
|
|
5578
|
+
function humanizeClass(className) {
|
|
5579
|
+
return className.replace(/([A-Z])/g, " $1").trim();
|
|
5580
|
+
}
|
|
5581
|
+
function buildEntityFieldsFile(entity, ctx) {
|
|
5582
|
+
const parsed = ctx.parsed.get(entity.name);
|
|
5583
|
+
const { camelName, className, classNamePlural, name, plural } = entity;
|
|
5584
|
+
const fields = displayFields(parsed);
|
|
5585
|
+
const rels = resolvableRels(entity, ctx);
|
|
5586
|
+
const ts3 = hasTimestamps(parsed);
|
|
5587
|
+
const fieldEntries = fields.map(renderFieldMeta);
|
|
5588
|
+
const relEntries = rels.map(
|
|
5589
|
+
(r) => ` ${r.propertyName}: {
|
|
5590
|
+
field: '${r.propertyName}',
|
|
5591
|
+
label: '${humanizeClass(r.target.className)}',
|
|
5592
|
+
type: 'entity' as FieldType,
|
|
5593
|
+
importance: 'secondary' as FieldImportance,
|
|
5594
|
+
reference: '${r.target.plural}',
|
|
5595
|
+
},`
|
|
5596
|
+
);
|
|
5597
|
+
const tsEntries = ts3 ? [
|
|
5598
|
+
` createdAt: {
|
|
5599
|
+
field: 'createdAt',
|
|
5600
|
+
label: 'Created',
|
|
5601
|
+
type: 'datetime' as FieldType,
|
|
5602
|
+
importance: 'tertiary' as FieldImportance,
|
|
5603
|
+
format: { dateFormat: 'relative' },
|
|
5604
|
+
},`,
|
|
5605
|
+
` updatedAt: {
|
|
5606
|
+
field: 'updatedAt',
|
|
5607
|
+
label: 'Updated',
|
|
5608
|
+
type: 'datetime' as FieldType,
|
|
5609
|
+
importance: 'tertiary' as FieldImportance,
|
|
5610
|
+
format: { dateFormat: 'relative' },
|
|
5611
|
+
},`
|
|
5612
|
+
] : [];
|
|
5613
|
+
const allEntries = [...fieldEntries, ...relEntries, ...tsEntries].join("\n");
|
|
5614
|
+
const primaryFields = fields.filter((f) => f.importance === "primary").map((f) => ` '${f.field}',`).join("\n");
|
|
5615
|
+
const searchFields = fields.filter((f) => f.filterable).map((f) => ` '${f.field}',`).join("\n");
|
|
5616
|
+
const defaultSortField = ts3 ? "createdAt" : "id";
|
|
5617
|
+
const expose = parsed?.expose ?? ["repository", "rest", "trpc"];
|
|
5618
|
+
const canWrite = expose.includes("repository") || expose.includes("trpc");
|
|
5619
|
+
const body = `import type { FieldMeta, FieldType, FieldImportance } from './field-meta';
|
|
5620
|
+
import type { ${className} } from '${ctx.config.dbEntitiesImport}/${name}';
|
|
5621
|
+
|
|
5622
|
+
export const ${camelName}Fields: Record<string, FieldMeta<${className}>> = {
|
|
5623
|
+
${allEntries}
|
|
5624
|
+
};
|
|
5625
|
+
|
|
5626
|
+
export const ${camelName}Metadata = {
|
|
5627
|
+
name: '${name}',
|
|
5628
|
+
plural: '${plural}',
|
|
5629
|
+
displayName: '${humanizeClass(className)}',
|
|
5630
|
+
displayNamePlural: '${humanizeClass(classNamePlural)}',
|
|
5631
|
+
|
|
5632
|
+
fields: ${camelName}Fields,
|
|
5633
|
+
|
|
5634
|
+
primaryFields: [
|
|
5635
|
+
${primaryFields}
|
|
5636
|
+
],
|
|
5637
|
+
searchFields: [
|
|
5638
|
+
${searchFields}
|
|
5639
|
+
],
|
|
5640
|
+
defaultSort: { field: '${defaultSortField}', direction: 'desc' as const },
|
|
5641
|
+
|
|
5642
|
+
capabilities: {
|
|
5643
|
+
create: ${canWrite},
|
|
5644
|
+
update: ${canWrite},
|
|
5645
|
+
delete: ${canWrite},
|
|
5646
|
+
list: true,
|
|
5647
|
+
get: true,
|
|
5648
|
+
},
|
|
5649
|
+
} as const;
|
|
5650
|
+
`;
|
|
5651
|
+
return withBanner(`entities/${name}.yaml`, body);
|
|
5652
|
+
}
|
|
5653
|
+
function buildFieldsIndexFile(ctx) {
|
|
5654
|
+
const entities = sortEntities(ctx.entities);
|
|
5655
|
+
const lines = entities.map((e) => `export * from './${e.name}';`);
|
|
5656
|
+
return withBanner(SOURCE_DESC_SET5, `${lines.join("\n")}
|
|
5657
|
+
`);
|
|
5658
|
+
}
|
|
5659
|
+
function emitFields(ctx, outDir) {
|
|
5660
|
+
const fieldsDir = join15(outDir, "fields");
|
|
5661
|
+
const entities = sortEntities(ctx.entities);
|
|
5662
|
+
const written = [];
|
|
5663
|
+
const typePath = join15(fieldsDir, "field-meta.ts");
|
|
5664
|
+
writeFile2(typePath, buildFieldMetaTypeFile());
|
|
5665
|
+
written.push(typePath);
|
|
5666
|
+
for (const entity of entities) {
|
|
5667
|
+
const filePath = join15(fieldsDir, `${entity.name}.ts`);
|
|
5668
|
+
writeFile2(filePath, buildEntityFieldsFile(entity, ctx));
|
|
5669
|
+
written.push(filePath);
|
|
5670
|
+
}
|
|
5671
|
+
const indexPath = join15(fieldsDir, "index.ts");
|
|
5672
|
+
writeFile2(indexPath, buildFieldsIndexFile(ctx));
|
|
5673
|
+
written.push(indexPath);
|
|
5674
|
+
return written;
|
|
5675
|
+
}
|
|
5676
|
+
|
|
5677
|
+
// src/emitters/frontend/emit-providers.ts
|
|
5678
|
+
import { join as join16 } from "path";
|
|
5679
|
+
var SOURCE_DESC2 = "definitions/providers";
|
|
5680
|
+
function vendorLiteral(p, indent) {
|
|
5681
|
+
const lines = [
|
|
5682
|
+
`${indent}{`,
|
|
5683
|
+
`${indent} provider: '${p.slug}',`,
|
|
5684
|
+
`${indent} name: '${(p.displayName ?? p.slug).replace(/'/g, "\\'")}',`,
|
|
5685
|
+
`${indent} planned: ${p.status === "planned"},`,
|
|
5686
|
+
`${indent} surfaces: [${p.surfaces.map((s) => `'${s}'`).join(", ")}],`
|
|
5687
|
+
];
|
|
5688
|
+
if (p.display?.blurb) {
|
|
5689
|
+
lines.push(`${indent} blurb: '${p.display.blurb.replace(/'/g, "\\'")}',`);
|
|
5690
|
+
}
|
|
5691
|
+
if (p.display?.hint) {
|
|
5692
|
+
lines.push(`${indent} hint: '${p.display.hint.replace(/'/g, "\\'")}',`);
|
|
5693
|
+
}
|
|
5694
|
+
lines.push(`${indent}},`);
|
|
5695
|
+
return lines.join("\n");
|
|
5696
|
+
}
|
|
5697
|
+
function sortProviders(providers) {
|
|
5698
|
+
return [...providers].sort((a, b) => a.slug.localeCompare(b.slug));
|
|
5699
|
+
}
|
|
5700
|
+
function buildProvidersFile(ctx) {
|
|
5701
|
+
const providers = sortProviders(ctx.providers ?? []);
|
|
5702
|
+
const categories = ctx.config.catalogCategories;
|
|
5703
|
+
const flat = providers.map((p) => vendorLiteral(p, " ")).join("\n");
|
|
5704
|
+
const groups = categories.map((cat) => {
|
|
5705
|
+
const vendors = providers.filter((p) => p.display?.category === cat.id);
|
|
5706
|
+
const vendorBlock = vendors.map((p) => vendorLiteral(p, " ")).join("\n");
|
|
5707
|
+
return [
|
|
5708
|
+
" {",
|
|
5709
|
+
` id: '${cat.id}',`,
|
|
5710
|
+
` name: '${cat.name.replace(/'/g, "\\'")}',`,
|
|
5711
|
+
` blurb: '${cat.blurb.replace(/'/g, "\\'")}',`,
|
|
5712
|
+
vendors.length > 0 ? ` vendors: [
|
|
5713
|
+
${vendorBlock}
|
|
5714
|
+
],` : " vendors: [],",
|
|
5715
|
+
" },"
|
|
5716
|
+
].join("\n");
|
|
5717
|
+
}).join("\n");
|
|
5718
|
+
const body = `/**
|
|
5719
|
+
* Providers catalog \u2014 emitted from \`definitions/providers/*.yaml\` (slug,
|
|
5720
|
+
* display_name, surfaces, status, display) + \`frontend.catalog.categories\`
|
|
5721
|
+
* (codegen.config.yaml). Provider truth lives in the definitions; this file
|
|
5722
|
+
* is a projection \u2014 never hand-edit, never hand-duplicate.
|
|
5723
|
+
*
|
|
5724
|
+
* \`planned: true\` vendors are roadmap stubs (no backend integration yet) \u2014
|
|
5725
|
+
* render them as unconnectable tiles. Join live rows on \`provider\` (the
|
|
5726
|
+
* canonical slug, e.g. \`Connection.provider\`).
|
|
5727
|
+
*/
|
|
5728
|
+
|
|
5729
|
+
export type ProviderStatus = 'active' | 'planned';
|
|
5730
|
+
|
|
5731
|
+
export interface CatalogVendor {
|
|
5732
|
+
/** Provider slug \u2014 joins to \`Connection.provider\` / STRATEGY_REGISTRY keys. */
|
|
5733
|
+
provider: string;
|
|
5734
|
+
name: string;
|
|
5735
|
+
/** True for roadmap stubs (\`status: planned\`) \u2014 no backend integration yet. */
|
|
5736
|
+
planned: boolean;
|
|
5737
|
+
/** Surfaces this provider serves (ADR-0006). */
|
|
5738
|
+
surfaces: string[];
|
|
5739
|
+
blurb?: string;
|
|
5740
|
+
/** Sub-line shown on an unconnected ("available") tile. */
|
|
5741
|
+
hint?: string;
|
|
5742
|
+
}
|
|
5743
|
+
|
|
5744
|
+
export interface CatalogCategory {
|
|
5745
|
+
id: string;
|
|
5746
|
+
name: string;
|
|
5747
|
+
blurb: string;
|
|
5748
|
+
vendors: CatalogVendor[];
|
|
5749
|
+
}
|
|
5750
|
+
|
|
5751
|
+
/** Every provider definition, flat (active + planned), slug-sorted. */
|
|
5752
|
+
export const PROVIDERS: CatalogVendor[] = [
|
|
5753
|
+
${flat}
|
|
5754
|
+
];
|
|
5755
|
+
|
|
5756
|
+
/**
|
|
5757
|
+
* Category-grouped catalog (\`frontend.catalog.categories\` order). Providers
|
|
5758
|
+
* join a group via \`display.category\`; uncategorized providers appear only
|
|
5759
|
+
* in \`PROVIDERS\`.
|
|
5760
|
+
*/
|
|
5761
|
+
export const PROVIDER_CATALOG: CatalogCategory[] = [
|
|
5762
|
+
${groups}
|
|
5763
|
+
];
|
|
5764
|
+
`;
|
|
5765
|
+
return withBanner(SOURCE_DESC2, body);
|
|
5766
|
+
}
|
|
5767
|
+
function emitProviders(ctx, outDir) {
|
|
5768
|
+
if (!ctx.providers || ctx.providers.length === 0) return [];
|
|
5769
|
+
const path36 = join16(outDir, "providers.ts");
|
|
5770
|
+
writeFile2(path36, buildProvidersFile(ctx));
|
|
5771
|
+
return [path36];
|
|
5772
|
+
}
|
|
5773
|
+
|
|
5774
|
+
// src/emitters/frontend/emit-index.ts
|
|
5775
|
+
import { join as join17 } from "path";
|
|
5776
|
+
|
|
5777
|
+
// src/emitters/frontend/deps.ts
|
|
5778
|
+
var FRONTEND_EMITTED_DEPS = {
|
|
5779
|
+
"@pattern-stack/frontend-patterns": "^0.2.0-alpha.18",
|
|
5780
|
+
"@tanstack/react-db": "^0.1.55",
|
|
5781
|
+
"@tanstack/electric-db-collection": "^0.2.11",
|
|
5782
|
+
"@tanstack/query-db-collection": "^1.0.6",
|
|
5783
|
+
"@tanstack/react-query": "^5.0.0"
|
|
5784
|
+
};
|
|
5785
|
+
|
|
5786
|
+
// src/emitters/frontend/emit-index.ts
|
|
5787
|
+
var SOURCE_DESC_SET6 = "the entity set";
|
|
5788
|
+
function buildVersionPairingComment() {
|
|
5789
|
+
const entries = Object.entries(FRONTEND_EMITTED_DEPS);
|
|
5790
|
+
const nameWidth = Math.max(...entries.map(([name]) => name.length));
|
|
5791
|
+
const rows = entries.map(([name, range]) => ` * ${name.padEnd(nameWidth)} ${range}`).join("\n");
|
|
5792
|
+
return ` * Version pairing \u2014 the emitted imports require these package ranges in the
|
|
5793
|
+
* consumer's frontend package.json:
|
|
5794
|
+
*
|
|
5795
|
+
${rows}`;
|
|
5796
|
+
}
|
|
5797
|
+
function buildRootIndexFile(ctx) {
|
|
5798
|
+
const entities = sortEntities(ctx.entities);
|
|
5799
|
+
const entityList = entities.map((e) => ` * - ${e.className}`).join("\n");
|
|
5800
|
+
const body = `/**
|
|
5801
|
+
* Generated frontend data layer.
|
|
5802
|
+
*
|
|
5803
|
+
* Entities:
|
|
5804
|
+
${entityList || " * (none)"}
|
|
5805
|
+
*
|
|
5806
|
+
${buildVersionPairingComment()}
|
|
5807
|
+
*/
|
|
5808
|
+
|
|
5809
|
+
// Per-entity sync configuration + runtime overrides
|
|
5810
|
+
export * from './config';
|
|
5811
|
+
|
|
5812
|
+
// Shared TanStack QueryClient
|
|
5813
|
+
export * from './query-client';
|
|
5814
|
+
|
|
5815
|
+
// REST api client
|
|
5816
|
+
export * from './api/index';
|
|
5817
|
+
|
|
5818
|
+
// TanStack DB collections (per-entity sync mode)
|
|
5819
|
+
export * from './collections/index';
|
|
5820
|
+
|
|
5821
|
+
// Entity hooks (createEntityHooks wiring)
|
|
5822
|
+
export * from './entities/index';
|
|
5823
|
+
|
|
5824
|
+
// Field metadata (DataGrid / forms / admin)
|
|
5825
|
+
export * from './fields/index';
|
|
5826
|
+
${ctx.providers && ctx.providers.length > 0 ? `
|
|
5827
|
+
// Providers catalog (definitions/providers + frontend.catalog.categories)
|
|
5828
|
+
export * from './providers';
|
|
5829
|
+
` : ""}
|
|
5830
|
+
// Unified store (entities + collections + resolvers + lookups)
|
|
5831
|
+
export * from './store/module-index';
|
|
5832
|
+
`;
|
|
5833
|
+
return withBanner(SOURCE_DESC_SET6, body);
|
|
5834
|
+
}
|
|
5835
|
+
function emitIndex(ctx, outDir) {
|
|
5836
|
+
const indexPath = join17(outDir, "index.ts");
|
|
5837
|
+
writeFile2(indexPath, buildRootIndexFile(ctx));
|
|
5838
|
+
return [indexPath];
|
|
5839
|
+
}
|
|
5840
|
+
|
|
5841
|
+
// src/emitters/frontend/load-context.ts
|
|
5842
|
+
import { existsSync as existsSync8, statSync as statSync5 } from "fs";
|
|
4823
5843
|
import path12 from "path";
|
|
5844
|
+
|
|
5845
|
+
// src/schema/codegen-config.schema.ts
|
|
5846
|
+
import { z } from "zod";
|
|
5847
|
+
var GenerateConfigSchema = z.object({
|
|
5848
|
+
/**
|
|
5849
|
+
* Backend architecture to generate. One of:
|
|
5850
|
+
* - 'clean' — Full Clean Architecture (domain + application + infrastructure + presentation)
|
|
5851
|
+
* - 'clean-lite-ps' — Clean-Lite-PS modules/{plural}/ layout
|
|
5852
|
+
*
|
|
5853
|
+
* Default: 'clean'.
|
|
5854
|
+
*/
|
|
5855
|
+
architecture: z.enum(["clean", "clean-lite-ps"]).default("clean"),
|
|
5856
|
+
/**
|
|
5857
|
+
* Whether to emit the frontend pipeline (collections, hooks, entity metadata).
|
|
5858
|
+
* Default: false — backend-only projects opt out by default.
|
|
5859
|
+
*/
|
|
5860
|
+
frontend: z.boolean().default(false),
|
|
5861
|
+
/**
|
|
5862
|
+
* Analytics backend to generate.
|
|
5863
|
+
* - 'none': no analytics layer (default)
|
|
5864
|
+
* - 'cube': generate cube.js semantic layer and analytics providers
|
|
5865
|
+
*/
|
|
5866
|
+
analytics: z.enum(["none", "cube"]).default("none")
|
|
5867
|
+
}).passthrough();
|
|
5868
|
+
var PathsConfigSchema = z.object({
|
|
5869
|
+
events_dir: z.string().optional(),
|
|
5870
|
+
generated: z.string().default("src/generated")
|
|
5871
|
+
}).passthrough();
|
|
5872
|
+
var PatternsConfigSchema = z.array(z.string()).optional().default(["src/patterns/*.pattern.ts"]);
|
|
5873
|
+
var RuntimeModeSchema = z.enum(["package", "vendored"]).default("package");
|
|
5874
|
+
var FrontendAuthConfigSchema = z.object({
|
|
5875
|
+
function: z.string().nullable().default("getAuthorizationHeader")
|
|
5876
|
+
}).default({ function: "getAuthorizationHeader" });
|
|
5877
|
+
var FrontendSyncConfigSchema = z.object({
|
|
5878
|
+
mode: z.enum(["api", "electric"]).default("electric"),
|
|
5879
|
+
shapeUrl: z.string().default("/v1/shape"),
|
|
5880
|
+
useTableParam: z.boolean().default(true),
|
|
5881
|
+
columnMapper: z.string().nullable().default("snakeCamelMapper"),
|
|
5882
|
+
columnMapperNeedsCall: z.boolean().default(true),
|
|
5883
|
+
apiBaseUrlImport: z.string().nullable().default(null),
|
|
5884
|
+
apiUrl: z.string().default("/api")
|
|
5885
|
+
}).default({});
|
|
5886
|
+
var FrontendCatalogConfigSchema = z.object({
|
|
5887
|
+
categories: z.array(
|
|
5888
|
+
z.object({
|
|
5889
|
+
id: z.string(),
|
|
5890
|
+
name: z.string(),
|
|
5891
|
+
blurb: z.string().default("")
|
|
5892
|
+
}).strict()
|
|
5893
|
+
).default([])
|
|
5894
|
+
}).default({});
|
|
5895
|
+
var FrontendConfigSchema = z.object({
|
|
5896
|
+
auth: FrontendAuthConfigSchema,
|
|
5897
|
+
parsers: z.record(z.string()).default({ timestamptz: "(date: string) => new Date(date)" }),
|
|
5898
|
+
sync: FrontendSyncConfigSchema,
|
|
5899
|
+
catalog: FrontendCatalogConfigSchema
|
|
5900
|
+
}).strict().default({});
|
|
5901
|
+
|
|
5902
|
+
// src/emitters/frontend/load-context.ts
|
|
5903
|
+
var DEFAULT_DB_ENTITIES = {
|
|
5904
|
+
path: "packages/db/src/entities",
|
|
5905
|
+
import: "@repo/db/entities"
|
|
5906
|
+
};
|
|
5907
|
+
var DEFAULT_FRONTEND_GENERATED = {
|
|
5908
|
+
path: "apps/frontend/src/generated",
|
|
5909
|
+
import: "@/generated"
|
|
5910
|
+
};
|
|
5911
|
+
var DEFAULT_FRONTEND_COLLECTIONS_AUTH = {
|
|
5912
|
+
path: "apps/frontend/src/lib/collections/auth",
|
|
5913
|
+
import: "@/lib/collections/auth"
|
|
5914
|
+
};
|
|
5915
|
+
function resolveLocation(config, key, fallback) {
|
|
5916
|
+
const override = config.locations?.[key];
|
|
5917
|
+
return {
|
|
5918
|
+
path: override?.path ?? fallback.path,
|
|
5919
|
+
import: override?.import ?? fallback.import
|
|
5920
|
+
};
|
|
5921
|
+
}
|
|
5922
|
+
function mapFrontendEmitConfig(config) {
|
|
5923
|
+
const parsed = FrontendConfigSchema.safeParse(config.frontend ?? {});
|
|
5924
|
+
const fe = parsed.success ? parsed.data : FrontendConfigSchema.parse({});
|
|
5925
|
+
const dbEntities = resolveLocation(config, "dbEntities", DEFAULT_DB_ENTITIES);
|
|
5926
|
+
const collectionsAuth = resolveLocation(
|
|
5927
|
+
config,
|
|
5928
|
+
"frontendCollectionsAuth",
|
|
5929
|
+
DEFAULT_FRONTEND_COLLECTIONS_AUTH
|
|
5930
|
+
);
|
|
5931
|
+
const architecture = config.generate?.architecture === "clean-lite-ps" ? "clean-lite-ps" : "clean";
|
|
5932
|
+
return {
|
|
5933
|
+
globalSyncMode: fe.sync.mode,
|
|
5934
|
+
// auth.function: absent → 'getAuthorizationHeader' (schema default), explicit
|
|
5935
|
+
// null → disabled (no header lines emitted).
|
|
5936
|
+
authFunction: fe.auth.function,
|
|
5937
|
+
authImport: collectionsAuth.import,
|
|
5938
|
+
shapeUrl: fe.sync.shapeUrl,
|
|
5939
|
+
useTableParam: fe.sync.useTableParam,
|
|
5940
|
+
columnMapper: fe.sync.columnMapper,
|
|
5941
|
+
columnMapperNeedsCall: fe.sync.columnMapperNeedsCall,
|
|
5942
|
+
apiUrl: fe.sync.apiUrl,
|
|
5943
|
+
apiBaseUrlImport: fe.sync.apiBaseUrlImport,
|
|
5944
|
+
parsers: fe.parsers,
|
|
5945
|
+
architecture,
|
|
5946
|
+
dbEntitiesImport: dbEntities.import,
|
|
5947
|
+
catalogCategories: fe.catalog.categories
|
|
5948
|
+
};
|
|
5949
|
+
}
|
|
5950
|
+
function loadProviderCatalogInputs(cwd, config) {
|
|
5951
|
+
const providersDir = path12.resolve(
|
|
5952
|
+
cwd,
|
|
5953
|
+
config.paths?.providers ?? "definitions/providers"
|
|
5954
|
+
);
|
|
5955
|
+
if (!existsSync8(providersDir) || !statSync5(providersDir).isDirectory()) {
|
|
5956
|
+
return [];
|
|
5957
|
+
}
|
|
5958
|
+
const files = findYamlFiles(providersDir);
|
|
5959
|
+
if (files.length === 0) return [];
|
|
5960
|
+
return loadProvidersFromYaml(files).successes.map((s) => ({
|
|
5961
|
+
slug: s.definition.slug,
|
|
5962
|
+
displayName: s.definition.display_name,
|
|
5963
|
+
surfaces: s.definition.surfaces,
|
|
5964
|
+
status: s.definition.status,
|
|
5965
|
+
display: s.definition.display
|
|
5966
|
+
}));
|
|
5967
|
+
}
|
|
5968
|
+
function loadFrontendEmitContext(cwd, config, opts = {}) {
|
|
5969
|
+
const entitiesDir = opts.entitiesDir ?? path12.resolve(cwd, config.paths?.entities_dir ?? "entities");
|
|
5970
|
+
const { registry } = loadEntityRegistry(entitiesDir);
|
|
5971
|
+
const entities = sortEntities([...registry.values()]);
|
|
5972
|
+
if (entities.length === 0) {
|
|
5973
|
+
return {
|
|
5974
|
+
skip: `no entities found in ${path12.relative(cwd, entitiesDir) || entitiesDir}`
|
|
5975
|
+
};
|
|
5976
|
+
}
|
|
5977
|
+
const parsedList = loadEntities(entitiesDir).entities;
|
|
5978
|
+
const parsed = new Map(
|
|
5979
|
+
parsedList.map((p) => [p.name, p])
|
|
5980
|
+
);
|
|
5981
|
+
const emitConfig = mapFrontendEmitConfig(config);
|
|
5982
|
+
const providers = loadProviderCatalogInputs(cwd, config);
|
|
5983
|
+
const generated = resolveLocation(
|
|
5984
|
+
config,
|
|
5985
|
+
"frontendGenerated",
|
|
5986
|
+
DEFAULT_FRONTEND_GENERATED
|
|
5987
|
+
);
|
|
5988
|
+
const outDir = path12.resolve(cwd, generated.path);
|
|
5989
|
+
return {
|
|
5990
|
+
skip: void 0,
|
|
5991
|
+
ctx: { entities, parsed, config: emitConfig, providers },
|
|
5992
|
+
outDir
|
|
5993
|
+
};
|
|
5994
|
+
}
|
|
5995
|
+
|
|
5996
|
+
// src/emitters/frontend/index.ts
|
|
5997
|
+
function emitFrontendSet(ctx, outDir) {
|
|
5998
|
+
return [
|
|
5999
|
+
...emitBase(ctx, outDir),
|
|
6000
|
+
...emitApi(ctx, outDir),
|
|
6001
|
+
...emitCollections(ctx, outDir),
|
|
6002
|
+
...emitEntities(ctx, outDir),
|
|
6003
|
+
...emitStore(ctx, outDir),
|
|
6004
|
+
...emitFields(ctx, outDir),
|
|
6005
|
+
...emitProviders(ctx, outDir),
|
|
6006
|
+
...emitIndex(ctx, outDir)
|
|
6007
|
+
];
|
|
6008
|
+
}
|
|
6009
|
+
|
|
6010
|
+
// src/cli/shared/events-path.ts
|
|
6011
|
+
import path13 from "path";
|
|
4824
6012
|
var FALLBACK = "events";
|
|
4825
6013
|
function resolveEventsDirFromConfig(cwd, config) {
|
|
4826
6014
|
const configured = config?.paths?.events_dir;
|
|
4827
6015
|
if (typeof configured === "string" && configured.length > 0) {
|
|
4828
|
-
return
|
|
6016
|
+
return path13.resolve(cwd, configured);
|
|
4829
6017
|
}
|
|
4830
|
-
return
|
|
6018
|
+
return path13.resolve(cwd, FALLBACK);
|
|
4831
6019
|
}
|
|
4832
6020
|
function resolveEventsDir(ctx) {
|
|
4833
6021
|
return resolveEventsDirFromConfig(ctx.cwd, ctx.config);
|
|
@@ -4856,7 +6044,7 @@ function printInfo(msg) {
|
|
|
4856
6044
|
// src/cli/commands/entity.ts
|
|
4857
6045
|
function resolveProvidersDir(ctx) {
|
|
4858
6046
|
const fromConfig = ctx.config?.paths?.providers;
|
|
4859
|
-
return fromConfig != null ?
|
|
6047
|
+
return fromConfig != null ? path14.resolve(ctx.cwd, fromConfig) : path14.resolve(ctx.cwd, "definitions/providers");
|
|
4860
6048
|
}
|
|
4861
6049
|
function listEntityYamls2(dir, providersDir) {
|
|
4862
6050
|
if (!fs10.existsSync(dir)) return [];
|
|
@@ -4938,10 +6126,10 @@ async function hints(ctx) {
|
|
|
4938
6126
|
{ command: "codegen entity validate", description: "Validate YAML definitions" },
|
|
4939
6127
|
{ command: "codegen entity list", description: "List entities as a table" }
|
|
4940
6128
|
];
|
|
4941
|
-
const providersDir = ctx.config?.paths?.providers != null ?
|
|
6129
|
+
const providersDir = ctx.config?.paths?.providers != null ? path14.resolve(
|
|
4942
6130
|
ctx.cwd,
|
|
4943
6131
|
ctx.config.paths.providers
|
|
4944
|
-
) :
|
|
6132
|
+
) : path14.resolve(ctx.cwd, "definitions/providers");
|
|
4945
6133
|
if (fs10.existsSync(providersDir)) {
|
|
4946
6134
|
baseHints.push({
|
|
4947
6135
|
command: "codegen entity new --all",
|
|
@@ -4997,14 +6185,14 @@ var EntityNewCommand = class extends Command2 {
|
|
|
4997
6185
|
}
|
|
4998
6186
|
let targets = [];
|
|
4999
6187
|
if (this.all) {
|
|
5000
|
-
const dir = ctx.entitiesDir ??
|
|
6188
|
+
const dir = ctx.entitiesDir ?? path14.resolve(ctx.cwd, "entities");
|
|
5001
6189
|
targets = listEntityYamls2(dir, resolveProvidersDir(ctx));
|
|
5002
6190
|
if (targets.length === 0) {
|
|
5003
6191
|
printError(`No entity YAML files found in ${dir}`);
|
|
5004
6192
|
return 1;
|
|
5005
6193
|
}
|
|
5006
6194
|
} else if (this.yaml) {
|
|
5007
|
-
targets = [
|
|
6195
|
+
targets = [path14.resolve(ctx.cwd, this.yaml)];
|
|
5008
6196
|
} else {
|
|
5009
6197
|
printError("Missing YAML path. Pass a file or --all.");
|
|
5010
6198
|
return 2;
|
|
@@ -5021,7 +6209,7 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5021
6209
|
}
|
|
5022
6210
|
if (invalid.length > 0 && !this.continueOnError) {
|
|
5023
6211
|
for (const i of invalid) {
|
|
5024
|
-
printError(`${
|
|
6212
|
+
printError(`${path14.basename(i.file)} \u2014 ${i.message}`);
|
|
5025
6213
|
for (const detail of i.details ?? []) {
|
|
5026
6214
|
printError(` \u2022 ${detail}`);
|
|
5027
6215
|
}
|
|
@@ -5030,7 +6218,7 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5030
6218
|
return 1;
|
|
5031
6219
|
}
|
|
5032
6220
|
}
|
|
5033
|
-
const entitiesDirForEmits = ctx.entitiesDir ??
|
|
6221
|
+
const entitiesDirForEmits = ctx.entitiesDir ?? path14.resolve(ctx.cwd, "entities");
|
|
5034
6222
|
const eventsDirForEmits = resolveEventsDir(ctx);
|
|
5035
6223
|
const allEntitiesForEmits = loadEntities(entitiesDirForEmits, {
|
|
5036
6224
|
excludeDirs: [resolveProvidersDir(ctx)]
|
|
@@ -5079,29 +6267,29 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5079
6267
|
if (!isJsonMode()) return 1;
|
|
5080
6268
|
}
|
|
5081
6269
|
}
|
|
5082
|
-
const entitiesDir = ctx.entitiesDir ??
|
|
5083
|
-
const relationshipsDir =
|
|
6270
|
+
const entitiesDir = ctx.entitiesDir ?? path14.resolve(ctx.cwd, "entities");
|
|
6271
|
+
const relationshipsDir = path14.resolve(ctx.cwd, "relationships");
|
|
5084
6272
|
const generatedDir = resolveGeneratedDir(ctx);
|
|
5085
6273
|
const architecture = resolveArchitecture(ctx);
|
|
5086
6274
|
const subsystemsRoot = resolveSubsystemsRoot(ctx);
|
|
5087
6275
|
const runtimeMode = resolveRuntimeMode(ctx.config);
|
|
5088
|
-
const scopeEntityTypePath = runtimeMode === "package" ?
|
|
6276
|
+
const scopeEntityTypePath = runtimeMode === "package" ? path14.resolve(generatedDir, "scope-entity-type.ts") : path14.resolve(subsystemsRoot, "jobs/generated/scope-entity-type.ts");
|
|
5089
6277
|
const eventsDir = resolveEventsDir(ctx);
|
|
5090
|
-
const eventCodegenOutputDir = runtimeMode === "package" ?
|
|
6278
|
+
const eventCodegenOutputDir = runtimeMode === "package" ? path14.resolve(generatedDir, "events") : path14.resolve(subsystemsRoot, "events/generated");
|
|
5091
6279
|
const bridgeInstalledForRegistry = configuredSubsystemNames(
|
|
5092
6280
|
ctx.config
|
|
5093
6281
|
).includes("bridge");
|
|
5094
|
-
const bridgeRegistryOutputDir = runtimeMode === "package" ? generatedDir :
|
|
6282
|
+
const bridgeRegistryOutputDir = runtimeMode === "package" ? generatedDir : path14.resolve(subsystemsRoot, "bridge/generated");
|
|
5095
6283
|
const backendSrcForHandlers = ctx.config?.paths?.backend_src ?? "src";
|
|
5096
|
-
const bridgeHandlersDir =
|
|
6284
|
+
const bridgeHandlersDir = path14.resolve(
|
|
5097
6285
|
ctx.cwd,
|
|
5098
6286
|
backendSrcForHandlers,
|
|
5099
6287
|
"jobs"
|
|
5100
6288
|
);
|
|
5101
6289
|
const orchestrationConfigured = ctx.config?.paths?.orchestration_src;
|
|
5102
|
-
const orchestrationOutputRoot =
|
|
6290
|
+
const orchestrationOutputRoot = path14.resolve(
|
|
5103
6291
|
ctx.cwd,
|
|
5104
|
-
typeof orchestrationConfigured === "string" && orchestrationConfigured.length > 0 ? orchestrationConfigured :
|
|
6292
|
+
typeof orchestrationConfigured === "string" && orchestrationConfigured.length > 0 ? orchestrationConfigured : path14.join(backendSrcForHandlers, "orchestration")
|
|
5105
6293
|
);
|
|
5106
6294
|
const orchestrationGlobs = (() => {
|
|
5107
6295
|
const fromCfg = ctx.config?.patterns;
|
|
@@ -5232,7 +6420,7 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5232
6420
|
}
|
|
5233
6421
|
if (invalid.length > 0) {
|
|
5234
6422
|
for (const i of invalid) {
|
|
5235
|
-
printWarning(`${
|
|
6423
|
+
printWarning(`${path14.basename(i.file)} \u2014 ${i.message}`);
|
|
5236
6424
|
}
|
|
5237
6425
|
}
|
|
5238
6426
|
console.log("");
|
|
@@ -5250,7 +6438,7 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5250
6438
|
}
|
|
5251
6439
|
const succeeded = [];
|
|
5252
6440
|
const failed = [
|
|
5253
|
-
...invalid.map((i) => ({ name:
|
|
6441
|
+
...invalid.map((i) => ({ name: path14.basename(i.file), file: i.file, message: i.message }))
|
|
5254
6442
|
];
|
|
5255
6443
|
for (const v of validated) {
|
|
5256
6444
|
if (!isJsonMode()) {
|
|
@@ -5373,10 +6561,40 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5373
6561
|
}
|
|
5374
6562
|
}
|
|
5375
6563
|
}
|
|
6564
|
+
let frontendResult = null;
|
|
6565
|
+
const frontendEnabled = ctx.config?.generate?.frontend === true;
|
|
6566
|
+
if (frontendEnabled) {
|
|
6567
|
+
try {
|
|
6568
|
+
const loaded = loadFrontendEmitContext(
|
|
6569
|
+
ctx.cwd,
|
|
6570
|
+
ctx.config,
|
|
6571
|
+
{ entitiesDir }
|
|
6572
|
+
);
|
|
6573
|
+
if (loaded.skip !== void 0) {
|
|
6574
|
+
if (!isJsonMode()) {
|
|
6575
|
+
printInfo(`frontend emission skipped \u2014 ${loaded.skip}`);
|
|
6576
|
+
}
|
|
6577
|
+
} else {
|
|
6578
|
+
const { ctx: frontendCtx, outDir: frontendOutDir } = loaded;
|
|
6579
|
+
const written = emitFrontendSet(frontendCtx, frontendOutDir);
|
|
6580
|
+
frontendResult = { written, outDir: frontendOutDir };
|
|
6581
|
+
if (!isJsonMode()) {
|
|
6582
|
+
printInfo(
|
|
6583
|
+
`frontend emitted (${written.length} files) \u2192 ${path14.relative(ctx.cwd, frontendOutDir)}`
|
|
6584
|
+
);
|
|
6585
|
+
}
|
|
6586
|
+
}
|
|
6587
|
+
} catch (err) {
|
|
6588
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6589
|
+
if (!isJsonMode()) {
|
|
6590
|
+
printWarning(`frontend emission failed \u2014 ${msg}`);
|
|
6591
|
+
}
|
|
6592
|
+
}
|
|
6593
|
+
}
|
|
5376
6594
|
let providerResult = null;
|
|
5377
6595
|
try {
|
|
5378
6596
|
const providersDir = resolveProvidersDir(ctx);
|
|
5379
|
-
const providerOutputRoot =
|
|
6597
|
+
const providerOutputRoot = path14.resolve(
|
|
5380
6598
|
ctx.cwd,
|
|
5381
6599
|
backendSrcForHandlers,
|
|
5382
6600
|
"integrations/providers"
|
|
@@ -5418,7 +6636,7 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5418
6636
|
try {
|
|
5419
6637
|
if (providerResult && !providerResult.skipped && providerResult.issues.length === 0) {
|
|
5420
6638
|
const providersDir = providerResult.providersDir;
|
|
5421
|
-
const adapterOutputRoot =
|
|
6639
|
+
const adapterOutputRoot = path14.resolve(
|
|
5422
6640
|
ctx.cwd,
|
|
5423
6641
|
backendSrcForHandlers,
|
|
5424
6642
|
"integrations"
|
|
@@ -5437,7 +6655,7 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5437
6655
|
providers: loadedProviders,
|
|
5438
6656
|
entities: entityDefs,
|
|
5439
6657
|
outputRoot: adapterOutputRoot,
|
|
5440
|
-
backendSrcAbs:
|
|
6658
|
+
backendSrcAbs: path14.resolve(ctx.cwd, backendSrcForHandlers),
|
|
5441
6659
|
aliases: assemblyTsAliases?.aliases ?? {},
|
|
5442
6660
|
mode: runtimeMode
|
|
5443
6661
|
});
|
|
@@ -5514,6 +6732,11 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5514
6732
|
relativePath: f.relativePath
|
|
5515
6733
|
}))
|
|
5516
6734
|
} : null,
|
|
6735
|
+
frontend: frontendResult ? {
|
|
6736
|
+
outDir: frontendResult.outDir,
|
|
6737
|
+
written: frontendResult.written,
|
|
6738
|
+
fileCount: frontendResult.written.length
|
|
6739
|
+
} : null,
|
|
5517
6740
|
emits: {
|
|
5518
6741
|
warnings: emitsWarnings.map((w) => ({
|
|
5519
6742
|
entity: w.entity ?? null,
|
|
@@ -5539,22 +6762,27 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5539
6762
|
}
|
|
5540
6763
|
if (barrelResult) {
|
|
5541
6764
|
printInfo(
|
|
5542
|
-
`barrels regenerated (${barrelResult.entityCount} entities) \u2192 ${
|
|
6765
|
+
`barrels regenerated (${barrelResult.entityCount} entities) \u2192 ${path14.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path14.relative(ctx.cwd, barrelResult.schemaBarrel)}`
|
|
5543
6766
|
);
|
|
5544
6767
|
}
|
|
5545
6768
|
if (scopeResult) {
|
|
5546
6769
|
printInfo(
|
|
5547
|
-
`scope-entity-type regenerated (${scopeResult.scopeableNames.length} scopeable) \u2192 ${
|
|
6770
|
+
`scope-entity-type regenerated (${scopeResult.scopeableNames.length} scopeable) \u2192 ${path14.relative(ctx.cwd, scopeResult.outputPath)}`
|
|
5548
6771
|
);
|
|
5549
6772
|
}
|
|
5550
6773
|
if (eventCodegenResult) {
|
|
5551
6774
|
printInfo(
|
|
5552
|
-
`event codegen regenerated (${eventCodegenResult.eventCount} events) \u2192 ${
|
|
6775
|
+
`event codegen regenerated (${eventCodegenResult.eventCount} events) \u2192 ${path14.relative(ctx.cwd, eventCodegenResult.outputDir)}`
|
|
5553
6776
|
);
|
|
5554
6777
|
}
|
|
5555
6778
|
if (orchestrationResult && orchestrationResult.patterns.length > 0) {
|
|
5556
6779
|
printInfo(
|
|
5557
|
-
`orchestration regenerated (${orchestrationResult.patterns.length} patterns, ${orchestrationResult.files.length} files) \u2192 ${
|
|
6780
|
+
`orchestration regenerated (${orchestrationResult.patterns.length} patterns, ${orchestrationResult.files.length} files) \u2192 ${path14.relative(ctx.cwd, orchestrationResult.outputRoot)}`
|
|
6781
|
+
);
|
|
6782
|
+
}
|
|
6783
|
+
if (frontendResult) {
|
|
6784
|
+
printInfo(
|
|
6785
|
+
`frontend regenerated (${frontendResult.written.length} files) \u2192 ${path14.relative(ctx.cwd, frontendResult.outDir)}`
|
|
5558
6786
|
);
|
|
5559
6787
|
}
|
|
5560
6788
|
}
|
|
@@ -5640,7 +6868,7 @@ var EntityValidateCommand = class extends Command2 {
|
|
|
5640
6868
|
json: this.json,
|
|
5641
6869
|
skipDetection: true
|
|
5642
6870
|
});
|
|
5643
|
-
const targetDir = this.dir ?
|
|
6871
|
+
const targetDir = this.dir ? path14.resolve(ctx.cwd, this.dir) : ctx.entitiesDir ?? path14.resolve(ctx.cwd, "entities");
|
|
5644
6872
|
if (!fs10.existsSync(targetDir)) {
|
|
5645
6873
|
printError(`Directory not found: ${targetDir}`);
|
|
5646
6874
|
return 1;
|
|
@@ -5693,7 +6921,7 @@ var entity_default = entityNoun;
|
|
|
5693
6921
|
|
|
5694
6922
|
// src/cli/commands/subsystem.ts
|
|
5695
6923
|
import fs13 from "fs";
|
|
5696
|
-
import
|
|
6924
|
+
import path24 from "path";
|
|
5697
6925
|
import { Command as Command3, Option as Option3 } from "clipanion";
|
|
5698
6926
|
|
|
5699
6927
|
// src/cli/shared/config-block-detect.ts
|
|
@@ -5726,26 +6954,26 @@ function stripConfigBlock(yamlSource, subsystem) {
|
|
|
5726
6954
|
}
|
|
5727
6955
|
|
|
5728
6956
|
// src/cli/shared/events-scaffold-locals.ts
|
|
5729
|
-
import
|
|
6957
|
+
import path15 from "path";
|
|
5730
6958
|
function resolveEventsScaffoldLocals(input) {
|
|
5731
6959
|
const { cwd, config } = input;
|
|
5732
6960
|
void input.fileExists;
|
|
5733
6961
|
const eventsBlock = config?.events ?? {};
|
|
5734
6962
|
const subsystemsRoot = resolveSubsystemsRootFromConfig(cwd, config);
|
|
5735
|
-
const configPath =
|
|
5736
|
-
const schemaPath =
|
|
6963
|
+
const configPath = path15.resolve(cwd, "codegen.config.yaml");
|
|
6964
|
+
const schemaPath = path15.resolve(
|
|
5737
6965
|
subsystemsRoot,
|
|
5738
6966
|
"events",
|
|
5739
6967
|
"domain-events.schema.ts"
|
|
5740
6968
|
);
|
|
5741
|
-
const generatedKeepPath =
|
|
6969
|
+
const generatedKeepPath = path15.resolve(
|
|
5742
6970
|
subsystemsRoot,
|
|
5743
6971
|
"events",
|
|
5744
6972
|
"generated",
|
|
5745
6973
|
".gitkeep"
|
|
5746
6974
|
);
|
|
5747
6975
|
return {
|
|
5748
|
-
appName:
|
|
6976
|
+
appName: path15.basename(cwd),
|
|
5749
6977
|
multiTenant: normaliseMultiTenant(eventsBlock.multi_tenant),
|
|
5750
6978
|
configPath,
|
|
5751
6979
|
schemaPath,
|
|
@@ -5771,7 +6999,7 @@ function localsToHygenArgs(locals) {
|
|
|
5771
6999
|
}
|
|
5772
7000
|
|
|
5773
7001
|
// src/cli/shared/jobs-scaffold-locals.ts
|
|
5774
|
-
import
|
|
7002
|
+
import path16 from "path";
|
|
5775
7003
|
var MAIN_HOOK_SENTINEL = "JOBS \u2014 Embedded worker mode (optional)";
|
|
5776
7004
|
function workerSkipValue(exists) {
|
|
5777
7005
|
return exists ? "true" : "";
|
|
@@ -5780,10 +7008,10 @@ function resolveJobsScaffoldLocals(input) {
|
|
|
5780
7008
|
const { cwd, config, fileExists, readFile } = input;
|
|
5781
7009
|
const jobsBlock = config?.jobs ?? {};
|
|
5782
7010
|
const subsystemsRoot = resolveSubsystemsRootFromConfig(cwd, config);
|
|
5783
|
-
const workerPath =
|
|
5784
|
-
const mainTsPath =
|
|
5785
|
-
const configPath =
|
|
5786
|
-
const schemaPath =
|
|
7011
|
+
const workerPath = path16.resolve(cwd, "worker.ts");
|
|
7012
|
+
const mainTsPath = path16.resolve(cwd, "src/main.ts");
|
|
7013
|
+
const configPath = path16.resolve(cwd, "codegen.config.yaml");
|
|
7014
|
+
const schemaPath = path16.resolve(
|
|
5787
7015
|
subsystemsRoot,
|
|
5788
7016
|
"jobs",
|
|
5789
7017
|
"job-orchestration.schema.ts"
|
|
@@ -5791,7 +7019,7 @@ function resolveJobsScaffoldLocals(input) {
|
|
|
5791
7019
|
const mainContent = readFile(mainTsPath);
|
|
5792
7020
|
const mainHookInjected = mainContent !== null && mainContent.includes(MAIN_HOOK_SENTINEL);
|
|
5793
7021
|
return {
|
|
5794
|
-
appName:
|
|
7022
|
+
appName: path16.basename(cwd),
|
|
5795
7023
|
workerMode: normaliseWorkerMode(jobsBlock.worker_mode),
|
|
5796
7024
|
multiTenant: normaliseMultiTenant2(jobsBlock.multi_tenant),
|
|
5797
7025
|
mainTsPath,
|
|
@@ -5833,20 +7061,20 @@ function localsToHygenArgs2(locals) {
|
|
|
5833
7061
|
}
|
|
5834
7062
|
|
|
5835
7063
|
// src/cli/shared/integration-scaffold-locals.ts
|
|
5836
|
-
import
|
|
7064
|
+
import path17 from "path";
|
|
5837
7065
|
function resolveIntegrationScaffoldLocals(input) {
|
|
5838
7066
|
const { cwd, config } = input;
|
|
5839
7067
|
void input.fileExists;
|
|
5840
7068
|
const integrationBlock = config?.integration ?? {};
|
|
5841
7069
|
const subsystemsRoot = resolveSubsystemsRootFromConfig(cwd, config);
|
|
5842
|
-
const configPath =
|
|
5843
|
-
const schemaPath =
|
|
7070
|
+
const configPath = path17.resolve(cwd, "codegen.config.yaml");
|
|
7071
|
+
const schemaPath = path17.resolve(
|
|
5844
7072
|
subsystemsRoot,
|
|
5845
7073
|
"integration",
|
|
5846
7074
|
"integration-audit.schema.ts"
|
|
5847
7075
|
);
|
|
5848
7076
|
return {
|
|
5849
|
-
appName:
|
|
7077
|
+
appName: path17.basename(cwd),
|
|
5850
7078
|
multiTenant: normaliseMultiTenant3(integrationBlock.multi_tenant),
|
|
5851
7079
|
configPath,
|
|
5852
7080
|
schemaPath
|
|
@@ -5869,21 +7097,21 @@ function localsToHygenArgs3(locals) {
|
|
|
5869
7097
|
}
|
|
5870
7098
|
|
|
5871
7099
|
// src/cli/shared/bridge-scaffold-locals.ts
|
|
5872
|
-
import
|
|
7100
|
+
import path18 from "path";
|
|
5873
7101
|
function resolveBridgeScaffoldLocals(input) {
|
|
5874
7102
|
const { cwd, config } = input;
|
|
5875
7103
|
void input.fileExists;
|
|
5876
7104
|
const bridgeBlock = config?.bridge ?? {};
|
|
5877
7105
|
const subsystemsRoot = resolveSubsystemsRootFromConfig(cwd, config);
|
|
5878
|
-
const configPath =
|
|
5879
|
-
const generatedKeepPath =
|
|
7106
|
+
const configPath = path18.resolve(cwd, "codegen.config.yaml");
|
|
7107
|
+
const generatedKeepPath = path18.resolve(
|
|
5880
7108
|
subsystemsRoot,
|
|
5881
7109
|
"bridge",
|
|
5882
7110
|
"generated",
|
|
5883
7111
|
".gitkeep"
|
|
5884
7112
|
);
|
|
5885
7113
|
return {
|
|
5886
|
-
appName:
|
|
7114
|
+
appName: path18.basename(cwd),
|
|
5887
7115
|
multiTenant: normaliseMultiTenant4(bridgeBlock.multi_tenant),
|
|
5888
7116
|
configPath,
|
|
5889
7117
|
generatedKeepPath
|
|
@@ -5906,19 +7134,19 @@ function localsToHygenArgs4(locals) {
|
|
|
5906
7134
|
}
|
|
5907
7135
|
|
|
5908
7136
|
// src/cli/shared/observability-scaffold-locals.ts
|
|
5909
|
-
import
|
|
7137
|
+
import path19 from "path";
|
|
5910
7138
|
var FALLBACK_BACKEND_SRC2 = "src";
|
|
5911
7139
|
function resolveObservabilityScaffoldLocals(input) {
|
|
5912
7140
|
const { cwd, config } = input;
|
|
5913
7141
|
void input.fileExists;
|
|
5914
7142
|
const backendSrc = typeof config?.paths?.backend_src === "string" && config.paths.backend_src.length > 0 ? config.paths.backend_src : FALLBACK_BACKEND_SRC2;
|
|
5915
|
-
const appModulePath =
|
|
5916
|
-
const configPath =
|
|
7143
|
+
const appModulePath = path19.resolve(cwd, backendSrc, "app.module.ts");
|
|
7144
|
+
const configPath = path19.resolve(cwd, "codegen.config.yaml");
|
|
5917
7145
|
const obsBlock = config?.observability ?? {};
|
|
5918
7146
|
const reporters = obsBlock.reporters ?? {};
|
|
5919
7147
|
const bridgeMetrics = reporters.bridgeMetrics ?? {};
|
|
5920
7148
|
return {
|
|
5921
|
-
appName:
|
|
7149
|
+
appName: path19.basename(cwd),
|
|
5922
7150
|
appModulePath,
|
|
5923
7151
|
configPath,
|
|
5924
7152
|
bridgeMetricsEnabled: bridgeMetrics.enabled === true
|
|
@@ -5939,7 +7167,7 @@ function localsToHygenArgs5(locals) {
|
|
|
5939
7167
|
|
|
5940
7168
|
// src/cli/shared/auth-scaffold-locals.ts
|
|
5941
7169
|
import crypto from "crypto";
|
|
5942
|
-
import
|
|
7170
|
+
import path20 from "path";
|
|
5943
7171
|
var FALLBACK_BACKEND_SRC3 = "src";
|
|
5944
7172
|
var DEFAULT_REDIRECT_URI_BASE = "http://localhost:3000";
|
|
5945
7173
|
function resolveAuthScaffoldLocals(input) {
|
|
@@ -5951,15 +7179,15 @@ function resolveAuthScaffoldLocals(input) {
|
|
|
5951
7179
|
const redirectUriBase = typeof redirectRaw === "string" && redirectRaw.length > 0 ? redirectRaw : DEFAULT_REDIRECT_URI_BASE;
|
|
5952
7180
|
const tokenEncryptionKey = crypto.randomBytes(32).toString("base64");
|
|
5953
7181
|
return {
|
|
5954
|
-
appName:
|
|
5955
|
-
configPath:
|
|
5956
|
-
schemaPath:
|
|
7182
|
+
appName: path20.basename(cwd),
|
|
7183
|
+
configPath: path20.resolve(cwd, "codegen.config.yaml"),
|
|
7184
|
+
schemaPath: path20.resolve(
|
|
5957
7185
|
subsystemsRoot,
|
|
5958
7186
|
"auth",
|
|
5959
7187
|
"auth-oauth-state.schema.ts"
|
|
5960
7188
|
),
|
|
5961
|
-
appModulePath:
|
|
5962
|
-
envConfigPath:
|
|
7189
|
+
appModulePath: path20.resolve(cwd, backendSrc, "app.module.ts"),
|
|
7190
|
+
envConfigPath: path20.resolve(cwd, ".env.config"),
|
|
5963
7191
|
redirectUriBase,
|
|
5964
7192
|
tokenEncryptionKey
|
|
5965
7193
|
};
|
|
@@ -5984,7 +7212,7 @@ function localsToHygenArgs6(locals) {
|
|
|
5984
7212
|
}
|
|
5985
7213
|
|
|
5986
7214
|
// src/cli/shared/auth-integrations-scaffold-locals.ts
|
|
5987
|
-
import
|
|
7215
|
+
import path21 from "path";
|
|
5988
7216
|
var FALLBACK_BACKEND_SRC4 = "src";
|
|
5989
7217
|
var DEFAULT_MODULES_DIR = "modules";
|
|
5990
7218
|
var DEFAULT_DEFINITIONS_DIR = "definitions/entities";
|
|
@@ -5993,17 +7221,17 @@ function resolveAuthIntegrationsScaffoldLocals(input) {
|
|
|
5993
7221
|
const backendSrc = typeof config?.paths?.backend_src === "string" && config.paths.backend_src.length > 0 ? config.paths.backend_src : FALLBACK_BACKEND_SRC4;
|
|
5994
7222
|
const pathsAny = config?.paths;
|
|
5995
7223
|
const modulesConfigured = pathsAny?.modules_dir;
|
|
5996
|
-
const vendorRoot = typeof modulesConfigured === "string" && modulesConfigured.length > 0 ?
|
|
7224
|
+
const vendorRoot = typeof modulesConfigured === "string" && modulesConfigured.length > 0 ? path21.resolve(cwd, modulesConfigured) : path21.resolve(cwd, backendSrc, DEFAULT_MODULES_DIR);
|
|
5997
7225
|
const entitiesConfigured = typeof pathsAny?.entities === "string" && pathsAny.entities.length > 0 ? pathsAny.entities : typeof pathsAny?.entities_dir === "string" && pathsAny.entities_dir.length > 0 ? pathsAny.entities_dir : null;
|
|
5998
|
-
const definitionsPath = entitiesConfigured !== null ?
|
|
5999
|
-
const appModulePath =
|
|
7226
|
+
const definitionsPath = entitiesConfigured !== null ? path21.resolve(cwd, entitiesConfigured, "connection.yaml") : path21.resolve(cwd, DEFAULT_DEFINITIONS_DIR, "connection.yaml");
|
|
7227
|
+
const appModulePath = path21.resolve(cwd, backendSrc, "app.module.ts");
|
|
6000
7228
|
let authModuleRegistered = false;
|
|
6001
7229
|
const appModuleSource = input.readFile(appModulePath);
|
|
6002
7230
|
if (appModuleSource && appModuleSource.includes("AuthModule.forRoot")) {
|
|
6003
7231
|
authModuleRegistered = true;
|
|
6004
7232
|
}
|
|
6005
7233
|
return {
|
|
6006
|
-
appName:
|
|
7234
|
+
appName: path21.basename(cwd),
|
|
6007
7235
|
appModulePath,
|
|
6008
7236
|
vendorRoot,
|
|
6009
7237
|
definitionsPath,
|
|
@@ -6021,7 +7249,7 @@ function localsToHygenArgs7(locals) {
|
|
|
6021
7249
|
|
|
6022
7250
|
// src/cli/shared/runtime-copier.ts
|
|
6023
7251
|
import fs11 from "fs";
|
|
6024
|
-
import
|
|
7252
|
+
import path22 from "path";
|
|
6025
7253
|
function readIfExists(p) {
|
|
6026
7254
|
try {
|
|
6027
7255
|
return fs11.readFileSync(p, "utf-8");
|
|
@@ -6039,8 +7267,8 @@ function extractRelativeImports(source) {
|
|
|
6039
7267
|
return out;
|
|
6040
7268
|
}
|
|
6041
7269
|
function resolveSourceImport(sourceFile, specifier) {
|
|
6042
|
-
const base =
|
|
6043
|
-
const candidates = [base + ".ts", base + ".tsx",
|
|
7270
|
+
const base = path22.resolve(path22.dirname(sourceFile), specifier);
|
|
7271
|
+
const candidates = [base + ".ts", base + ".tsx", path22.join(base, "index.ts")];
|
|
6044
7272
|
for (const c of candidates) {
|
|
6045
7273
|
if (fs11.existsSync(c)) return c;
|
|
6046
7274
|
}
|
|
@@ -6051,8 +7279,8 @@ async function copyRuntime(opts) {
|
|
|
6051
7279
|
if (!fs11.existsSync(sourceDir) || !fs11.statSync(sourceDir).isDirectory()) {
|
|
6052
7280
|
throw new Error(`runtime source directory not found: ${sourceDir}`);
|
|
6053
7281
|
}
|
|
6054
|
-
const runtimeRoot4 = opts.runtimeRoot ?
|
|
6055
|
-
const depsTargetRoot = opts.depsTargetRoot ??
|
|
7282
|
+
const runtimeRoot4 = opts.runtimeRoot ? path22.resolve(opts.runtimeRoot) : path22.resolve(sourceDir, "..", "..");
|
|
7283
|
+
const depsTargetRoot = opts.depsTargetRoot ?? path22.resolve(targetDir, "..");
|
|
6056
7284
|
const result = {
|
|
6057
7285
|
written: [],
|
|
6058
7286
|
updated: [],
|
|
@@ -6063,7 +7291,7 @@ async function copyRuntime(opts) {
|
|
|
6063
7291
|
const queue = [];
|
|
6064
7292
|
function walk(dir) {
|
|
6065
7293
|
for (const entry of fs11.readdirSync(dir)) {
|
|
6066
|
-
const src =
|
|
7294
|
+
const src = path22.join(dir, entry);
|
|
6067
7295
|
const stat = fs11.statSync(src);
|
|
6068
7296
|
if (stat.isDirectory()) {
|
|
6069
7297
|
if (entry === "generated") continue;
|
|
@@ -6072,9 +7300,9 @@ async function copyRuntime(opts) {
|
|
|
6072
7300
|
}
|
|
6073
7301
|
if (!stat.isFile()) continue;
|
|
6074
7302
|
if (!entry.endsWith(".ts") && !entry.endsWith(".tsx")) continue;
|
|
6075
|
-
const rel2 =
|
|
7303
|
+
const rel2 = path22.relative(sourceDir, src);
|
|
6076
7304
|
if (filter && !filter(rel2) && !filter(entry)) continue;
|
|
6077
|
-
queue.push({ src, dest:
|
|
7305
|
+
queue.push({ src, dest: path22.join(targetDir, rel2), isDep: false });
|
|
6078
7306
|
}
|
|
6079
7307
|
}
|
|
6080
7308
|
walk(sourceDir);
|
|
@@ -6095,18 +7323,18 @@ async function copyRuntime(opts) {
|
|
|
6095
7323
|
else result.unchanged.push(next.dest);
|
|
6096
7324
|
if (next.isDep) result.dependenciesCopied.push(next.dest);
|
|
6097
7325
|
if (!dryRun && status !== "unchanged") {
|
|
6098
|
-
fs11.mkdirSync(
|
|
7326
|
+
fs11.mkdirSync(path22.dirname(next.dest), { recursive: true });
|
|
6099
7327
|
fs11.writeFileSync(next.dest, content);
|
|
6100
7328
|
}
|
|
6101
7329
|
if (resolveDeps) {
|
|
6102
7330
|
for (const spec of extractRelativeImports(content)) {
|
|
6103
7331
|
const resolvedSrc = resolveSourceImport(next.src, spec);
|
|
6104
7332
|
if (!resolvedSrc) continue;
|
|
6105
|
-
const relToRuntime =
|
|
6106
|
-
if (relToRuntime.startsWith("..") ||
|
|
6107
|
-
const relToSource =
|
|
6108
|
-
if (!relToSource.startsWith("..") && !
|
|
6109
|
-
const depDest =
|
|
7333
|
+
const relToRuntime = path22.relative(runtimeRoot4, resolvedSrc);
|
|
7334
|
+
if (relToRuntime.startsWith("..") || path22.isAbsolute(relToRuntime)) continue;
|
|
7335
|
+
const relToSource = path22.relative(sourceDir, resolvedSrc);
|
|
7336
|
+
if (!relToSource.startsWith("..") && !path22.isAbsolute(relToSource)) continue;
|
|
7337
|
+
const depDest = path22.join(depsTargetRoot, relToRuntime);
|
|
6110
7338
|
queue.push({ src: resolvedSrc, dest: depDest, isDep: true });
|
|
6111
7339
|
}
|
|
6112
7340
|
}
|
|
@@ -6116,7 +7344,7 @@ async function copyRuntime(opts) {
|
|
|
6116
7344
|
|
|
6117
7345
|
// src/cli/shared/subsystems-install-config.ts
|
|
6118
7346
|
import fs12 from "fs";
|
|
6119
|
-
import
|
|
7347
|
+
import path23 from "path";
|
|
6120
7348
|
import yaml2 from "yaml";
|
|
6121
7349
|
function readInstallList(config) {
|
|
6122
7350
|
const raw = config?.subsystems?.install;
|
|
@@ -6125,7 +7353,7 @@ function readInstallList(config) {
|
|
|
6125
7353
|
}
|
|
6126
7354
|
function ensureSubsystemInstalled(configPath, name) {
|
|
6127
7355
|
if (!fs12.existsSync(configPath)) {
|
|
6128
|
-
fs12.mkdirSync(
|
|
7356
|
+
fs12.mkdirSync(path23.dirname(configPath), { recursive: true });
|
|
6129
7357
|
fs12.writeFileSync(
|
|
6130
7358
|
configPath,
|
|
6131
7359
|
`subsystems:
|
|
@@ -6176,13 +7404,13 @@ ${indent}- ${name}${after}`;
|
|
|
6176
7404
|
|
|
6177
7405
|
// src/cli/commands/subsystem.ts
|
|
6178
7406
|
function runtimeRoot() {
|
|
6179
|
-
const pkgRoot =
|
|
6180
|
-
const topLevel =
|
|
7407
|
+
const pkgRoot = path24.resolve(import.meta.dirname, "..", "..", "..");
|
|
7408
|
+
const topLevel = path24.join(pkgRoot, "runtime");
|
|
6181
7409
|
if (fs13.existsSync(topLevel)) return topLevel;
|
|
6182
|
-
return
|
|
7410
|
+
return path24.join(pkgRoot, "dist", "runtime");
|
|
6183
7411
|
}
|
|
6184
7412
|
function subsystemSource(name) {
|
|
6185
|
-
return
|
|
7413
|
+
return path24.join(runtimeRoot(), "subsystems", name);
|
|
6186
7414
|
}
|
|
6187
7415
|
function describeSubsystem(name) {
|
|
6188
7416
|
return SUBSYSTEMS.find((s) => s.name === name) ?? null;
|
|
@@ -6217,7 +7445,7 @@ async function summary2(ctx) {
|
|
|
6217
7445
|
}
|
|
6218
7446
|
body.push(theme.muted("Installed:"));
|
|
6219
7447
|
for (const i of installed) {
|
|
6220
|
-
const rel2 =
|
|
7448
|
+
const rel2 = path24.relative(ctx.cwd, i.path) || i.path;
|
|
6221
7449
|
body.push(
|
|
6222
7450
|
` ${theme.success(icons.check)} ${i.name.padEnd(10)} ${theme.muted(
|
|
6223
7451
|
`${i.backend} backend`
|
|
@@ -6360,14 +7588,14 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6360
7588
|
return 0;
|
|
6361
7589
|
}
|
|
6362
7590
|
const targetRoot = resolveSubsystemsRoot(ctx, this.target);
|
|
6363
|
-
const subsystemTarget =
|
|
7591
|
+
const subsystemTarget = path24.join(targetRoot, desc.name);
|
|
6364
7592
|
const source = subsystemSource(desc.name);
|
|
6365
7593
|
if (!fs13.existsSync(source)) {
|
|
6366
7594
|
printError(`Runtime subsystem source missing: ${source}`);
|
|
6367
7595
|
return 1;
|
|
6368
7596
|
}
|
|
6369
7597
|
if (!this.force) {
|
|
6370
|
-
const gitCheck = checkGitSafety([
|
|
7598
|
+
const gitCheck = checkGitSafety([path24.relative(ctx.cwd, subsystemTarget) || subsystemTarget], ctx.cwd);
|
|
6371
7599
|
if (gitCheck.inRepo && !gitCheck.clean) {
|
|
6372
7600
|
printWarning(
|
|
6373
7601
|
`Uncommitted changes under ${subsystemTarget}. Pass --force to overwrite.`
|
|
@@ -6376,7 +7604,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6376
7604
|
}
|
|
6377
7605
|
}
|
|
6378
7606
|
if (!isJsonMode()) {
|
|
6379
|
-
printInfo(`target = ${
|
|
7607
|
+
printInfo(`target = ${path24.relative(ctx.cwd, subsystemTarget) || subsystemTarget}`);
|
|
6380
7608
|
printInfo(`backend = ${backend}`);
|
|
6381
7609
|
}
|
|
6382
7610
|
const result = await copyRuntime({
|
|
@@ -6385,7 +7613,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6385
7613
|
filter: backendFileFilter(backend, desc.name),
|
|
6386
7614
|
resolveDeps: true,
|
|
6387
7615
|
runtimeRoot: runtimeRoot(),
|
|
6388
|
-
depsTargetRoot:
|
|
7616
|
+
depsTargetRoot: path24.resolve(targetRoot, ".."),
|
|
6389
7617
|
dryRun: this.dryRun
|
|
6390
7618
|
});
|
|
6391
7619
|
const jobsScaffold = desc.name === "jobs" ? runJobsScaffold(ctx.cwd, ctx.config, {
|
|
@@ -6480,14 +7708,14 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6480
7708
|
if (this.dryRun) {
|
|
6481
7709
|
printInfo(`Dry run \u2014 ${result.planned.length} files would be written`);
|
|
6482
7710
|
for (const p of result.planned) {
|
|
6483
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7711
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6484
7712
|
}
|
|
6485
7713
|
if (jobsScaffold?.planned?.length) {
|
|
6486
7714
|
printInfo(
|
|
6487
7715
|
`Jobs scaffold \u2014 ${jobsScaffold.planned.length} template targets`
|
|
6488
7716
|
);
|
|
6489
7717
|
for (const p of jobsScaffold.planned) {
|
|
6490
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7718
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6491
7719
|
}
|
|
6492
7720
|
}
|
|
6493
7721
|
if (eventsScaffold?.planned?.length) {
|
|
@@ -6495,7 +7723,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6495
7723
|
`Events scaffold \u2014 ${eventsScaffold.planned.length} template targets`
|
|
6496
7724
|
);
|
|
6497
7725
|
for (const p of eventsScaffold.planned) {
|
|
6498
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7726
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6499
7727
|
}
|
|
6500
7728
|
}
|
|
6501
7729
|
if (integrationScaffold?.planned?.length) {
|
|
@@ -6503,7 +7731,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6503
7731
|
`Integration scaffold \u2014 ${integrationScaffold.planned.length} template targets`
|
|
6504
7732
|
);
|
|
6505
7733
|
for (const p of integrationScaffold.planned) {
|
|
6506
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7734
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6507
7735
|
}
|
|
6508
7736
|
}
|
|
6509
7737
|
if (bridgeScaffold?.planned?.length) {
|
|
@@ -6511,7 +7739,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6511
7739
|
`Bridge scaffold \u2014 ${bridgeScaffold.planned.length} template targets`
|
|
6512
7740
|
);
|
|
6513
7741
|
for (const p of bridgeScaffold.planned) {
|
|
6514
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7742
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6515
7743
|
}
|
|
6516
7744
|
}
|
|
6517
7745
|
if (observabilityScaffold?.planned?.length) {
|
|
@@ -6519,7 +7747,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6519
7747
|
`Observability scaffold \u2014 ${observabilityScaffold.planned.length} template targets`
|
|
6520
7748
|
);
|
|
6521
7749
|
for (const p of observabilityScaffold.planned) {
|
|
6522
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7750
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6523
7751
|
}
|
|
6524
7752
|
}
|
|
6525
7753
|
if (authScaffold?.planned?.length) {
|
|
@@ -6527,7 +7755,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6527
7755
|
`Auth scaffold \u2014 ${authScaffold.planned.length} template targets`
|
|
6528
7756
|
);
|
|
6529
7757
|
for (const p of authScaffold.planned) {
|
|
6530
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7758
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6531
7759
|
}
|
|
6532
7760
|
}
|
|
6533
7761
|
return 0;
|
|
@@ -6657,7 +7885,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6657
7885
|
* `--force-config` and always regenerates the barrels).
|
|
6658
7886
|
*/
|
|
6659
7887
|
async executePackageMode(ctx, desc, backend) {
|
|
6660
|
-
const configPath =
|
|
7888
|
+
const configPath = path24.join(ctx.cwd, "codegen.config.yaml");
|
|
6661
7889
|
const installed = configuredSubsystemNames(
|
|
6662
7890
|
ctx.config
|
|
6663
7891
|
);
|
|
@@ -6784,7 +8012,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6784
8012
|
* semantics as jobs/events/integration/bridge.
|
|
6785
8013
|
*/
|
|
6786
8014
|
async executeOpenApiConfig(ctx) {
|
|
6787
|
-
const configPath =
|
|
8015
|
+
const configPath = path24.join(ctx.cwd, "codegen.config.yaml");
|
|
6788
8016
|
const outcome = planConfigBlockAction(configPath, "openapi", this.forceConfig);
|
|
6789
8017
|
if (outcome === "parse-error") {
|
|
6790
8018
|
printError(
|
|
@@ -6803,7 +8031,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6803
8031
|
});
|
|
6804
8032
|
} else {
|
|
6805
8033
|
printInfo(`Dry run \u2014 openapi config block would be ${outcome}`);
|
|
6806
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
8034
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, configPath) || configPath}`);
|
|
6807
8035
|
}
|
|
6808
8036
|
return 0;
|
|
6809
8037
|
}
|
|
@@ -6898,7 +8126,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6898
8126
|
);
|
|
6899
8127
|
for (const p of scaffold.planned) {
|
|
6900
8128
|
console.log(
|
|
6901
|
-
` ${theme.muted(icons.arrow)} ${
|
|
8129
|
+
` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`
|
|
6902
8130
|
);
|
|
6903
8131
|
}
|
|
6904
8132
|
return 0;
|
|
@@ -7281,7 +8509,7 @@ function runAuthScaffold(cwd, config, opts) {
|
|
|
7281
8509
|
return { ok: true, planned, configBlockOutcome };
|
|
7282
8510
|
}
|
|
7283
8511
|
if (!fs13.existsSync(locals.envConfigPath)) {
|
|
7284
|
-
fs13.mkdirSync(
|
|
8512
|
+
fs13.mkdirSync(path24.dirname(locals.envConfigPath), { recursive: true });
|
|
7285
8513
|
fs13.writeFileSync(locals.envConfigPath, "", "utf-8");
|
|
7286
8514
|
}
|
|
7287
8515
|
const result = invokeHygen({
|
|
@@ -7318,10 +8546,10 @@ function runAuthScaffold(cwd, config, opts) {
|
|
|
7318
8546
|
return { ok: true, planned, configBlockOutcome };
|
|
7319
8547
|
}
|
|
7320
8548
|
function authIntegrationsExamplesRoot() {
|
|
7321
|
-
const pkgRoot =
|
|
7322
|
-
const topLevel =
|
|
8549
|
+
const pkgRoot = path24.resolve(import.meta.dirname, "..", "..", "..");
|
|
8550
|
+
const topLevel = path24.join(pkgRoot, "examples", "auth-integrations");
|
|
7323
8551
|
if (fs13.existsSync(topLevel)) return topLevel;
|
|
7324
|
-
return
|
|
8552
|
+
return path24.join(pkgRoot, "dist", "examples", "auth-integrations");
|
|
7325
8553
|
}
|
|
7326
8554
|
function copyTreeIdempotent(srcDir, destDir, force, transform) {
|
|
7327
8555
|
const written = [];
|
|
@@ -7329,8 +8557,8 @@ function copyTreeIdempotent(srcDir, destDir, force, transform) {
|
|
|
7329
8557
|
const walk = (src, dest) => {
|
|
7330
8558
|
const entries = fs13.readdirSync(src, { withFileTypes: true });
|
|
7331
8559
|
for (const entry of entries) {
|
|
7332
|
-
const srcPath =
|
|
7333
|
-
const destPath =
|
|
8560
|
+
const srcPath = path24.join(src, entry.name);
|
|
8561
|
+
const destPath = path24.join(dest, entry.name);
|
|
7334
8562
|
if (entry.isDirectory()) {
|
|
7335
8563
|
fs13.mkdirSync(destPath, { recursive: true });
|
|
7336
8564
|
walk(srcPath, destPath);
|
|
@@ -7341,7 +8569,7 @@ function copyTreeIdempotent(srcDir, destDir, force, transform) {
|
|
|
7341
8569
|
skipped.push(destPath);
|
|
7342
8570
|
continue;
|
|
7343
8571
|
}
|
|
7344
|
-
fs13.mkdirSync(
|
|
8572
|
+
fs13.mkdirSync(path24.dirname(destPath), { recursive: true });
|
|
7345
8573
|
const isTextSource = transform && (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx"));
|
|
7346
8574
|
if (isTextSource && transform) {
|
|
7347
8575
|
const raw = fs13.readFileSync(srcPath, "utf-8");
|
|
@@ -7359,16 +8587,16 @@ function copyTreeIdempotent(srcDir, destDir, force, transform) {
|
|
|
7359
8587
|
}
|
|
7360
8588
|
var AUTH_BARE_IMPORT_RE = /(['"])@pattern-stack\/codegen\/runtime\/subsystems\/auth\1/g;
|
|
7361
8589
|
function buildAuthImportRewriter(subsystemsRoot) {
|
|
7362
|
-
const authRoot =
|
|
8590
|
+
const authRoot = path24.join(subsystemsRoot, "auth");
|
|
7363
8591
|
return (content, destPath) => {
|
|
7364
8592
|
if (!AUTH_BARE_IMPORT_RE.test(content)) {
|
|
7365
8593
|
AUTH_BARE_IMPORT_RE.lastIndex = 0;
|
|
7366
8594
|
return content;
|
|
7367
8595
|
}
|
|
7368
8596
|
AUTH_BARE_IMPORT_RE.lastIndex = 0;
|
|
7369
|
-
let rel2 =
|
|
8597
|
+
let rel2 = path24.relative(path24.dirname(destPath), authRoot);
|
|
7370
8598
|
if (!rel2.startsWith(".")) rel2 = `./${rel2}`;
|
|
7371
|
-
const relPosix = rel2.split(
|
|
8599
|
+
const relPosix = rel2.split(path24.sep).join("/");
|
|
7372
8600
|
return content.replace(
|
|
7373
8601
|
AUTH_BARE_IMPORT_RE,
|
|
7374
8602
|
(_match, quote) => `${quote}${relPosix}${quote}`
|
|
@@ -7390,9 +8618,9 @@ function runAuthIntegrationsScaffold(cwd, config, opts) {
|
|
|
7390
8618
|
error: `auth-integrations starter source missing: ${examplesRoot}`
|
|
7391
8619
|
};
|
|
7392
8620
|
}
|
|
7393
|
-
const adaptersSrc =
|
|
7394
|
-
const adaptersDest =
|
|
7395
|
-
const connectionYamlSrc =
|
|
8621
|
+
const adaptersSrc = path24.join(examplesRoot, "runtime", "connections");
|
|
8622
|
+
const adaptersDest = path24.join(locals.vendorRoot, "connections");
|
|
8623
|
+
const connectionYamlSrc = path24.join(
|
|
7396
8624
|
examplesRoot,
|
|
7397
8625
|
"definitions",
|
|
7398
8626
|
"entities",
|
|
@@ -7424,7 +8652,7 @@ function runAuthIntegrationsScaffold(cwd, config, opts) {
|
|
|
7424
8652
|
if (fs13.existsSync(connectionYamlDest) && !opts.force) {
|
|
7425
8653
|
yamlSkipped = true;
|
|
7426
8654
|
} else if (fs13.existsSync(connectionYamlSrc)) {
|
|
7427
|
-
fs13.mkdirSync(
|
|
8655
|
+
fs13.mkdirSync(path24.dirname(connectionYamlDest), { recursive: true });
|
|
7428
8656
|
fs13.copyFileSync(connectionYamlSrc, connectionYamlDest);
|
|
7429
8657
|
yamlWritten = true;
|
|
7430
8658
|
}
|
|
@@ -7490,7 +8718,7 @@ var SubsystemListCommand = class extends Command3 {
|
|
|
7490
8718
|
name: s.name,
|
|
7491
8719
|
status: inst ? inst.status : "available",
|
|
7492
8720
|
backend: inst ? inst.backend : null,
|
|
7493
|
-
path: inst ?
|
|
8721
|
+
path: inst ? path24.relative(ctx.cwd, inst.path) || inst.path : null
|
|
7494
8722
|
};
|
|
7495
8723
|
});
|
|
7496
8724
|
if (isJsonMode()) {
|
|
@@ -7586,7 +8814,7 @@ var SubsystemRemoveCommand = class extends Command3 {
|
|
|
7586
8814
|
return 1;
|
|
7587
8815
|
}
|
|
7588
8816
|
if (!this.force) {
|
|
7589
|
-
const rel2 =
|
|
8817
|
+
const rel2 = path24.relative(ctx.cwd, subsystemDir) || subsystemDir;
|
|
7590
8818
|
const gitCheck = checkGitSafety([rel2], ctx.cwd);
|
|
7591
8819
|
if (gitCheck.inRepo && !gitCheck.clean) {
|
|
7592
8820
|
printWarning(
|
|
@@ -7622,7 +8850,7 @@ var SubsystemRemoveCommand = class extends Command3 {
|
|
|
7622
8850
|
return 0;
|
|
7623
8851
|
}
|
|
7624
8852
|
printSuccess(
|
|
7625
|
-
`${desc.name} subsystem removed (${
|
|
8853
|
+
`${desc.name} subsystem removed (${path24.relative(ctx.cwd, subsystemDir) || subsystemDir}).`
|
|
7626
8854
|
);
|
|
7627
8855
|
if (barrelRegenerated) {
|
|
7628
8856
|
printInfo("Regenerated <generated>/subsystems.ts barrel.");
|
|
@@ -7651,27 +8879,27 @@ var subsystem_default = subsystemNoun;
|
|
|
7651
8879
|
|
|
7652
8880
|
// src/cli/commands/project.ts
|
|
7653
8881
|
import fs19 from "fs";
|
|
7654
|
-
import
|
|
8882
|
+
import path30 from "path";
|
|
7655
8883
|
import readline from "readline";
|
|
7656
8884
|
import { Command as Command7, Option as Option7 } from "clipanion";
|
|
7657
8885
|
import { stringify as stringifyYaml2 } from "yaml";
|
|
7658
8886
|
|
|
7659
8887
|
// src/cli/shared/init-scaffold.ts
|
|
7660
8888
|
import fs14 from "fs";
|
|
7661
|
-
import
|
|
8889
|
+
import path25 from "path";
|
|
7662
8890
|
import { stringify as stringifyYaml } from "yaml";
|
|
7663
8891
|
function runtimeRoot2() {
|
|
7664
|
-
const pkgRoot =
|
|
7665
|
-
const topLevel =
|
|
8892
|
+
const pkgRoot = path25.resolve(import.meta.dirname, "..", "..", "..");
|
|
8893
|
+
const topLevel = path25.join(pkgRoot, "runtime");
|
|
7666
8894
|
if (fs14.existsSync(topLevel)) return topLevel;
|
|
7667
|
-
return
|
|
8895
|
+
return path25.join(pkgRoot, "dist", "runtime");
|
|
7668
8896
|
}
|
|
7669
8897
|
function resolveRuntimePath(cwd) {
|
|
7670
|
-
const shimDir =
|
|
7671
|
-
return
|
|
8898
|
+
const shimDir = path25.join(cwd, "src", "shared", "base-classes");
|
|
8899
|
+
return path25.relative(shimDir, runtimeRoot2());
|
|
7672
8900
|
}
|
|
7673
8901
|
function loadRuntimeFile(relPath) {
|
|
7674
|
-
return fs14.readFileSync(
|
|
8902
|
+
return fs14.readFileSync(path25.join(runtimeRoot2(), relPath), "utf-8");
|
|
7675
8903
|
}
|
|
7676
8904
|
var VENDORED_RUNTIME_FILES = [
|
|
7677
8905
|
// base-classes — consumer-facing inheritance targets
|
|
@@ -8087,8 +9315,38 @@ function mergeTsconfig(raw) {
|
|
|
8087
9315
|
unchanged: false
|
|
8088
9316
|
};
|
|
8089
9317
|
}
|
|
9318
|
+
function mergeFrontendDeps(raw) {
|
|
9319
|
+
let parsed;
|
|
9320
|
+
try {
|
|
9321
|
+
parsed = JSON.parse(raw);
|
|
9322
|
+
} catch (err) {
|
|
9323
|
+
return {
|
|
9324
|
+
content: raw,
|
|
9325
|
+
added: [],
|
|
9326
|
+
unchanged: true,
|
|
9327
|
+
parseError: err instanceof Error ? err.message : String(err)
|
|
9328
|
+
};
|
|
9329
|
+
}
|
|
9330
|
+
const deps = parsed.dependencies ?? {};
|
|
9331
|
+
const added = [];
|
|
9332
|
+
for (const [pkg, range] of Object.entries(FRONTEND_EMITTED_DEPS)) {
|
|
9333
|
+
if (!(pkg in deps)) {
|
|
9334
|
+
deps[pkg] = range;
|
|
9335
|
+
added.push(pkg);
|
|
9336
|
+
}
|
|
9337
|
+
}
|
|
9338
|
+
if (added.length === 0) {
|
|
9339
|
+
return { content: raw, added: [], unchanged: true };
|
|
9340
|
+
}
|
|
9341
|
+
parsed.dependencies = deps;
|
|
9342
|
+
return {
|
|
9343
|
+
content: JSON.stringify(parsed, null, 2) + "\n",
|
|
9344
|
+
added,
|
|
9345
|
+
unchanged: false
|
|
9346
|
+
};
|
|
9347
|
+
}
|
|
8090
9348
|
function relOf(cwd, abs) {
|
|
8091
|
-
return
|
|
9349
|
+
return path25.relative(cwd, abs) || abs;
|
|
8092
9350
|
}
|
|
8093
9351
|
function fileEntry(cwd, absPath, content, opts) {
|
|
8094
9352
|
const exists = fs14.existsSync(absPath);
|
|
@@ -8139,7 +9397,7 @@ async function buildInitPlan(ctx, options) {
|
|
|
8139
9397
|
const runtimePath = options.runtimePath ?? resolveRuntimePath(cwd);
|
|
8140
9398
|
const entries = [];
|
|
8141
9399
|
{
|
|
8142
|
-
const configPath =
|
|
9400
|
+
const configPath = path25.join(cwd, "codegen.config.yaml");
|
|
8143
9401
|
const config = {
|
|
8144
9402
|
// Runtime mode (ADR-037). `package` (default) imports the runtime from
|
|
8145
9403
|
// `@pattern-stack/codegen/*`; `vendored` imports it via `@shared/*` and
|
|
@@ -8174,7 +9432,7 @@ async function buildInitPlan(ctx, options) {
|
|
|
8174
9432
|
entries.push(fileEntry(cwd, configPath, content, { force }));
|
|
8175
9433
|
}
|
|
8176
9434
|
{
|
|
8177
|
-
const tsconfigPath =
|
|
9435
|
+
const tsconfigPath = path25.join(cwd, "tsconfig.json");
|
|
8178
9436
|
if (fs14.existsSync(tsconfigPath)) {
|
|
8179
9437
|
const raw = fs14.readFileSync(tsconfigPath, "utf-8");
|
|
8180
9438
|
const merged = mergeTsconfig(raw);
|
|
@@ -8221,7 +9479,7 @@ async function buildInitPlan(ctx, options) {
|
|
|
8221
9479
|
entries.push(
|
|
8222
9480
|
fileEntry(
|
|
8223
9481
|
cwd,
|
|
8224
|
-
|
|
9482
|
+
path25.join(cwd, "src", "shared", "database", "database.module.ts"),
|
|
8225
9483
|
databaseModuleContent(runtimeMode),
|
|
8226
9484
|
{ force }
|
|
8227
9485
|
)
|
|
@@ -8229,14 +9487,14 @@ async function buildInitPlan(ctx, options) {
|
|
|
8229
9487
|
if (runtimeMode === "vendored") {
|
|
8230
9488
|
for (const v of VENDORED_RUNTIME_FILES) {
|
|
8231
9489
|
entries.push(
|
|
8232
|
-
fileEntry(cwd,
|
|
9490
|
+
fileEntry(cwd, path25.join(cwd, v.target), loadRuntimeFile(v.runtime), { force })
|
|
8233
9491
|
);
|
|
8234
9492
|
}
|
|
8235
9493
|
}
|
|
8236
9494
|
entries.push(
|
|
8237
9495
|
fileEntry(
|
|
8238
9496
|
cwd,
|
|
8239
|
-
|
|
9497
|
+
path25.join(cwd, "src", "generated", "modules.ts"),
|
|
8240
9498
|
emptyModulesBarrel(),
|
|
8241
9499
|
{ force }
|
|
8242
9500
|
)
|
|
@@ -8244,13 +9502,13 @@ async function buildInitPlan(ctx, options) {
|
|
|
8244
9502
|
entries.push(
|
|
8245
9503
|
fileEntry(
|
|
8246
9504
|
cwd,
|
|
8247
|
-
|
|
9505
|
+
path25.join(cwd, "src", "generated", "schema.ts"),
|
|
8248
9506
|
emptySchemaBarrel(),
|
|
8249
9507
|
{ force }
|
|
8250
9508
|
)
|
|
8251
9509
|
);
|
|
8252
9510
|
{
|
|
8253
|
-
const appModulePath =
|
|
9511
|
+
const appModulePath = path25.join(cwd, "src", "app.module.ts");
|
|
8254
9512
|
if (!fs14.existsSync(appModulePath)) {
|
|
8255
9513
|
entries.push({
|
|
8256
9514
|
path: appModulePath,
|
|
@@ -8268,7 +9526,7 @@ async function buildInitPlan(ctx, options) {
|
|
|
8268
9526
|
}
|
|
8269
9527
|
}
|
|
8270
9528
|
{
|
|
8271
|
-
const mainPath =
|
|
9529
|
+
const mainPath = path25.join(cwd, "src", "main.ts");
|
|
8272
9530
|
if (!fs14.existsSync(mainPath)) {
|
|
8273
9531
|
entries.push({
|
|
8274
9532
|
path: mainPath,
|
|
@@ -8286,7 +9544,7 @@ async function buildInitPlan(ctx, options) {
|
|
|
8286
9544
|
}
|
|
8287
9545
|
}
|
|
8288
9546
|
{
|
|
8289
|
-
const schemaPath =
|
|
9547
|
+
const schemaPath = path25.join(cwd, "src", "schema.ts");
|
|
8290
9548
|
if (!fs14.existsSync(schemaPath)) {
|
|
8291
9549
|
entries.push({
|
|
8292
9550
|
path: schemaPath,
|
|
@@ -8303,12 +9561,12 @@ async function buildInitPlan(ctx, options) {
|
|
|
8303
9561
|
});
|
|
8304
9562
|
}
|
|
8305
9563
|
}
|
|
8306
|
-
entries.push(dirEntry(cwd,
|
|
9564
|
+
entries.push(dirEntry(cwd, path25.join(cwd, "entities")));
|
|
8307
9565
|
{
|
|
8308
|
-
const entitiesDir =
|
|
8309
|
-
const examplePath =
|
|
9566
|
+
const entitiesDir = path25.join(cwd, "entities");
|
|
9567
|
+
const examplePath = path25.join(entitiesDir, "example.yaml");
|
|
8310
9568
|
const hasOtherYamls = fs14.existsSync(entitiesDir) && findYamlFiles(entitiesDir).some(
|
|
8311
|
-
(f) =>
|
|
9569
|
+
(f) => path25.basename(f) !== "example.yaml"
|
|
8312
9570
|
);
|
|
8313
9571
|
if (fs14.existsSync(examplePath)) {
|
|
8314
9572
|
entries.push({
|
|
@@ -8333,6 +9591,46 @@ async function buildInitPlan(ctx, options) {
|
|
|
8333
9591
|
});
|
|
8334
9592
|
}
|
|
8335
9593
|
}
|
|
9594
|
+
if (frontend) {
|
|
9595
|
+
const frontendSrc = ctx.config?.paths?.frontend_src ?? "apps/frontend/src";
|
|
9596
|
+
const frontendRoot = path25.dirname(path25.join(cwd, frontendSrc));
|
|
9597
|
+
const pkgPath = path25.join(frontendRoot, "package.json");
|
|
9598
|
+
const depsList = Object.entries(FRONTEND_EMITTED_DEPS).map(([p, r]) => `${p}@${r}`).join(", ");
|
|
9599
|
+
if (fs14.existsSync(pkgPath)) {
|
|
9600
|
+
const raw = fs14.readFileSync(pkgPath, "utf-8");
|
|
9601
|
+
const merged = mergeFrontendDeps(raw);
|
|
9602
|
+
if (merged.parseError) {
|
|
9603
|
+
entries.push({
|
|
9604
|
+
path: pkgPath,
|
|
9605
|
+
relPath: relOf(cwd, pkgPath),
|
|
9606
|
+
action: "skip",
|
|
9607
|
+
reason: `unable to parse (${merged.parseError}); add frontend deps manually: ${depsList}`
|
|
9608
|
+
});
|
|
9609
|
+
} else if (merged.unchanged) {
|
|
9610
|
+
entries.push({
|
|
9611
|
+
path: pkgPath,
|
|
9612
|
+
relPath: relOf(cwd, pkgPath),
|
|
9613
|
+
action: "skip",
|
|
9614
|
+
reason: "frontend deps already present"
|
|
9615
|
+
});
|
|
9616
|
+
} else {
|
|
9617
|
+
entries.push({
|
|
9618
|
+
path: pkgPath,
|
|
9619
|
+
relPath: relOf(cwd, pkgPath),
|
|
9620
|
+
action: "merge",
|
|
9621
|
+
content: merged.content,
|
|
9622
|
+
reason: `add frontend deps: ${merged.added.join(", ")}`
|
|
9623
|
+
});
|
|
9624
|
+
}
|
|
9625
|
+
} else {
|
|
9626
|
+
entries.push({
|
|
9627
|
+
path: pkgPath,
|
|
9628
|
+
relPath: relOf(cwd, pkgPath),
|
|
9629
|
+
action: "skip",
|
|
9630
|
+
reason: `frontend enabled but ${relOf(cwd, pkgPath)} not found \u2014 install: ${depsList}`
|
|
9631
|
+
});
|
|
9632
|
+
}
|
|
9633
|
+
}
|
|
8336
9634
|
return {
|
|
8337
9635
|
entries,
|
|
8338
9636
|
summary: {
|
|
@@ -8364,7 +9662,7 @@ function writePlan(plan) {
|
|
|
8364
9662
|
skipped.push(e);
|
|
8365
9663
|
continue;
|
|
8366
9664
|
}
|
|
8367
|
-
fs14.mkdirSync(
|
|
9665
|
+
fs14.mkdirSync(path25.dirname(e.path), { recursive: true });
|
|
8368
9666
|
fs14.writeFileSync(e.path, e.content, "utf-8");
|
|
8369
9667
|
if (e.action === "create") created.push(e);
|
|
8370
9668
|
else if (e.action === "merge") merged.push(e);
|
|
@@ -8375,7 +9673,7 @@ function writePlan(plan) {
|
|
|
8375
9673
|
|
|
8376
9674
|
// src/cli/commands/project-upgrade-openapi.ts
|
|
8377
9675
|
import fs15 from "fs";
|
|
8378
|
-
import
|
|
9676
|
+
import path26 from "path";
|
|
8379
9677
|
import { Command as Command4, Option as Option4 } from "clipanion";
|
|
8380
9678
|
import { Project, IndentationText, QuoteKind, NewLineKind } from "ts-morph";
|
|
8381
9679
|
|
|
@@ -8614,31 +9912,31 @@ var MAIN_SWAGGER_IMPORTS = [
|
|
|
8614
9912
|
"import { OPENAPI_REGISTRY, OpenApiRegistry } from './shared/openapi';"
|
|
8615
9913
|
];
|
|
8616
9914
|
function runtimeRoot3() {
|
|
8617
|
-
const pkgRoot =
|
|
8618
|
-
const topLevel =
|
|
9915
|
+
const pkgRoot = path26.resolve(import.meta.dirname, "..", "..", "..");
|
|
9916
|
+
const topLevel = path26.join(pkgRoot, "runtime");
|
|
8619
9917
|
if (fs15.existsSync(topLevel)) return topLevel;
|
|
8620
|
-
return
|
|
9918
|
+
return path26.join(pkgRoot, "dist", "runtime");
|
|
8621
9919
|
}
|
|
8622
9920
|
function loadRuntimeFile2(rel2) {
|
|
8623
|
-
return fs15.readFileSync(
|
|
9921
|
+
return fs15.readFileSync(path26.join(runtimeRoot3(), rel2), "utf-8");
|
|
8624
9922
|
}
|
|
8625
9923
|
function resolveProjectRoot(startDir) {
|
|
8626
|
-
let dir =
|
|
9924
|
+
let dir = path26.resolve(startDir);
|
|
8627
9925
|
for (let i = 0; i < 16; i++) {
|
|
8628
|
-
if (fs15.existsSync(
|
|
9926
|
+
if (fs15.existsSync(path26.join(dir, "codegen.config.yaml")) || fs15.existsSync(path26.join(dir, "package.json"))) {
|
|
8629
9927
|
return dir;
|
|
8630
9928
|
}
|
|
8631
|
-
const parent =
|
|
9929
|
+
const parent = path26.dirname(dir);
|
|
8632
9930
|
if (parent === dir) break;
|
|
8633
9931
|
dir = parent;
|
|
8634
9932
|
}
|
|
8635
|
-
return
|
|
9933
|
+
return path26.resolve(startDir);
|
|
8636
9934
|
}
|
|
8637
9935
|
async function runUpgradeOpenapi(opts) {
|
|
8638
9936
|
const { projectRoot, dryRun, force } = opts;
|
|
8639
9937
|
const changes = [];
|
|
8640
9938
|
for (const v of OPENAPI_VENDORED_FILES) {
|
|
8641
|
-
const target =
|
|
9939
|
+
const target = path26.join(projectRoot, v.target);
|
|
8642
9940
|
const exists = fs15.existsSync(target);
|
|
8643
9941
|
const newContent = loadRuntimeFile2(v.runtime);
|
|
8644
9942
|
if (exists && !force) {
|
|
@@ -8654,7 +9952,7 @@ async function runUpgradeOpenapi(opts) {
|
|
|
8654
9952
|
}
|
|
8655
9953
|
} else {
|
|
8656
9954
|
if (!dryRun) {
|
|
8657
|
-
fs15.mkdirSync(
|
|
9955
|
+
fs15.mkdirSync(path26.dirname(target), { recursive: true });
|
|
8658
9956
|
fs15.writeFileSync(target, newContent);
|
|
8659
9957
|
}
|
|
8660
9958
|
changes.push({
|
|
@@ -8663,7 +9961,7 @@ async function runUpgradeOpenapi(opts) {
|
|
|
8663
9961
|
});
|
|
8664
9962
|
}
|
|
8665
9963
|
}
|
|
8666
|
-
const appModulePath =
|
|
9964
|
+
const appModulePath = path26.join(projectRoot, "src", "app.module.ts");
|
|
8667
9965
|
if (!fs15.existsSync(appModulePath)) {
|
|
8668
9966
|
return {
|
|
8669
9967
|
projectRoot,
|
|
@@ -8747,7 +10045,7 @@ async function runUpgradeOpenapi(opts) {
|
|
|
8747
10045
|
} else {
|
|
8748
10046
|
changes.push({ path: "src/app.module.ts", action: "unchanged" });
|
|
8749
10047
|
}
|
|
8750
|
-
const mainPath =
|
|
10048
|
+
const mainPath = path26.join(projectRoot, "src", "main.ts");
|
|
8751
10049
|
if (fs15.existsSync(mainPath)) {
|
|
8752
10050
|
const mainSource = project.addSourceFileAtPath(mainPath);
|
|
8753
10051
|
const mainBefore = mainSource.getFullText();
|
|
@@ -8828,7 +10126,7 @@ var ProjectUpgradeOpenapiCommand = class extends Command4 {
|
|
|
8828
10126
|
json = Option4.Boolean("--json", false);
|
|
8829
10127
|
async execute() {
|
|
8830
10128
|
if (this.json) setJsonMode(true);
|
|
8831
|
-
const startDir = this.pathOpt ?
|
|
10129
|
+
const startDir = this.pathOpt ? path26.resolve(this.pathOpt) : process.cwd();
|
|
8832
10130
|
if (!fs15.existsSync(startDir)) {
|
|
8833
10131
|
printError(`Directory not found: ${startDir}`);
|
|
8834
10132
|
return 1;
|
|
@@ -8905,17 +10203,17 @@ ${CONSUMER_SETUP_POINTER}
|
|
|
8905
10203
|
|
|
8906
10204
|
// src/cli/commands/project-update.ts
|
|
8907
10205
|
import fs18 from "fs";
|
|
8908
|
-
import
|
|
10206
|
+
import path29 from "path";
|
|
8909
10207
|
import { Command as Command6, Option as Option6 } from "clipanion";
|
|
8910
10208
|
|
|
8911
10209
|
// src/cli/commands/skills.ts
|
|
8912
10210
|
import fs17 from "fs";
|
|
8913
|
-
import
|
|
10211
|
+
import path28 from "path";
|
|
8914
10212
|
import { Command as Command5, Option as Option5 } from "clipanion";
|
|
8915
10213
|
|
|
8916
10214
|
// src/cli/shared/tree-copier.ts
|
|
8917
10215
|
import fs16 from "fs";
|
|
8918
|
-
import
|
|
10216
|
+
import path27 from "path";
|
|
8919
10217
|
var TEXT_EXTENSIONS = [".ts", ".tsx", ".md", ".mdx", ".yaml", ".yml", ".json"];
|
|
8920
10218
|
function isTextFile(name) {
|
|
8921
10219
|
return TEXT_EXTENSIONS.some((ext) => name.endsWith(ext));
|
|
@@ -8932,17 +10230,17 @@ function copyTreeWithReport(opts) {
|
|
|
8932
10230
|
throw new Error(`tree-copier source directory not found: ${srcDir}`);
|
|
8933
10231
|
}
|
|
8934
10232
|
const walk = (relDir) => {
|
|
8935
|
-
const absSrcDir =
|
|
10233
|
+
const absSrcDir = path27.join(srcDir, relDir);
|
|
8936
10234
|
for (const entry of fs16.readdirSync(absSrcDir, { withFileTypes: true })) {
|
|
8937
|
-
const relPath = relDir ?
|
|
8938
|
-
const absSrc =
|
|
10235
|
+
const relPath = relDir ? path27.posix.join(relDir, entry.name) : entry.name;
|
|
10236
|
+
const absSrc = path27.join(srcDir, relPath);
|
|
8939
10237
|
if (entry.isDirectory()) {
|
|
8940
10238
|
walk(relPath);
|
|
8941
10239
|
continue;
|
|
8942
10240
|
}
|
|
8943
10241
|
if (!entry.isFile()) continue;
|
|
8944
10242
|
if (include && !include(relPath)) continue;
|
|
8945
|
-
const dest =
|
|
10243
|
+
const dest = path27.join(destDir, relPath);
|
|
8946
10244
|
let content = fs16.readFileSync(absSrc, "utf-8");
|
|
8947
10245
|
if (transform && isTextFile(entry.name)) {
|
|
8948
10246
|
content = transform(content, dest);
|
|
@@ -8957,7 +10255,7 @@ function copyTreeWithReport(opts) {
|
|
|
8957
10255
|
action = "updated";
|
|
8958
10256
|
}
|
|
8959
10257
|
if (!dryRun && action !== "unchanged") {
|
|
8960
|
-
fs16.mkdirSync(
|
|
10258
|
+
fs16.mkdirSync(path27.dirname(dest), { recursive: true });
|
|
8961
10259
|
fs16.writeFileSync(dest, content, "utf-8");
|
|
8962
10260
|
}
|
|
8963
10261
|
const record = { relPath, dest, action };
|
|
@@ -8971,13 +10269,13 @@ function copyTreeWithReport(opts) {
|
|
|
8971
10269
|
|
|
8972
10270
|
// src/cli/commands/skills.ts
|
|
8973
10271
|
function consumerSkillsRoot() {
|
|
8974
|
-
const pkgRoot =
|
|
8975
|
-
const topLevel =
|
|
10272
|
+
const pkgRoot = path28.resolve(import.meta.dirname, "..", "..", "..");
|
|
10273
|
+
const topLevel = path28.join(pkgRoot, "consumer-skills");
|
|
8976
10274
|
if (fs17.existsSync(topLevel)) return topLevel;
|
|
8977
|
-
return
|
|
10275
|
+
return path28.join(pkgRoot, "dist", "consumer-skills");
|
|
8978
10276
|
}
|
|
8979
10277
|
function skillsTargetDir(cwd) {
|
|
8980
|
-
return
|
|
10278
|
+
return path28.join(cwd, ".claude", "skills");
|
|
8981
10279
|
}
|
|
8982
10280
|
function availableSkills() {
|
|
8983
10281
|
const root = consumerSkillsRoot();
|
|
@@ -9024,13 +10322,13 @@ async function summary3(ctx) {
|
|
|
9024
10322
|
return {
|
|
9025
10323
|
title: "skills",
|
|
9026
10324
|
body,
|
|
9027
|
-
footer: `${installedCount} of ${skills.length} skills installed \u2192 ${
|
|
10325
|
+
footer: `${installedCount} of ${skills.length} skills installed \u2192 ${path28.relative(ctx.cwd, targetDir) || targetDir}`
|
|
9028
10326
|
};
|
|
9029
10327
|
}
|
|
9030
10328
|
async function hints3(ctx) {
|
|
9031
10329
|
const skills = availableSkills();
|
|
9032
10330
|
const targetDir = skillsTargetDir(ctx.cwd);
|
|
9033
|
-
const allPresent = skills.length > 0 && fs17.existsSync(targetDir) && skills.every((s) => fs17.existsSync(
|
|
10331
|
+
const allPresent = skills.length > 0 && fs17.existsSync(targetDir) && skills.every((s) => fs17.existsSync(path28.join(targetDir, s)));
|
|
9034
10332
|
if (allPresent) {
|
|
9035
10333
|
return [
|
|
9036
10334
|
{ command: "codegen update", description: "Re-sync skills + runtime after a package bump" }
|
|
@@ -9105,7 +10403,7 @@ var SkillsInstallCommand = class extends Command5 {
|
|
|
9105
10403
|
});
|
|
9106
10404
|
return 0;
|
|
9107
10405
|
}
|
|
9108
|
-
printInfo(`target = ${
|
|
10406
|
+
printInfo(`target = ${path28.relative(ctx.cwd, result.targetDir) || result.targetDir}`);
|
|
9109
10407
|
console.log("");
|
|
9110
10408
|
renderTreeReport(report);
|
|
9111
10409
|
console.log("");
|
|
@@ -9133,7 +10431,7 @@ var SkillsListCommand = class extends Command5 {
|
|
|
9133
10431
|
const skills = availableSkills();
|
|
9134
10432
|
const targetDir = skillsTargetDir(ctx.cwd);
|
|
9135
10433
|
const rows = skills.map((name) => {
|
|
9136
|
-
const dir =
|
|
10434
|
+
const dir = path28.join(targetDir, name);
|
|
9137
10435
|
return { name, status: fs17.existsSync(dir) ? "installed" : "available" };
|
|
9138
10436
|
});
|
|
9139
10437
|
if (isJsonMode()) {
|
|
@@ -9163,7 +10461,7 @@ var NON_RUNTIME_SUBSYSTEMS = /* @__PURE__ */ new Set(["openapi-config", "auth-in
|
|
|
9163
10461
|
function syncVendoredRuntime(cwd, write) {
|
|
9164
10462
|
const changes = [];
|
|
9165
10463
|
for (const v of VENDORED_RUNTIME_FILES) {
|
|
9166
|
-
const dest =
|
|
10464
|
+
const dest = path29.join(cwd, v.target);
|
|
9167
10465
|
const content = loadRuntimeFile(v.runtime);
|
|
9168
10466
|
const existing = fs18.existsSync(dest) ? fs18.readFileSync(dest, "utf-8") : null;
|
|
9169
10467
|
let action;
|
|
@@ -9171,7 +10469,7 @@ function syncVendoredRuntime(cwd, write) {
|
|
|
9171
10469
|
else if (existing === content) action = "unchanged";
|
|
9172
10470
|
else action = "updated";
|
|
9173
10471
|
if (write && action !== "unchanged") {
|
|
9174
|
-
fs18.mkdirSync(
|
|
10472
|
+
fs18.mkdirSync(path29.dirname(dest), { recursive: true });
|
|
9175
10473
|
fs18.writeFileSync(dest, content, "utf-8");
|
|
9176
10474
|
}
|
|
9177
10475
|
changes.push({ path: v.target, action });
|
|
@@ -9186,14 +10484,14 @@ async function syncSubsystemRuntime(cwd, inst, write) {
|
|
|
9186
10484
|
if (!fs18.existsSync(source)) {
|
|
9187
10485
|
return { name: inst.name, changes: [], skippedReason: "no runtime source in package" };
|
|
9188
10486
|
}
|
|
9189
|
-
const subsystemsRoot =
|
|
10487
|
+
const subsystemsRoot = path29.dirname(inst.path);
|
|
9190
10488
|
const result = await copyRuntime({
|
|
9191
10489
|
sourceDir: source,
|
|
9192
10490
|
targetDir: inst.path,
|
|
9193
10491
|
filter: backendFileFilter(inst.backend, inst.name),
|
|
9194
10492
|
resolveDeps: true,
|
|
9195
10493
|
runtimeRoot: runtimeRoot2(),
|
|
9196
|
-
depsTargetRoot:
|
|
10494
|
+
depsTargetRoot: path29.resolve(subsystemsRoot, ".."),
|
|
9197
10495
|
dryRun: !write,
|
|
9198
10496
|
// Refresh files already vendored for this subsystem; never install new
|
|
9199
10497
|
// ones (that's `subsystem install`). copyRuntime classifies accurately
|
|
@@ -9207,7 +10505,7 @@ async function syncSubsystemRuntime(cwd, inst, write) {
|
|
|
9207
10505
|
return { name: inst.name, changes };
|
|
9208
10506
|
}
|
|
9209
10507
|
function rel(cwd, abs) {
|
|
9210
|
-
return
|
|
10508
|
+
return path29.relative(cwd, abs) || abs;
|
|
9211
10509
|
}
|
|
9212
10510
|
var ProjectUpdateCommand = class extends Command6 {
|
|
9213
10511
|
static paths = [["project", "update"]];
|
|
@@ -9246,7 +10544,7 @@ var ProjectUpdateCommand = class extends Command6 {
|
|
|
9246
10544
|
const installed = this.skipSubsystems ? [] : await detectInstalledSubsystems(ctx);
|
|
9247
10545
|
if (!this.dryRun && !this.force) {
|
|
9248
10546
|
const vendoredDry = syncVendoredRuntime(ctx.cwd, false);
|
|
9249
|
-
const vendoredDirtyCandidates = vendoredDry.filter((c) => c.action === "updated").map((c) =>
|
|
10547
|
+
const vendoredDirtyCandidates = vendoredDry.filter((c) => c.action === "updated").map((c) => path29.join(ctx.cwd, c.path));
|
|
9250
10548
|
const skillDirtyCandidates = this.skipSkills ? [] : runSkillsInstall({ cwd: ctx.cwd, dryRun: true }).report?.updated.map((e) => e.dest) ?? [];
|
|
9251
10549
|
const subsystemDirs = installed.filter((i) => !NON_RUNTIME_SUBSYSTEMS.has(i.name)).map((i) => i.path);
|
|
9252
10550
|
const gate = checkGitSafety(
|
|
@@ -9597,8 +10895,8 @@ var ProjectScanCommand = class extends Command7 {
|
|
|
9597
10895
|
cwd = Option7.String("--cwd", { required: false });
|
|
9598
10896
|
async execute() {
|
|
9599
10897
|
if (this.json) setJsonMode(true);
|
|
9600
|
-
const baseCwd = this.cwd ?
|
|
9601
|
-
const target = this.directory ?
|
|
10898
|
+
const baseCwd = this.cwd ? path30.resolve(this.cwd) : process.cwd();
|
|
10899
|
+
const target = this.directory ? path30.resolve(baseCwd, this.directory) : baseCwd;
|
|
9602
10900
|
if (!fs19.existsSync(target)) {
|
|
9603
10901
|
printError(`Directory not found: ${target}`);
|
|
9604
10902
|
return 1;
|
|
@@ -9649,7 +10947,7 @@ var ProjectScanCommand = class extends Command7 {
|
|
|
9649
10947
|
`architecture: ${profile.architecture.evidence.join(", ") || "\u2014"}`
|
|
9650
10948
|
]);
|
|
9651
10949
|
}
|
|
9652
|
-
const outPath =
|
|
10950
|
+
const outPath = path30.join(target, "codegen.config.yaml");
|
|
9653
10951
|
const existsNow = fs19.existsSync(outPath);
|
|
9654
10952
|
if (this.dryRun) {
|
|
9655
10953
|
console.log("");
|
|
@@ -9770,8 +11068,8 @@ var ProjectInspectCommand = class extends Command7 {
|
|
|
9770
11068
|
return 2;
|
|
9771
11069
|
}
|
|
9772
11070
|
resolveEntitiesDir(ctx) {
|
|
9773
|
-
if (this.dir) return
|
|
9774
|
-
return ctx.entitiesDir ??
|
|
11071
|
+
if (this.dir) return path30.resolve(ctx.cwd, this.dir);
|
|
11072
|
+
return ctx.entitiesDir ?? path30.resolve(ctx.cwd, "entities");
|
|
9775
11073
|
}
|
|
9776
11074
|
async runAnalysis(ctx, kind) {
|
|
9777
11075
|
const entitiesDir = this.resolveEntitiesDir(ctx);
|
|
@@ -9974,15 +11272,15 @@ var ProjectGraphCommand = class extends Command7 {
|
|
|
9974
11272
|
json: this.json,
|
|
9975
11273
|
skipDetection: true
|
|
9976
11274
|
});
|
|
9977
|
-
const entitiesDir = this.dir ?
|
|
11275
|
+
const entitiesDir = this.dir ? path30.resolve(ctx.cwd, this.dir) : ctx.entitiesDir ?? path30.resolve(ctx.cwd, "entities");
|
|
9978
11276
|
if (!fs19.existsSync(entitiesDir)) {
|
|
9979
11277
|
printError(`Entity directory not found: ${entitiesDir}`);
|
|
9980
11278
|
return 1;
|
|
9981
11279
|
}
|
|
9982
11280
|
const relCandidates = [
|
|
9983
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
11281
|
+
path30.resolve(path30.dirname(entitiesDir), "relationships"),
|
|
11282
|
+
path30.resolve(entitiesDir, "relationships"),
|
|
11283
|
+
path30.resolve(ctx.cwd, "relationships")
|
|
9986
11284
|
];
|
|
9987
11285
|
const relationshipsDir = relCandidates.find((d) => fs19.existsSync(d));
|
|
9988
11286
|
const result = await analyzeDomain(entitiesDir, relationshipsDir);
|
|
@@ -9998,20 +11296,20 @@ var ProjectGraphCommand = class extends Command7 {
|
|
|
9998
11296
|
return 0;
|
|
9999
11297
|
}
|
|
10000
11298
|
if (this.output) {
|
|
10001
|
-
const outPath =
|
|
11299
|
+
const outPath = path30.resolve(ctx.cwd, this.output);
|
|
10002
11300
|
fs19.writeFileSync(outPath, JSON.stringify(serialized, null, 2));
|
|
10003
11301
|
printSuccess(`Graph written to ${outPath}`);
|
|
10004
11302
|
printInfo(`${result.entities.length} entities, ${result.relationshipDefinitions.length} relationships, ${result.graph.edges.length} edges`);
|
|
10005
11303
|
return 0;
|
|
10006
11304
|
}
|
|
10007
11305
|
const os = await import("os");
|
|
10008
|
-
const tmpDir = fs19.mkdtempSync(
|
|
10009
|
-
const graphPath =
|
|
11306
|
+
const tmpDir = fs19.mkdtempSync(path30.join(os.default.tmpdir(), "codegen-graph-"));
|
|
11307
|
+
const graphPath = path30.join(tmpDir, "graph.json");
|
|
10010
11308
|
fs19.writeFileSync(graphPath, JSON.stringify(serialized, null, 2));
|
|
10011
|
-
const viewerDir =
|
|
10012
|
-
const viewerDist =
|
|
11309
|
+
const viewerDir = path30.resolve(import.meta.dirname, "..", "..", "..", "tools", "schema-graph-viewer");
|
|
11310
|
+
const viewerDist = path30.join(viewerDir, "dist", "index.html");
|
|
10013
11311
|
if (fs19.existsSync(viewerDist)) {
|
|
10014
|
-
fs19.copyFileSync(graphPath,
|
|
11312
|
+
fs19.copyFileSync(graphPath, path30.join(viewerDir, "dist", "graph.json"));
|
|
10015
11313
|
printSuccess("Graph exported");
|
|
10016
11314
|
printInfo(`${result.entities.length} entities, ${result.relationshipDefinitions.length} relationships, ${result.graph.edges.length} edges`);
|
|
10017
11315
|
printInfo(`Graph JSON: ${graphPath}`);
|
|
@@ -10043,7 +11341,7 @@ var project_default = projectNoun;
|
|
|
10043
11341
|
|
|
10044
11342
|
// src/cli/commands/dev.ts
|
|
10045
11343
|
import fs20 from "fs";
|
|
10046
|
-
import
|
|
11344
|
+
import path31 from "path";
|
|
10047
11345
|
import { execSync as execSync3, spawn, spawnSync } from "child_process";
|
|
10048
11346
|
import { Command as Command8, Option as Option8 } from "clipanion";
|
|
10049
11347
|
var DEFAULT_APP_PORT = 3e3;
|
|
@@ -10076,14 +11374,14 @@ function getRedisPort(_ctx) {
|
|
|
10076
11374
|
return Number(process.env.DEV_REDIS_PORT ?? DEFAULT_REDIS_PORT);
|
|
10077
11375
|
}
|
|
10078
11376
|
function composeFilePath(cwd) {
|
|
10079
|
-
const devPath =
|
|
11377
|
+
const devPath = path31.join(cwd, COMPOSE_FILE);
|
|
10080
11378
|
if (fs20.existsSync(devPath)) return devPath;
|
|
10081
|
-
const rootPath =
|
|
11379
|
+
const rootPath = path31.join(cwd, "docker-compose.yml");
|
|
10082
11380
|
if (fs20.existsSync(rootPath)) return rootPath;
|
|
10083
11381
|
return devPath;
|
|
10084
11382
|
}
|
|
10085
11383
|
function pidFilePath(cwd) {
|
|
10086
|
-
return
|
|
11384
|
+
return path31.join(cwd, PID_FILE);
|
|
10087
11385
|
}
|
|
10088
11386
|
function readAppPid(cwd) {
|
|
10089
11387
|
const p = pidFilePath(cwd);
|
|
@@ -10144,7 +11442,7 @@ function checkApp(cwd, port) {
|
|
|
10144
11442
|
function listEntityNames(ctx) {
|
|
10145
11443
|
if (!ctx.entitiesDir || !fs20.existsSync(ctx.entitiesDir)) return [];
|
|
10146
11444
|
return findYamlFiles(ctx.entitiesDir).map(
|
|
10147
|
-
(f) =>
|
|
11445
|
+
(f) => path31.basename(f).replace(/\.ya?ml$/, "")
|
|
10148
11446
|
);
|
|
10149
11447
|
}
|
|
10150
11448
|
function formatServiceLine(svc) {
|
|
@@ -10154,7 +11452,7 @@ function formatServiceLine(svc) {
|
|
|
10154
11452
|
return `${icon} ${svc.name.padEnd(12)} ${theme.muted(`${svc.host}:${svc.port}`)} ${status}${pidStr}`;
|
|
10155
11453
|
}
|
|
10156
11454
|
function ensureComposeFile(cwd, pgPort, redisPort) {
|
|
10157
|
-
const composePath =
|
|
11455
|
+
const composePath = path31.join(cwd, COMPOSE_FILE);
|
|
10158
11456
|
if (fs20.existsSync(composePath)) return composePath;
|
|
10159
11457
|
const content = `# Auto-generated by codegen dev
|
|
10160
11458
|
# Ports offset from defaults to avoid conflicts with other local services.
|
|
@@ -10239,7 +11537,7 @@ var DevUpCommand = class extends Command8 {
|
|
|
10239
11537
|
if (!pgReady) printWarning("postgres did not become healthy in time");
|
|
10240
11538
|
if (!redisReady) printWarning("redis did not become healthy in time");
|
|
10241
11539
|
const drizzleConfig = ["drizzle.config.ts", "drizzle.config.js"].find(
|
|
10242
|
-
(f) => fs20.existsSync(
|
|
11540
|
+
(f) => fs20.existsSync(path31.join(ctx.cwd, f))
|
|
10243
11541
|
);
|
|
10244
11542
|
if (drizzleConfig) {
|
|
10245
11543
|
if (!isJsonMode()) printInfo("pushing database schema...");
|
|
@@ -10259,7 +11557,7 @@ var DevUpCommand = class extends Command8 {
|
|
|
10259
11557
|
if (!isJsonMode()) printInfo("starting NestJS app...");
|
|
10260
11558
|
const dbUrl = `postgres://postgres:postgres@localhost:${pgPort}/codegen_dev`;
|
|
10261
11559
|
const redisUrl = `redis://localhost:${redisPort}`;
|
|
10262
|
-
const logFile =
|
|
11560
|
+
const logFile = path31.join(ctx.cwd, ".dev-app.log");
|
|
10263
11561
|
const logFd = fs20.openSync(logFile, "a");
|
|
10264
11562
|
const child = spawn("bun", ["src/main.ts"], {
|
|
10265
11563
|
cwd: ctx.cwd,
|
|
@@ -10417,7 +11715,7 @@ var DevLogsCommand = class extends Command8 {
|
|
|
10417
11715
|
}
|
|
10418
11716
|
return 0;
|
|
10419
11717
|
}
|
|
10420
|
-
const logFile =
|
|
11718
|
+
const logFile = path31.join(ctx.cwd, ".dev-app.log");
|
|
10421
11719
|
if (!fs20.existsSync(logFile)) {
|
|
10422
11720
|
printInfo("no app logs found \u2014 is the app running?");
|
|
10423
11721
|
return 0;
|
|
@@ -10461,7 +11759,7 @@ var DevRestartCommand = class extends Command8 {
|
|
|
10461
11759
|
spawnSync("sleep", ["1"]);
|
|
10462
11760
|
const dbUrl = `postgres://postgres:postgres@localhost:${pgPort}/codegen_dev`;
|
|
10463
11761
|
const redisUrl = `redis://localhost:${redisPort}`;
|
|
10464
|
-
const logFile =
|
|
11762
|
+
const logFile = path31.join(ctx.cwd, ".dev-app.log");
|
|
10465
11763
|
const logFd = fs20.openSync(logFile, "a");
|
|
10466
11764
|
const child = spawn("bun", ["src/main.ts"], {
|
|
10467
11765
|
cwd: ctx.cwd,
|
|
@@ -10591,7 +11889,7 @@ var dev_default = devNoun;
|
|
|
10591
11889
|
|
|
10592
11890
|
// src/cli/commands/relationship.ts
|
|
10593
11891
|
import fs21 from "fs";
|
|
10594
|
-
import
|
|
11892
|
+
import path32 from "path";
|
|
10595
11893
|
import { Command as Command9, Option as Option9 } from "clipanion";
|
|
10596
11894
|
function listRelationshipYamls2(dir) {
|
|
10597
11895
|
if (!fs21.existsSync(dir)) return [];
|
|
@@ -10618,7 +11916,7 @@ function padRight2(s, n) {
|
|
|
10618
11916
|
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
10619
11917
|
}
|
|
10620
11918
|
async function summary6(ctx) {
|
|
10621
|
-
const relDir =
|
|
11919
|
+
const relDir = path32.resolve(ctx.cwd, "relationships");
|
|
10622
11920
|
const files = listRelationshipYamls2(relDir);
|
|
10623
11921
|
if (files.length === 0) {
|
|
10624
11922
|
return {
|
|
@@ -10688,14 +11986,14 @@ var RelationshipNewCommand = class extends Command9 {
|
|
|
10688
11986
|
}
|
|
10689
11987
|
let targets = [];
|
|
10690
11988
|
if (this.all) {
|
|
10691
|
-
const dir =
|
|
11989
|
+
const dir = path32.resolve(ctx.cwd, "relationships");
|
|
10692
11990
|
targets = listRelationshipYamls2(dir);
|
|
10693
11991
|
if (targets.length === 0) {
|
|
10694
11992
|
printError(`No relationship YAML files found in ${dir}`);
|
|
10695
11993
|
return 1;
|
|
10696
11994
|
}
|
|
10697
11995
|
} else if (this.yaml) {
|
|
10698
|
-
targets = [
|
|
11996
|
+
targets = [path32.resolve(ctx.cwd, this.yaml)];
|
|
10699
11997
|
} else {
|
|
10700
11998
|
printError("Missing YAML path. Pass a file or --all.");
|
|
10701
11999
|
return 2;
|
|
@@ -10712,7 +12010,7 @@ var RelationshipNewCommand = class extends Command9 {
|
|
|
10712
12010
|
}
|
|
10713
12011
|
if (invalid.length > 0) {
|
|
10714
12012
|
for (const i of invalid) {
|
|
10715
|
-
printError(`${
|
|
12013
|
+
printError(`${path32.basename(i.file)} \u2014 ${i.message}`);
|
|
10716
12014
|
}
|
|
10717
12015
|
if (!isJsonMode()) return 1;
|
|
10718
12016
|
}
|
|
@@ -10743,7 +12041,7 @@ var RelationshipNewCommand = class extends Command9 {
|
|
|
10743
12041
|
}
|
|
10744
12042
|
const succeeded = [];
|
|
10745
12043
|
const failed = [
|
|
10746
|
-
...invalid.map((i) => ({ name:
|
|
12044
|
+
...invalid.map((i) => ({ name: path32.basename(i.file), file: i.file, message: i.message }))
|
|
10747
12045
|
];
|
|
10748
12046
|
for (const v of validated) {
|
|
10749
12047
|
if (!isJsonMode()) {
|
|
@@ -10762,8 +12060,8 @@ var RelationshipNewCommand = class extends Command9 {
|
|
|
10762
12060
|
if (!isJsonMode()) printError(`${v.name} \u2014 ${res.stderr ?? "failed"}`);
|
|
10763
12061
|
}
|
|
10764
12062
|
}
|
|
10765
|
-
const entitiesDir = ctx.entitiesDir ??
|
|
10766
|
-
const relationshipsDir =
|
|
12063
|
+
const entitiesDir = ctx.entitiesDir ?? path32.resolve(ctx.cwd, "entities");
|
|
12064
|
+
const relationshipsDir = path32.resolve(ctx.cwd, "relationships");
|
|
10767
12065
|
const generatedDir = resolveGeneratedDir(ctx);
|
|
10768
12066
|
const architecture = resolveArchitecture(ctx);
|
|
10769
12067
|
let barrelResult = null;
|
|
@@ -10808,7 +12106,7 @@ var RelationshipNewCommand = class extends Command9 {
|
|
|
10808
12106
|
}
|
|
10809
12107
|
if (barrelResult) {
|
|
10810
12108
|
printInfo(
|
|
10811
|
-
`barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${
|
|
12109
|
+
`barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path32.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path32.relative(ctx.cwd, barrelResult.schemaBarrel)}`
|
|
10812
12110
|
);
|
|
10813
12111
|
}
|
|
10814
12112
|
}
|
|
@@ -10831,7 +12129,7 @@ var RelationshipListCommand = class extends Command9 {
|
|
|
10831
12129
|
json: this.json,
|
|
10832
12130
|
skipDetection: true
|
|
10833
12131
|
});
|
|
10834
|
-
const relDir =
|
|
12132
|
+
const relDir = path32.resolve(ctx.cwd, "relationships");
|
|
10835
12133
|
const files = listRelationshipYamls2(relDir);
|
|
10836
12134
|
if (files.length === 0) {
|
|
10837
12135
|
printInfo("No relationship definitions found.");
|
|
@@ -10871,7 +12169,7 @@ var relationshipNoun = {
|
|
|
10871
12169
|
var relationship_default = relationshipNoun;
|
|
10872
12170
|
|
|
10873
12171
|
// src/cli/commands/junction.ts
|
|
10874
|
-
import
|
|
12172
|
+
import path33 from "path";
|
|
10875
12173
|
import { Command as Command10, Option as Option10 } from "clipanion";
|
|
10876
12174
|
function summarizeJunctionFile(filePath) {
|
|
10877
12175
|
const result = loadJunctionFromYaml(filePath);
|
|
@@ -10894,7 +12192,7 @@ function padRight3(s, n) {
|
|
|
10894
12192
|
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
10895
12193
|
}
|
|
10896
12194
|
async function summary7(ctx) {
|
|
10897
|
-
const junctionDir =
|
|
12195
|
+
const junctionDir = path33.resolve(ctx.cwd, "junctions");
|
|
10898
12196
|
const files = listJunctionYamls(junctionDir);
|
|
10899
12197
|
if (files.length === 0) {
|
|
10900
12198
|
return {
|
|
@@ -10964,14 +12262,14 @@ var JunctionNewCommand = class extends Command10 {
|
|
|
10964
12262
|
}
|
|
10965
12263
|
let targets = [];
|
|
10966
12264
|
if (this.all) {
|
|
10967
|
-
const dir =
|
|
12265
|
+
const dir = path33.resolve(ctx.cwd, "junctions");
|
|
10968
12266
|
targets = listJunctionYamls(dir);
|
|
10969
12267
|
if (targets.length === 0) {
|
|
10970
12268
|
printError(`No junction YAML files found in ${dir}`);
|
|
10971
12269
|
return 1;
|
|
10972
12270
|
}
|
|
10973
12271
|
} else if (this.yaml) {
|
|
10974
|
-
targets = [
|
|
12272
|
+
targets = [path33.resolve(ctx.cwd, this.yaml)];
|
|
10975
12273
|
} else {
|
|
10976
12274
|
printError("Missing YAML path. Pass a file or --all.");
|
|
10977
12275
|
return 2;
|
|
@@ -10990,7 +12288,7 @@ var JunctionNewCommand = class extends Command10 {
|
|
|
10990
12288
|
}
|
|
10991
12289
|
if (invalid.length > 0) {
|
|
10992
12290
|
for (const i of invalid) {
|
|
10993
|
-
printError(`${
|
|
12291
|
+
printError(`${path33.basename(i.file)} \u2014 ${i.message}`);
|
|
10994
12292
|
}
|
|
10995
12293
|
if (!isJsonMode()) return 1;
|
|
10996
12294
|
}
|
|
@@ -11021,7 +12319,7 @@ var JunctionNewCommand = class extends Command10 {
|
|
|
11021
12319
|
}
|
|
11022
12320
|
const succeeded = [];
|
|
11023
12321
|
const failed = [
|
|
11024
|
-
...invalid.map((i) => ({ name:
|
|
12322
|
+
...invalid.map((i) => ({ name: path33.basename(i.file), file: i.file, message: i.message }))
|
|
11025
12323
|
];
|
|
11026
12324
|
for (const v of validated) {
|
|
11027
12325
|
if (!isJsonMode()) {
|
|
@@ -11040,9 +12338,9 @@ var JunctionNewCommand = class extends Command10 {
|
|
|
11040
12338
|
if (!isJsonMode()) printError(`${v.name} \u2014 ${res.stderr ?? "failed"}`);
|
|
11041
12339
|
}
|
|
11042
12340
|
}
|
|
11043
|
-
const entitiesDir = ctx.entitiesDir ??
|
|
11044
|
-
const relationshipsDir =
|
|
11045
|
-
const junctionsDir =
|
|
12341
|
+
const entitiesDir = ctx.entitiesDir ?? path33.resolve(ctx.cwd, "entities");
|
|
12342
|
+
const relationshipsDir = path33.resolve(ctx.cwd, "relationships");
|
|
12343
|
+
const junctionsDir = path33.resolve(ctx.cwd, "junctions");
|
|
11046
12344
|
const generatedDir = resolveGeneratedDir(ctx);
|
|
11047
12345
|
const architecture = resolveArchitecture(ctx);
|
|
11048
12346
|
let barrelResult = null;
|
|
@@ -11088,7 +12386,7 @@ var JunctionNewCommand = class extends Command10 {
|
|
|
11088
12386
|
}
|
|
11089
12387
|
if (barrelResult) {
|
|
11090
12388
|
printInfo(
|
|
11091
|
-
`barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${
|
|
12389
|
+
`barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path33.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path33.relative(ctx.cwd, barrelResult.schemaBarrel)}`
|
|
11092
12390
|
);
|
|
11093
12391
|
}
|
|
11094
12392
|
}
|
|
@@ -11111,7 +12409,7 @@ var JunctionListCommand = class extends Command10 {
|
|
|
11111
12409
|
json: this.json,
|
|
11112
12410
|
skipDetection: true
|
|
11113
12411
|
});
|
|
11114
|
-
const junctionDir =
|
|
12412
|
+
const junctionDir = path33.resolve(ctx.cwd, "junctions");
|
|
11115
12413
|
const files = listJunctionYamls(junctionDir);
|
|
11116
12414
|
if (files.length === 0) {
|
|
11117
12415
|
printInfo("No junction definitions found.");
|
|
@@ -11152,7 +12450,7 @@ var junction_default = junctionNoun;
|
|
|
11152
12450
|
|
|
11153
12451
|
// src/cli/commands/events.ts
|
|
11154
12452
|
import fs22 from "fs";
|
|
11155
|
-
import
|
|
12453
|
+
import path34 from "path";
|
|
11156
12454
|
import ts2 from "typescript";
|
|
11157
12455
|
import { Command as Command11, Option as Option11 } from "clipanion";
|
|
11158
12456
|
function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
|
|
@@ -11288,7 +12586,7 @@ function suggestEventTypes(target, known, limit = 3) {
|
|
|
11288
12586
|
return known.map((t) => ({ t, d: levenshtein(target, t) })).sort((a, b) => a.d - b.d).slice(0, limit).map((x) => x.t);
|
|
11289
12587
|
}
|
|
11290
12588
|
function renderConsumerReport(result, cwd) {
|
|
11291
|
-
const rel2 = (p) =>
|
|
12589
|
+
const rel2 = (p) => path34.relative(cwd, p) || p;
|
|
11292
12590
|
const lines = [];
|
|
11293
12591
|
const total = result.tier3.length + result.tier2.length + result.tier1.length;
|
|
11294
12592
|
lines.push(`Event: ${result.eventType}`);
|
|
@@ -11324,7 +12622,7 @@ function renderConsumerReport(result, cwd) {
|
|
|
11324
12622
|
return lines;
|
|
11325
12623
|
}
|
|
11326
12624
|
function runConsumersScan(opts) {
|
|
11327
|
-
const scanRoot = opts.scanRoot ??
|
|
12625
|
+
const scanRoot = opts.scanRoot ?? path34.join(opts.cwd, "src");
|
|
11328
12626
|
const handlersDir = opts.handlersDir ?? scanRoot;
|
|
11329
12627
|
const allTriggers = scanHandlerFiles(handlersDir);
|
|
11330
12628
|
const tier3 = allTriggers.filter((t) => t.event === opts.eventType).map((t) => ({
|
|
@@ -11334,7 +12632,7 @@ function runConsumersScan(opts) {
|
|
|
11334
12632
|
sourceLine: t.sourceLine
|
|
11335
12633
|
}));
|
|
11336
12634
|
const tier21 = fs22.existsSync(scanRoot) ? scanDirectoryForConsumers(scanRoot, opts.eventType) : { tier2: [], tier1: [], hasEventFlowImport: false };
|
|
11337
|
-
const eventsGeneratedDir = opts.eventsGeneratedDir ??
|
|
12635
|
+
const eventsGeneratedDir = opts.eventsGeneratedDir ?? path34.join(
|
|
11338
12636
|
resolveSubsystemsRootFromContext(opts.cwd, opts.config),
|
|
11339
12637
|
"events",
|
|
11340
12638
|
"generated"
|
|
@@ -11355,11 +12653,11 @@ function runConsumersScan(opts) {
|
|
|
11355
12653
|
function resolveSubsystemsRootFromContext(cwd, config) {
|
|
11356
12654
|
const configured = config?.paths?.subsystems;
|
|
11357
12655
|
if (typeof configured === "string" && configured.length > 0) {
|
|
11358
|
-
return
|
|
12656
|
+
return path34.resolve(cwd, configured);
|
|
11359
12657
|
}
|
|
11360
12658
|
const backendSrc = config?.paths?.backend_src;
|
|
11361
12659
|
const base = typeof backendSrc === "string" && backendSrc.length > 0 ? backendSrc : "src";
|
|
11362
|
-
return
|
|
12660
|
+
return path34.resolve(cwd, base, "shared", "subsystems");
|
|
11363
12661
|
}
|
|
11364
12662
|
var EventsConsumersCommand = class extends Command11 {
|
|
11365
12663
|
static paths = [["events", "consumers"]];
|
|
@@ -11448,7 +12746,7 @@ var eventsNoun = {
|
|
|
11448
12746
|
var events_default = eventsNoun;
|
|
11449
12747
|
|
|
11450
12748
|
// src/cli/commands/orchestration.ts
|
|
11451
|
-
import
|
|
12749
|
+
import path35 from "path";
|
|
11452
12750
|
import { Command as Command12, Option as Option12 } from "clipanion";
|
|
11453
12751
|
var DEFAULT_PATTERN_GLOBS = ["src/patterns/*.pattern.ts"];
|
|
11454
12752
|
function resolvePatternGlobs(ctx) {
|
|
@@ -11462,10 +12760,10 @@ function resolveOrchestrationOutputRoot(ctx) {
|
|
|
11462
12760
|
const paths = ctx.config?.paths;
|
|
11463
12761
|
const explicit = paths?.orchestration_src;
|
|
11464
12762
|
if (typeof explicit === "string" && explicit.length > 0) {
|
|
11465
|
-
return
|
|
12763
|
+
return path35.resolve(ctx.cwd, explicit);
|
|
11466
12764
|
}
|
|
11467
12765
|
const backendSrc = typeof paths?.backend_src === "string" && paths.backend_src.length > 0 ? paths.backend_src : "app/backend/src";
|
|
11468
|
-
return
|
|
12766
|
+
return path35.resolve(ctx.cwd, backendSrc, "orchestration");
|
|
11469
12767
|
}
|
|
11470
12768
|
async function reloadRegistry(ctx) {
|
|
11471
12769
|
_resetRegistryForTests({ includeLibrary: false });
|
|
@@ -11554,12 +12852,12 @@ var OrchestrationGenCommand = class extends Command12 {
|
|
|
11554
12852
|
);
|
|
11555
12853
|
for (const f of result.files) {
|
|
11556
12854
|
console.log(
|
|
11557
|
-
` ${theme.muted(icons.arrow)} ${
|
|
12855
|
+
` ${theme.muted(icons.arrow)} ${path35.relative(ctx.cwd, f.outputPath)}`
|
|
11558
12856
|
);
|
|
11559
12857
|
}
|
|
11560
12858
|
} else {
|
|
11561
12859
|
printSuccess(
|
|
11562
|
-
`Emitted ${result.files.length} file(s) across ${targets.length} pattern(s) \u2192 ${
|
|
12860
|
+
`Emitted ${result.files.length} file(s) across ${targets.length} pattern(s) \u2192 ${path35.relative(ctx.cwd, outputRoot)}`
|
|
11563
12861
|
);
|
|
11564
12862
|
}
|
|
11565
12863
|
return 0;
|
|
@@ -11725,7 +13023,7 @@ var update_default = UpdateShortcut;
|
|
|
11725
13023
|
// src/cli/index.ts
|
|
11726
13024
|
function readVersion() {
|
|
11727
13025
|
try {
|
|
11728
|
-
const pkgPath =
|
|
13026
|
+
const pkgPath = join18(import.meta.dirname, "..", "..", "package.json");
|
|
11729
13027
|
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
11730
13028
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
11731
13029
|
} catch {
|