@pattern-stack/codegen 0.17.2 → 0.18.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/README.md +106 -2
- package/dist/{chunk-65MO75WM.js → chunk-2VGVSL2D.js} +8 -8
- package/dist/{chunk-VNBC3VXM.js → chunk-3A34R6CI.js} +7 -7
- package/dist/{chunk-I6UXRJ3Q.js → chunk-43SBT72G.js} +4 -4
- package/dist/{chunk-7OVCARTQ.js → chunk-5RT7JGKT.js} +4 -4
- package/dist/{chunk-E6PLM6QG.js → chunk-7MMS36AN.js} +9 -9
- package/dist/{chunk-4MVGAMUA.js → chunk-BK5ICA2F.js} +4 -4
- package/dist/{chunk-VDL5CJ5C.js → chunk-C5E7H553.js} +7 -7
- package/dist/{chunk-IOQMMH6C.js → chunk-CFFTPWHM.js} +79 -4
- package/dist/chunk-CFFTPWHM.js.map +1 -0
- package/dist/{chunk-XKWOJZZ4.js → chunk-E45CSC33.js} +2 -2
- package/dist/{chunk-AZLUWG5S.js → chunk-EWYI5GGJ.js} +13 -13
- package/dist/{chunk-BHZP6LOV.js → chunk-IN3EWFB4.js} +4 -4
- package/dist/{chunk-CZQUOIDY.js → chunk-J7JMVS2B.js} +4 -4
- package/dist/{chunk-KSTZIULO.js → chunk-K2I6XIK5.js} +4 -4
- package/dist/{chunk-Z7PQCAVK.js → chunk-LQ6PYFU6.js} +4 -4
- package/dist/{chunk-T6SCOJF4.js → chunk-NXHL5YII.js} +4 -4
- package/dist/{chunk-ATVGYF3D.js → chunk-PKDS6QIJ.js} +7 -7
- package/dist/{chunk-OZEPJGMA.js → chunk-R4BPUUB5.js} +4 -4
- package/dist/{chunk-B34G6PHD.js → chunk-RKNW56RU.js} +8 -8
- package/dist/{chunk-R6F6KFIL.js → chunk-TBGTMALE.js} +4 -4
- package/dist/{chunk-GM3RMJIJ.js → chunk-VHAR2BGH.js} +4 -4
- package/dist/{chunk-CLWBNXKF.js → chunk-W2UIDI3R.js} +4 -4
- package/dist/{chunk-235ZMMJR.js → chunk-X6BP6LI5.js} +3 -3
- package/dist/{chunk-KZDHMZ45.js → chunk-YZLBU6O2.js} +10 -10
- 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 +2 -2
- package/dist/runtime/subsystems/auth/index.js +12 -12
- package/dist/runtime/subsystems/bridge/bridge-delivery-handler.js +2 -2
- package/dist/runtime/subsystems/bridge/bridge-delivery.drizzle-backend.js +3 -3
- package/dist/runtime/subsystems/bridge/bridge-outbox-drain-hook.js +6 -6
- package/dist/runtime/subsystems/bridge/bridge.module.js +17 -17
- package/dist/runtime/subsystems/bridge/event-flow.service.js +2 -2
- package/dist/runtime/subsystems/bridge/index.js +24 -24
- package/dist/runtime/subsystems/cache/cache.module.js +1 -1
- package/dist/runtime/subsystems/cache/index.js +3 -3
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +3 -3
- package/dist/runtime/subsystems/events/event-bus.memory-backend.js +2 -2
- package/dist/runtime/subsystems/events/events.module.js +7 -7
- package/dist/runtime/subsystems/events/generated/bus.js +2 -2
- package/dist/runtime/subsystems/events/generated/index.js +2 -2
- package/dist/runtime/subsystems/events/index.js +10 -10
- package/dist/runtime/subsystems/index.js +90 -90
- 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/runtime/subsystems/jobs/index.js +29 -29
- package/dist/runtime/subsystems/jobs/job-orchestrator.bullmq-backend.js +5 -5
- package/dist/runtime/subsystems/jobs/job-orchestrator.drizzle-backend.js +3 -3
- package/dist/runtime/subsystems/jobs/job-run-service.drizzle-backend.js +3 -3
- package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js +2 -2
- package/dist/runtime/subsystems/jobs/job-worker.js +3 -3
- package/dist/runtime/subsystems/jobs/job-worker.module.js +10 -10
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js +8 -8
- package/dist/runtime/subsystems/observability/index.js +3 -3
- package/dist/runtime/subsystems/observability/observability.module.js +3 -3
- package/dist/runtime/subsystems/observability/observability.service.js +2 -2
- package/dist/runtime/subsystems/storage/index.js +4 -4
- package/dist/runtime/subsystems/storage/storage.module.js +2 -2
- package/dist/src/cli/index.js +1415 -252
- 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-65MO75WM.js.map → chunk-2VGVSL2D.js.map} +0 -0
- /package/dist/{chunk-VNBC3VXM.js.map → chunk-3A34R6CI.js.map} +0 -0
- /package/dist/{chunk-I6UXRJ3Q.js.map → chunk-43SBT72G.js.map} +0 -0
- /package/dist/{chunk-7OVCARTQ.js.map → chunk-5RT7JGKT.js.map} +0 -0
- /package/dist/{chunk-E6PLM6QG.js.map → chunk-7MMS36AN.js.map} +0 -0
- /package/dist/{chunk-4MVGAMUA.js.map → chunk-BK5ICA2F.js.map} +0 -0
- /package/dist/{chunk-VDL5CJ5C.js.map → chunk-C5E7H553.js.map} +0 -0
- /package/dist/{chunk-XKWOJZZ4.js.map → chunk-E45CSC33.js.map} +0 -0
- /package/dist/{chunk-AZLUWG5S.js.map → chunk-EWYI5GGJ.js.map} +0 -0
- /package/dist/{chunk-BHZP6LOV.js.map → chunk-IN3EWFB4.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-Z7PQCAVK.js.map → chunk-LQ6PYFU6.js.map} +0 -0
- /package/dist/{chunk-T6SCOJF4.js.map → chunk-NXHL5YII.js.map} +0 -0
- /package/dist/{chunk-ATVGYF3D.js.map → chunk-PKDS6QIJ.js.map} +0 -0
- /package/dist/{chunk-OZEPJGMA.js.map → chunk-R4BPUUB5.js.map} +0 -0
- /package/dist/{chunk-B34G6PHD.js.map → chunk-RKNW56RU.js.map} +0 -0
- /package/dist/{chunk-R6F6KFIL.js.map → chunk-TBGTMALE.js.map} +0 -0
- /package/dist/{chunk-GM3RMJIJ.js.map → chunk-VHAR2BGH.js.map} +0 -0
- /package/dist/{chunk-CLWBNXKF.js.map → chunk-W2UIDI3R.js.map} +0 -0
- /package/dist/{chunk-235ZMMJR.js.map → chunk-X6BP6LI5.js.map} +0 -0
- /package/dist/{chunk-KZDHMZ45.js.map → chunk-YZLBU6O2.js.map} +0 -0
package/dist/src/cli/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
loadEntities,
|
|
23
23
|
loadEntitiesFromYaml,
|
|
24
24
|
loadEntityFromYaml,
|
|
25
|
+
loadEntityRegistry,
|
|
25
26
|
loadEventFromYaml,
|
|
26
27
|
loadJunctionFromYaml,
|
|
27
28
|
loadProvidersFromYaml,
|
|
@@ -36,35 +37,35 @@ import {
|
|
|
36
37
|
validateOrchestrationProject,
|
|
37
38
|
validateProviders,
|
|
38
39
|
writeManifest
|
|
39
|
-
} from "../../chunk-
|
|
40
|
+
} from "../../chunk-CFFTPWHM.js";
|
|
40
41
|
import "../../chunk-KVOWSC5S.js";
|
|
41
|
-
import "../../chunk-
|
|
42
|
+
import "../../chunk-PKDS6QIJ.js";
|
|
43
|
+
import "../../chunk-PRWIX6UW.js";
|
|
42
44
|
import "../../chunk-YK5JEVLX.js";
|
|
43
45
|
import "../../chunk-EO2QPOKH.js";
|
|
44
|
-
import "../../chunk-
|
|
46
|
+
import "../../chunk-SQDOBLBP.js";
|
|
47
|
+
import "../../chunk-TDEHU73T.js";
|
|
48
|
+
import "../../chunk-LG57S2SC.js";
|
|
45
49
|
import "../../chunk-XWBK3XJK.js";
|
|
50
|
+
import "../../chunk-S7C6TIIF.js";
|
|
51
|
+
import "../../chunk-MZ6GV4YF.js";
|
|
52
|
+
import "../../chunk-HNWZFNKP.js";
|
|
46
53
|
import "../../chunk-AHV4GDYM.js";
|
|
47
|
-
import "../../chunk-
|
|
54
|
+
import "../../chunk-43SBT72G.js";
|
|
55
|
+
import "../../chunk-4MF3HKJA.js";
|
|
56
|
+
import "../../chunk-TIZXQU26.js";
|
|
48
57
|
import "../../chunk-JEINYUJH.js";
|
|
49
58
|
import {
|
|
50
59
|
isDivisibleCursor
|
|
51
60
|
} from "../../chunk-5TK7MEN4.js";
|
|
52
61
|
import "../../chunk-4KNXX6TI.js";
|
|
53
62
|
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
63
|
import "../../chunk-U64T4YZE.js";
|
|
63
64
|
import "../../chunk-2E224ZSN.js";
|
|
64
65
|
|
|
65
66
|
// src/cli/index.ts
|
|
66
67
|
import { readFileSync as readFileSync6 } from "fs";
|
|
67
|
-
import { join as
|
|
68
|
+
import { join as join17 } from "path";
|
|
68
69
|
import { Builtins, Cli, Command as Command13 } from "clipanion";
|
|
69
70
|
|
|
70
71
|
// src/cli/noun-module.ts
|
|
@@ -1099,7 +1100,7 @@ var icons = {
|
|
|
1099
1100
|
|
|
1100
1101
|
// src/cli/commands/entity.ts
|
|
1101
1102
|
import fs10 from "fs";
|
|
1102
|
-
import
|
|
1103
|
+
import path14 from "path";
|
|
1103
1104
|
import { Command as Command2, Option as Option2 } from "clipanion";
|
|
1104
1105
|
|
|
1105
1106
|
// src/cli/shared/hygen.ts
|
|
@@ -3847,8 +3848,8 @@ function generateIntegrationAggregator(surface, entries) {
|
|
|
3847
3848
|
);
|
|
3848
3849
|
const importLines = sorted.map((e) => {
|
|
3849
3850
|
const cls = assemblyModuleClass(e.entityName, e.provider);
|
|
3850
|
-
const
|
|
3851
|
-
return `import { ${cls} } from '${
|
|
3851
|
+
const path36 = `./modules/${e.provider}/${e.entityName}-integration.module`;
|
|
3852
|
+
return `import { ${cls} } from '${path36}';`;
|
|
3852
3853
|
}).join("\n");
|
|
3853
3854
|
const membersInline = moduleClasses.join(", ");
|
|
3854
3855
|
return `${generatedBanner(`surface: ${surface}`)}
|
|
@@ -4472,9 +4473,9 @@ function emitAdapters(opts) {
|
|
|
4472
4473
|
[aggregatorPath, generateSurfaceAggregator(surface, slugs, mode)],
|
|
4473
4474
|
[typedViewPath, generateTypedView(surface, slugs, entitiesBySurface.get(surface) ?? [])]
|
|
4474
4475
|
];
|
|
4475
|
-
for (const [
|
|
4476
|
-
if (!opts.dryRun) writeIfChanged(
|
|
4477
|
-
result.written.push(
|
|
4476
|
+
for (const [path36, content] of files) {
|
|
4477
|
+
if (!opts.dryRun) writeIfChanged(path36, content);
|
|
4478
|
+
result.written.push(path36);
|
|
4478
4479
|
}
|
|
4479
4480
|
if (opts.backendSrcAbs) {
|
|
4480
4481
|
const aliases = opts.aliases ?? {};
|
|
@@ -4819,15 +4820,1067 @@ function relativeSource(filePath) {
|
|
|
4819
4820
|
return slash === -1 ? filePath : `definitions/providers/${filePath.slice(slash + 1)}`;
|
|
4820
4821
|
}
|
|
4821
4822
|
|
|
4822
|
-
// src/
|
|
4823
|
+
// src/emitters/frontend/emit-base.ts
|
|
4824
|
+
import { join as join10 } from "path";
|
|
4825
|
+
|
|
4826
|
+
// src/emitters/frontend/types.ts
|
|
4827
|
+
function sortEntities(entities) {
|
|
4828
|
+
return [...entities].sort((a, b) => a.name.localeCompare(b.name));
|
|
4829
|
+
}
|
|
4830
|
+
function resolveSyncMode(entity, config) {
|
|
4831
|
+
return entity.sync ?? config.globalSyncMode;
|
|
4832
|
+
}
|
|
4833
|
+
|
|
4834
|
+
// src/emitters/frontend/emit-utils.ts
|
|
4835
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
4836
|
+
import { dirname as dirname3 } from "path";
|
|
4837
|
+
function generatedBanner3(sourceDesc) {
|
|
4838
|
+
return `// @generated by @pattern-stack/codegen from ${sourceDesc} \u2014 DO NOT EDIT.
|
|
4839
|
+
// Hand edits are overwritten on re-emit. Regenerate with \`bun run codegen\`.`;
|
|
4840
|
+
}
|
|
4841
|
+
function withBanner(sourceDesc, body) {
|
|
4842
|
+
return `${generatedBanner3(sourceDesc)}
|
|
4843
|
+
|
|
4844
|
+
${body}`;
|
|
4845
|
+
}
|
|
4846
|
+
function writeFile2(outPath, content) {
|
|
4847
|
+
mkdirSync3(dirname3(outPath), { recursive: true });
|
|
4848
|
+
writeFileSync3(outPath, content);
|
|
4849
|
+
}
|
|
4850
|
+
|
|
4851
|
+
// src/emitters/frontend/emit-base.ts
|
|
4852
|
+
var SOURCE_DESC = "the entity set";
|
|
4853
|
+
function buildQueryClientFile() {
|
|
4854
|
+
const body = `import { QueryClient } from '@tanstack/react-query';
|
|
4855
|
+
|
|
4856
|
+
/**
|
|
4857
|
+
* Shared QueryClient for REST-backed (\`api\` sync mode) collections.
|
|
4858
|
+
*
|
|
4859
|
+
* Replaceable: swap this for your app's own QueryClient if you already create
|
|
4860
|
+
* one \u2014 every generated collection imports \`queryClient\` from this module.
|
|
4861
|
+
*/
|
|
4862
|
+
export const queryClient = new QueryClient({
|
|
4863
|
+
defaultOptions: {
|
|
4864
|
+
queries: {
|
|
4865
|
+
staleTime: 60 * 1000, // 60s
|
|
4866
|
+
gcTime: 5 * 60 * 1000, // 5m
|
|
4867
|
+
},
|
|
4868
|
+
},
|
|
4869
|
+
});
|
|
4870
|
+
`;
|
|
4871
|
+
return withBanner(SOURCE_DESC, body);
|
|
4872
|
+
}
|
|
4873
|
+
function buildConfigFile(ctx) {
|
|
4874
|
+
const entities = sortEntities(ctx.entities);
|
|
4875
|
+
const entityNameUnion = entities.length > 0 ? entities.map((e) => `'${e.name}'`).join(" | ") : "string";
|
|
4876
|
+
const defaultEntries = entities.map((e) => ` ${e.name}: { mode: '${resolveSyncMode(e, ctx.config)}' },`).join("\n");
|
|
4877
|
+
const body = `/**
|
|
4878
|
+
* Per-entity sync configuration + runtime overrides.
|
|
4879
|
+
*
|
|
4880
|
+
* \`mode\` selects the collection backing for an entity:
|
|
4881
|
+
* - 'electric' \u2192 real-time shape sync (electricCollectionOptions)
|
|
4882
|
+
* - 'api' \u2192 REST via TanStack Query (queryCollectionOptions)
|
|
4883
|
+
*
|
|
4884
|
+
* The offline mode (Electric + Dexie) is deferred \u2014 see
|
|
4885
|
+
* docs/specs/2026-06-04-frontend-pipeline-rebuild.md OQ-6.
|
|
4886
|
+
*/
|
|
4887
|
+
|
|
4888
|
+
export type SyncMode = 'api' | 'electric';
|
|
4889
|
+
|
|
4890
|
+
export type EntityName = ${entityNameUnion};
|
|
4891
|
+
|
|
4892
|
+
export interface EntitySyncConfig {
|
|
4893
|
+
mode: SyncMode;
|
|
4894
|
+
}
|
|
4895
|
+
|
|
4896
|
+
/** Resolved per-entity sync modes (per-entity \`sync:\` over global default). */
|
|
4897
|
+
export const defaultConfig: Record<EntityName, EntitySyncConfig> = {
|
|
4898
|
+
${defaultEntries}
|
|
4899
|
+
};
|
|
4900
|
+
|
|
4901
|
+
/** Runtime overrides, layered over \`defaultConfig\`. */
|
|
4902
|
+
const overrides: Partial<Record<EntityName, EntitySyncConfig>> = {};
|
|
4903
|
+
|
|
4904
|
+
/** Resolve an entity's effective sync mode (override wins over default). */
|
|
4905
|
+
export function getSyncMode(entity: EntityName): SyncMode {
|
|
4906
|
+
return (overrides[entity] ?? defaultConfig[entity]).mode;
|
|
4907
|
+
}
|
|
4908
|
+
|
|
4909
|
+
/** Override an entity's sync config at runtime. */
|
|
4910
|
+
export function setEntityConfig(entity: EntityName, config: EntitySyncConfig): void {
|
|
4911
|
+
overrides[entity] = config;
|
|
4912
|
+
}
|
|
4913
|
+
`;
|
|
4914
|
+
return withBanner(SOURCE_DESC, body);
|
|
4915
|
+
}
|
|
4916
|
+
function emitBase(ctx, outDir) {
|
|
4917
|
+
const written = [];
|
|
4918
|
+
const queryClientPath = join10(outDir, "query-client.ts");
|
|
4919
|
+
writeFile2(queryClientPath, buildQueryClientFile());
|
|
4920
|
+
written.push(queryClientPath);
|
|
4921
|
+
const configPath = join10(outDir, "config.ts");
|
|
4922
|
+
writeFile2(configPath, buildConfigFile(ctx));
|
|
4923
|
+
written.push(configPath);
|
|
4924
|
+
return written;
|
|
4925
|
+
}
|
|
4926
|
+
|
|
4927
|
+
// src/emitters/frontend/emit-api.ts
|
|
4928
|
+
import { join as join11 } from "path";
|
|
4929
|
+
var SOURCE_DESC_SET = "the entity set";
|
|
4930
|
+
function updateVerb(architecture) {
|
|
4931
|
+
return architecture === "clean-lite-ps" ? "PATCH" : "PUT";
|
|
4932
|
+
}
|
|
4933
|
+
function buildClientFile(ctx) {
|
|
4934
|
+
const { config } = ctx;
|
|
4935
|
+
const importLines = [];
|
|
4936
|
+
if (config.apiBaseUrlImport) {
|
|
4937
|
+
importLines.push(`import { API_BASE_URL } from '${config.apiBaseUrlImport}';`);
|
|
4938
|
+
}
|
|
4939
|
+
if (config.authFunction) {
|
|
4940
|
+
importLines.push(`import { ${config.authFunction} } from '${config.authImport}';`);
|
|
4941
|
+
}
|
|
4942
|
+
const baseUrlConst = config.apiBaseUrlImport ? "const BASE_URL = API_BASE_URL;" : `const BASE_URL = '${config.apiUrl}';`;
|
|
4943
|
+
const authHeaderBlock = config.authFunction ? ` const headers: Record<string, string> = {
|
|
4944
|
+
'Content-Type': 'application/json',
|
|
4945
|
+
Authorization: ${config.authFunction}(),
|
|
4946
|
+
};` : ` const headers: Record<string, string> = {
|
|
4947
|
+
'Content-Type': 'application/json',
|
|
4948
|
+
};`;
|
|
4949
|
+
const imports = importLines.length > 0 ? `${importLines.join("\n")}
|
|
4950
|
+
|
|
4951
|
+
` : "";
|
|
4952
|
+
const body = `${imports}${baseUrlConst}
|
|
4953
|
+
|
|
4954
|
+
/**
|
|
4955
|
+
* Base REST transport for \`api\` sync-mode collections and entity api clients.
|
|
4956
|
+
* Throws on non-2xx; returns parsed JSON, or \`undefined\` for 204 No Content.
|
|
4957
|
+
*/
|
|
4958
|
+
export async function request<T>(
|
|
4959
|
+
method: string,
|
|
4960
|
+
path: string,
|
|
4961
|
+
body?: unknown,
|
|
4962
|
+
): Promise<T> {
|
|
4963
|
+
${authHeaderBlock}
|
|
4964
|
+
|
|
4965
|
+
const res = await fetch(\`\${BASE_URL}\${path}\`, {
|
|
4966
|
+
method,
|
|
4967
|
+
headers,
|
|
4968
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
4969
|
+
});
|
|
4970
|
+
|
|
4971
|
+
if (!res.ok) {
|
|
4972
|
+
throw new Error(\`\${method} \${path} \u2192 \${res.status} \${res.statusText}\`);
|
|
4973
|
+
}
|
|
4974
|
+
|
|
4975
|
+
if (res.status === 204) {
|
|
4976
|
+
return undefined as T;
|
|
4977
|
+
}
|
|
4978
|
+
|
|
4979
|
+
return res.json() as Promise<T>;
|
|
4980
|
+
}
|
|
4981
|
+
`;
|
|
4982
|
+
return withBanner(SOURCE_DESC_SET, body);
|
|
4983
|
+
}
|
|
4984
|
+
function buildEntityApiFile(entity, ctx) {
|
|
4985
|
+
const { config } = ctx;
|
|
4986
|
+
const { camelName, plural, className, name } = entity;
|
|
4987
|
+
const verb = updateVerb(config.architecture);
|
|
4988
|
+
const body = `import { request } from './client';
|
|
4989
|
+
import type { ${className} } from '${config.dbEntitiesImport}/${name}';
|
|
4990
|
+
|
|
4991
|
+
export const ${camelName}Api = {
|
|
4992
|
+
list: (): Promise<${className}[]> => request<${className}[]>('GET', '/${plural}'),
|
|
4993
|
+
|
|
4994
|
+
get: (id: string): Promise<${className}> =>
|
|
4995
|
+
request<${className}>('GET', \`/${plural}/\${id}\`),
|
|
4996
|
+
|
|
4997
|
+
create: (data: Partial<${className}>): Promise<${className}> =>
|
|
4998
|
+
request<${className}>('POST', '/${plural}', data),
|
|
4999
|
+
|
|
5000
|
+
update: (id: string, data: Partial<${className}>): Promise<${className}> =>
|
|
5001
|
+
request<${className}>('${verb}', \`/${plural}/\${id}\`, data),
|
|
5002
|
+
|
|
5003
|
+
delete: (id: string): Promise<void> =>
|
|
5004
|
+
request<void>('DELETE', \`/${plural}/\${id}\`),
|
|
5005
|
+
};
|
|
5006
|
+
`;
|
|
5007
|
+
return withBanner(`entities/${name}.yaml`, body);
|
|
5008
|
+
}
|
|
5009
|
+
function buildApiIndexFile(ctx) {
|
|
5010
|
+
const entities = sortEntities(ctx.entities);
|
|
5011
|
+
const lines = [
|
|
5012
|
+
"export * from './client';",
|
|
5013
|
+
...entities.map((e) => `export * from './${e.name}';`)
|
|
5014
|
+
];
|
|
5015
|
+
return withBanner(SOURCE_DESC_SET, `${lines.join("\n")}
|
|
5016
|
+
`);
|
|
5017
|
+
}
|
|
5018
|
+
function emitApi(ctx, outDir) {
|
|
5019
|
+
const apiDir = join11(outDir, "api");
|
|
5020
|
+
const entities = sortEntities(ctx.entities);
|
|
5021
|
+
const written = [];
|
|
5022
|
+
const clientPath = join11(apiDir, "client.ts");
|
|
5023
|
+
writeFile2(clientPath, buildClientFile(ctx));
|
|
5024
|
+
written.push(clientPath);
|
|
5025
|
+
for (const entity of entities) {
|
|
5026
|
+
const entityPath = join11(apiDir, `${entity.name}.ts`);
|
|
5027
|
+
writeFile2(entityPath, buildEntityApiFile(entity, ctx));
|
|
5028
|
+
written.push(entityPath);
|
|
5029
|
+
}
|
|
5030
|
+
const indexPath = join11(apiDir, "index.ts");
|
|
5031
|
+
writeFile2(indexPath, buildApiIndexFile(ctx));
|
|
5032
|
+
written.push(indexPath);
|
|
5033
|
+
return written;
|
|
5034
|
+
}
|
|
5035
|
+
|
|
5036
|
+
// src/emitters/frontend/emit-collections.ts
|
|
5037
|
+
import { join as join12 } from "path";
|
|
5038
|
+
var SOURCE_DESC_SET2 = "the entity set";
|
|
5039
|
+
function baseUrlExpr(base, plural, apiBaseUrlImport) {
|
|
5040
|
+
return apiBaseUrlImport ? `\`\${API_BASE_URL}/${plural}\`` : `\`${base}/${plural}\``;
|
|
5041
|
+
}
|
|
5042
|
+
var SSR_ORIGIN_EXPR = "typeof window !== 'undefined' ? window.location.origin : ''";
|
|
5043
|
+
function buildElectricCollection(entity, ctx) {
|
|
5044
|
+
const { config } = ctx;
|
|
5045
|
+
const { camelName, plural, name } = entity;
|
|
5046
|
+
const imports = [
|
|
5047
|
+
"import { electricCollectionOptions } from '@tanstack/electric-db-collection';",
|
|
5048
|
+
"import { createCollection } from '@tanstack/react-db';"
|
|
5049
|
+
];
|
|
5050
|
+
if (config.columnMapper) {
|
|
5051
|
+
imports.push(`import { ${config.columnMapper} } from '@electric-sql/client';`);
|
|
5052
|
+
}
|
|
5053
|
+
if (config.authFunction) {
|
|
5054
|
+
imports.push(`import { ${config.authFunction} } from '${config.authImport}';`);
|
|
5055
|
+
}
|
|
5056
|
+
if (config.apiBaseUrlImport) {
|
|
5057
|
+
imports.push(`import { API_BASE_URL } from '${config.apiBaseUrlImport}';`);
|
|
5058
|
+
}
|
|
5059
|
+
imports.push(`import { ${camelName}Schema } from '${config.dbEntitiesImport}/${name}';`);
|
|
5060
|
+
let urlBlock;
|
|
5061
|
+
if (config.useTableParam) {
|
|
5062
|
+
urlBlock = ` url: new URL(
|
|
5063
|
+
'${config.shapeUrl}',
|
|
5064
|
+
${SSR_ORIGIN_EXPR},
|
|
5065
|
+
).toString(),
|
|
5066
|
+
params: {
|
|
5067
|
+
table: '${plural}',
|
|
5068
|
+
},`;
|
|
5069
|
+
} else {
|
|
5070
|
+
const shapeUrl = baseUrlExpr(config.shapeUrl, plural, config.apiBaseUrlImport);
|
|
5071
|
+
urlBlock = ` url: new URL(
|
|
5072
|
+
${shapeUrl},
|
|
5073
|
+
${SSR_ORIGIN_EXPR},
|
|
5074
|
+
).toString(),`;
|
|
5075
|
+
}
|
|
5076
|
+
const headersBlock = config.authFunction ? `
|
|
5077
|
+
headers: {
|
|
5078
|
+
Authorization: ${config.authFunction}(),
|
|
5079
|
+
},` : "";
|
|
5080
|
+
const parserEntries = Object.entries(config.parsers).map(([type, fn]) => ` ${type}: ${fn},`).join("\n");
|
|
5081
|
+
const parserBlock = parserEntries ? `
|
|
5082
|
+
parser: {
|
|
5083
|
+
${parserEntries}
|
|
5084
|
+
},` : `
|
|
5085
|
+
parser: {},`;
|
|
5086
|
+
let columnMapperBlock = "";
|
|
5087
|
+
if (config.columnMapper) {
|
|
5088
|
+
const mapperExpr = config.columnMapperNeedsCall ? `${config.columnMapper}()` : config.columnMapper;
|
|
5089
|
+
columnMapperBlock = `
|
|
5090
|
+
columnMapper: ${mapperExpr},`;
|
|
5091
|
+
}
|
|
5092
|
+
const body = `${imports.join("\n")}
|
|
5093
|
+
|
|
5094
|
+
export const ${camelName}Collection = createCollection(
|
|
5095
|
+
electricCollectionOptions({
|
|
5096
|
+
id: '${plural}',
|
|
5097
|
+
shapeOptions: {
|
|
5098
|
+
${urlBlock}${headersBlock}${parserBlock}${columnMapperBlock}
|
|
5099
|
+
},
|
|
5100
|
+
schema: ${camelName}Schema,
|
|
5101
|
+
getKey: (item) => item.id,
|
|
5102
|
+
}),
|
|
5103
|
+
);
|
|
5104
|
+
`;
|
|
5105
|
+
return withBanner(`entities/${name}.yaml`, body);
|
|
5106
|
+
}
|
|
5107
|
+
function buildApiCollection(entity, ctx) {
|
|
5108
|
+
const { config } = ctx;
|
|
5109
|
+
const { camelName, plural, name } = entity;
|
|
5110
|
+
const imports = [
|
|
5111
|
+
"import { queryCollectionOptions } from '@tanstack/query-db-collection';",
|
|
5112
|
+
"import { createCollection } from '@tanstack/react-db';",
|
|
5113
|
+
"import { queryClient } from '../query-client';",
|
|
5114
|
+
`import { ${camelName}Api } from '../api/${name}';`,
|
|
5115
|
+
`import { ${camelName}Schema } from '${config.dbEntitiesImport}/${name}';`
|
|
5116
|
+
];
|
|
5117
|
+
const body = `${imports.join("\n")}
|
|
5118
|
+
|
|
5119
|
+
export const ${camelName}Collection = createCollection(
|
|
5120
|
+
queryCollectionOptions({
|
|
5121
|
+
id: '${plural}',
|
|
5122
|
+
queryKey: ['${plural}'],
|
|
5123
|
+
queryClient,
|
|
5124
|
+
queryFn: () => ${camelName}Api.list(),
|
|
5125
|
+
getKey: (item) => item.id,
|
|
5126
|
+
schema: ${camelName}Schema,
|
|
5127
|
+
}),
|
|
5128
|
+
);
|
|
5129
|
+
`;
|
|
5130
|
+
return withBanner(`entities/${name}.yaml`, body);
|
|
5131
|
+
}
|
|
5132
|
+
function buildCollectionFile(entity, ctx) {
|
|
5133
|
+
const mode = resolveSyncMode(entity, ctx.config);
|
|
5134
|
+
return mode === "api" ? buildApiCollection(entity, ctx) : buildElectricCollection(entity, ctx);
|
|
5135
|
+
}
|
|
5136
|
+
function buildCollectionsIndexFile(ctx) {
|
|
5137
|
+
const entities = sortEntities(ctx.entities);
|
|
5138
|
+
const lines = entities.map((e) => `export * from './${e.name}';`);
|
|
5139
|
+
return withBanner(SOURCE_DESC_SET2, `${lines.join("\n")}
|
|
5140
|
+
`);
|
|
5141
|
+
}
|
|
5142
|
+
function emitCollections(ctx, outDir) {
|
|
5143
|
+
const collectionsDir = join12(outDir, "collections");
|
|
5144
|
+
const entities = sortEntities(ctx.entities);
|
|
5145
|
+
const written = [];
|
|
5146
|
+
for (const entity of entities) {
|
|
5147
|
+
const filePath = join12(collectionsDir, `${entity.name}.ts`);
|
|
5148
|
+
writeFile2(filePath, buildCollectionFile(entity, ctx));
|
|
5149
|
+
written.push(filePath);
|
|
5150
|
+
}
|
|
5151
|
+
const indexPath = join12(collectionsDir, "index.ts");
|
|
5152
|
+
writeFile2(indexPath, buildCollectionsIndexFile(ctx));
|
|
5153
|
+
written.push(indexPath);
|
|
5154
|
+
return written;
|
|
5155
|
+
}
|
|
5156
|
+
|
|
5157
|
+
// src/emitters/frontend/emit-entities.ts
|
|
5158
|
+
import { join as join13 } from "path";
|
|
5159
|
+
var SOURCE_DESC_SET3 = "the entity set";
|
|
5160
|
+
function buildEntityHooksFile(entity, ctx) {
|
|
5161
|
+
const { camelName, className, name } = entity;
|
|
5162
|
+
const body = `import { createEntityHooks } from '@pattern-stack/frontend-patterns';
|
|
5163
|
+
import { ${camelName}Collection } from '../collections/${name}';
|
|
5164
|
+
import { ${camelName}Api } from '../api/${name}';
|
|
5165
|
+
import { getSyncMode } from '../config';
|
|
5166
|
+
import type { ${className} } from '${ctx.config.dbEntitiesImport}/${name}';
|
|
5167
|
+
|
|
5168
|
+
/**
|
|
5169
|
+
* Typed hooks for ${className}, wired via the framework factory.
|
|
5170
|
+
*
|
|
5171
|
+
* \`localFirst\` is resolved at call time from the entity's runtime sync mode
|
|
5172
|
+
* (\`getSyncMode('${name}')\`) \u2014 \`api\` mode is confirmed-write, everything else
|
|
5173
|
+
* is local-first (optimistic).
|
|
5174
|
+
*/
|
|
5175
|
+
export const ${camelName}Hooks = createEntityHooks<${className}>({
|
|
5176
|
+
name: '${name}',
|
|
5177
|
+
collection: ${camelName}Collection,
|
|
5178
|
+
api: ${camelName}Api,
|
|
5179
|
+
localFirst: () => getSyncMode('${name}') !== 'api',
|
|
5180
|
+
});
|
|
5181
|
+
|
|
5182
|
+
// Per-entity hook re-exports for direct imports.
|
|
5183
|
+
export const {
|
|
5184
|
+
useList: use${className}List,
|
|
5185
|
+
useGet: use${className},
|
|
5186
|
+
useCreate: useCreate${className},
|
|
5187
|
+
useUpdate: useUpdate${className},
|
|
5188
|
+
useDelete: useDelete${className},
|
|
5189
|
+
keys: ${camelName}Keys,
|
|
5190
|
+
} = ${camelName}Hooks;
|
|
5191
|
+
`;
|
|
5192
|
+
return withBanner(`entities/${name}.yaml`, body);
|
|
5193
|
+
}
|
|
5194
|
+
function buildEntitiesIndexFile(ctx) {
|
|
5195
|
+
const entities = sortEntities(ctx.entities);
|
|
5196
|
+
const blocks = entities.map((e) => {
|
|
5197
|
+
const { camelName, className, name } = e;
|
|
5198
|
+
return `export {
|
|
5199
|
+
${camelName}Hooks,
|
|
5200
|
+
use${className}List,
|
|
5201
|
+
use${className},
|
|
5202
|
+
useCreate${className},
|
|
5203
|
+
useUpdate${className},
|
|
5204
|
+
useDelete${className},
|
|
5205
|
+
${camelName}Keys,
|
|
5206
|
+
} from './${name}';`;
|
|
5207
|
+
});
|
|
5208
|
+
return withBanner(SOURCE_DESC_SET3, `${blocks.join("\n\n")}
|
|
5209
|
+
`);
|
|
5210
|
+
}
|
|
5211
|
+
function emitEntities(ctx, outDir) {
|
|
5212
|
+
const entitiesDir = join13(outDir, "entities");
|
|
5213
|
+
const entities = sortEntities(ctx.entities);
|
|
5214
|
+
const written = [];
|
|
5215
|
+
for (const entity of entities) {
|
|
5216
|
+
const filePath = join13(entitiesDir, `${entity.name}.ts`);
|
|
5217
|
+
writeFile2(filePath, buildEntityHooksFile(entity, ctx));
|
|
5218
|
+
written.push(filePath);
|
|
5219
|
+
}
|
|
5220
|
+
const indexPath = join13(entitiesDir, "index.ts");
|
|
5221
|
+
writeFile2(indexPath, buildEntitiesIndexFile(ctx));
|
|
5222
|
+
written.push(indexPath);
|
|
5223
|
+
return written;
|
|
5224
|
+
}
|
|
5225
|
+
|
|
5226
|
+
// src/emitters/frontend/emit-store.ts
|
|
5227
|
+
import { join as join14 } from "path";
|
|
5228
|
+
var SOURCE_DESC_SET4 = "the entity set";
|
|
5229
|
+
var CAMEL = (s) => s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
5230
|
+
function resolvableRels(entity, ctx) {
|
|
5231
|
+
const parsed = ctx.parsed.get(entity.name);
|
|
5232
|
+
if (!parsed) return [];
|
|
5233
|
+
const registryByName = new Map(ctx.entities.map((e) => [e.name, e]));
|
|
5234
|
+
const out = [];
|
|
5235
|
+
for (const rel2 of parsed.relationships.values()) {
|
|
5236
|
+
if (rel2.type !== "belongs_to") continue;
|
|
5237
|
+
const target = registryByName.get(rel2.target);
|
|
5238
|
+
if (!target) continue;
|
|
5239
|
+
out.push({
|
|
5240
|
+
propertyName: CAMEL(rel2.name),
|
|
5241
|
+
fieldNameCamel: CAMEL(fkField(rel2)),
|
|
5242
|
+
target
|
|
5243
|
+
});
|
|
5244
|
+
}
|
|
5245
|
+
out.sort((a, b) => a.propertyName.localeCompare(b.propertyName));
|
|
5246
|
+
return out;
|
|
5247
|
+
}
|
|
5248
|
+
function fkField(rel2) {
|
|
5249
|
+
return rel2.foreignKey && rel2.foreignKey.length > 0 ? rel2.foreignKey : `${rel2.target}_id`;
|
|
5250
|
+
}
|
|
5251
|
+
function buildStoreIndexFile(ctx) {
|
|
5252
|
+
const entities = sortEntities(ctx.entities);
|
|
5253
|
+
const hookImports = entities.map((e) => `import { ${e.camelName}Hooks } from '../entities/${e.name}';`).join("\n");
|
|
5254
|
+
const collectionImports = entities.map((e) => `import { ${e.camelName}Collection } from '../collections/${e.name}';`).join("\n");
|
|
5255
|
+
const entityEntries = entities.map((e) => ` ${e.plural}: ${e.camelName}Hooks,`).join("\n");
|
|
5256
|
+
const collectionEntries = entities.map((e) => ` ${e.plural}: ${e.camelName}Collection,`).join("\n");
|
|
5257
|
+
const body = `import { createStore } from '@pattern-stack/frontend-patterns';
|
|
5258
|
+
|
|
5259
|
+
${hookImports}
|
|
5260
|
+
|
|
5261
|
+
${collectionImports}
|
|
5262
|
+
|
|
5263
|
+
/**
|
|
5264
|
+
* The application store \u2014 unified access to every entity.
|
|
5265
|
+
*
|
|
5266
|
+
* Entities and collections are keyed by their plural name:
|
|
5267
|
+
* store.${entities[0]?.plural ?? "things"}.useList()
|
|
5268
|
+
* store.resolve.<entity>(id)
|
|
5269
|
+
* store.lookups.build()
|
|
5270
|
+
*/
|
|
5271
|
+
export const store = createStore({
|
|
5272
|
+
entities: {
|
|
5273
|
+
${entityEntries}
|
|
5274
|
+
},
|
|
5275
|
+
collections: {
|
|
5276
|
+
${collectionEntries}
|
|
5277
|
+
},
|
|
5278
|
+
});
|
|
5279
|
+
|
|
5280
|
+
/** Store type for the \`useStore\` hook. */
|
|
5281
|
+
export type AppStore = typeof store;
|
|
5282
|
+
`;
|
|
5283
|
+
return withBanner(SOURCE_DESC_SET4, body);
|
|
5284
|
+
}
|
|
5285
|
+
function buildResolversFile(ctx) {
|
|
5286
|
+
const entities = sortEntities(ctx.entities);
|
|
5287
|
+
const collectionImports = entities.map((e) => `import { ${e.camelName}Collection } from '../collections/${e.name}';`).join("\n");
|
|
5288
|
+
const typeImports = entities.map((e) => `import type { ${e.className} } from '${ctx.config.dbEntitiesImport}/${e.name}';`).join("\n");
|
|
5289
|
+
const resolverIface = entities.map(
|
|
5290
|
+
(e) => ` ${e.camelName}: (id: string | null | undefined) => ${e.className} | undefined;`
|
|
5291
|
+
).join("\n");
|
|
5292
|
+
const resolverImpls = entities.map(
|
|
5293
|
+
(e) => ` ${e.camelName}: (id) => {
|
|
5294
|
+
if (!id) return undefined;
|
|
5295
|
+
return ${e.camelName}Collection.state.get(id) as ${e.className} | undefined;
|
|
5296
|
+
},`
|
|
5297
|
+
).join("\n");
|
|
5298
|
+
const refBlocks = [];
|
|
5299
|
+
for (const e of entities) {
|
|
5300
|
+
const rels = resolvableRels(e, ctx);
|
|
5301
|
+
if (rels.length === 0) continue;
|
|
5302
|
+
const refFields = rels.map(
|
|
5303
|
+
(r) => ` ${r.propertyName}: ${r.target.className} | undefined;`
|
|
5304
|
+
).join("\n");
|
|
5305
|
+
const hydrateFields = rels.map(
|
|
5306
|
+
(r) => ` ${r.propertyName}: resolvers.${r.target.camelName}(entity.${r.fieldNameCamel}),`
|
|
5307
|
+
).join("\n");
|
|
5308
|
+
refBlocks.push(`/** Resolved FK references for ${e.className}. */
|
|
5309
|
+
export interface ${e.className}Refs {
|
|
5310
|
+
${refFields}
|
|
5311
|
+
}
|
|
5312
|
+
|
|
5313
|
+
/** Hydrate a ${e.className} with its resolved FK references. */
|
|
5314
|
+
export function resolve${e.className}Refs(
|
|
5315
|
+
entity: ${e.className},
|
|
5316
|
+
resolvers: Resolvers,
|
|
5317
|
+
): ${e.className} & ${e.className}Refs {
|
|
5318
|
+
return {
|
|
5319
|
+
...entity,
|
|
5320
|
+
${hydrateFields}
|
|
5321
|
+
};
|
|
5322
|
+
}`);
|
|
5323
|
+
}
|
|
5324
|
+
const refsSection = refBlocks.length > 0 ? `
|
|
5325
|
+
// ${"=".repeat(73)}
|
|
5326
|
+
// WithResolved helpers \u2014 hydrate entities with resolved FKs
|
|
5327
|
+
// ${"=".repeat(73)}
|
|
5328
|
+
|
|
5329
|
+
${refBlocks.join("\n\n")}
|
|
5330
|
+
` : "";
|
|
5331
|
+
const body = `${collectionImports}
|
|
5332
|
+
${typeImports}
|
|
5333
|
+
|
|
5334
|
+
/**
|
|
5335
|
+
* FK resolvers \u2014 resolve a foreign-key id to the full entity object via the
|
|
5336
|
+
* backing collection's local state (\`O(1)\` \`Map.get\`).
|
|
5337
|
+
*
|
|
5338
|
+
* Usage:
|
|
5339
|
+
* const ${entities[0]?.camelName ?? "thing"} = resolvers.${entities[0]?.camelName ?? "thing"}(other.${entities[0]?.camelName ?? "thing"}Id);
|
|
5340
|
+
*/
|
|
5341
|
+
export interface Resolvers {
|
|
5342
|
+
${resolverIface}
|
|
5343
|
+
}
|
|
5344
|
+
|
|
5345
|
+
/** Build the resolver table over the generated collections. */
|
|
5346
|
+
export function createResolvers(): Resolvers {
|
|
5347
|
+
return {
|
|
5348
|
+
${resolverImpls}
|
|
5349
|
+
};
|
|
5350
|
+
}
|
|
5351
|
+
${refsSection}`;
|
|
5352
|
+
return withBanner(SOURCE_DESC_SET4, body);
|
|
5353
|
+
}
|
|
5354
|
+
function buildLookupsFile(ctx) {
|
|
5355
|
+
const entities = sortEntities(ctx.entities);
|
|
5356
|
+
const collectionImports = entities.map((e) => `import { ${e.camelName}Collection } from '../collections/${e.name}';`).join("\n");
|
|
5357
|
+
const typeImports = entities.map((e) => `import type { ${e.className} } from '${ctx.config.dbEntitiesImport}/${e.name}';`).join("\n");
|
|
5358
|
+
const lookupIface = entities.map((e) => ` ${e.plural}: Map<string, ${e.className}>;`).join("\n");
|
|
5359
|
+
const lookupBuild = entities.map(
|
|
5360
|
+
(e) => ` ${e.plural}: new Map(
|
|
5361
|
+
Array.from(${e.camelName}Collection.state.values()).map((item) => [
|
|
5362
|
+
(item as ${e.className}).id as string,
|
|
5363
|
+
item as ${e.className},
|
|
5364
|
+
]),
|
|
5365
|
+
),`
|
|
5366
|
+
).join("\n");
|
|
5367
|
+
const body = `${collectionImports}
|
|
5368
|
+
${typeImports}
|
|
5369
|
+
|
|
5370
|
+
/** All entity lookup maps, keyed by plural entity name (id \u2192 entity). */
|
|
5371
|
+
export interface EntityLookups {
|
|
5372
|
+
${lookupIface}
|
|
5373
|
+
}
|
|
5374
|
+
|
|
5375
|
+
/** Build fresh lookup maps from current collection state. */
|
|
5376
|
+
export function buildLookups(): EntityLookups {
|
|
5377
|
+
return {
|
|
5378
|
+
${lookupBuild}
|
|
5379
|
+
};
|
|
5380
|
+
}
|
|
5381
|
+
|
|
5382
|
+
/** Caching lookup factory: \`build()\` (re)computes, \`current\` reads, \`clear()\` resets. */
|
|
5383
|
+
export function createLookups() {
|
|
5384
|
+
let cache: EntityLookups | null = null;
|
|
5385
|
+
return {
|
|
5386
|
+
build: (): EntityLookups => {
|
|
5387
|
+
cache = buildLookups();
|
|
5388
|
+
return cache;
|
|
5389
|
+
},
|
|
5390
|
+
get current(): EntityLookups | null {
|
|
5391
|
+
return cache;
|
|
5392
|
+
},
|
|
5393
|
+
clear: (): void => {
|
|
5394
|
+
cache = null;
|
|
5395
|
+
},
|
|
5396
|
+
};
|
|
5397
|
+
}
|
|
5398
|
+
`;
|
|
5399
|
+
return withBanner(SOURCE_DESC_SET4, body);
|
|
5400
|
+
}
|
|
5401
|
+
function buildStoreModuleIndexFile(ctx) {
|
|
5402
|
+
const entities = sortEntities(ctx.entities);
|
|
5403
|
+
const lines = [
|
|
5404
|
+
"export { store, type AppStore } from './index';",
|
|
5405
|
+
"export { createResolvers, type Resolvers } from './resolvers';",
|
|
5406
|
+
"export { buildLookups, createLookups, type EntityLookups } from './lookups';"
|
|
5407
|
+
];
|
|
5408
|
+
const refExports = entities.filter((e) => resolvableRels(e, ctx).length > 0).map(
|
|
5409
|
+
(e) => `export { resolve${e.className}Refs, type ${e.className}Refs } from './resolvers';`
|
|
5410
|
+
);
|
|
5411
|
+
if (refExports.length > 0) {
|
|
5412
|
+
lines.push("", ...refExports);
|
|
5413
|
+
}
|
|
5414
|
+
return withBanner(SOURCE_DESC_SET4, `${lines.join("\n")}
|
|
5415
|
+
`);
|
|
5416
|
+
}
|
|
5417
|
+
function emitStore(ctx, outDir) {
|
|
5418
|
+
const storeDir = join14(outDir, "store");
|
|
5419
|
+
const written = [];
|
|
5420
|
+
const files = [
|
|
5421
|
+
["index.ts", buildStoreIndexFile(ctx)],
|
|
5422
|
+
["resolvers.ts", buildResolversFile(ctx)],
|
|
5423
|
+
["lookups.ts", buildLookupsFile(ctx)],
|
|
5424
|
+
["module-index.ts", buildStoreModuleIndexFile(ctx)]
|
|
5425
|
+
];
|
|
5426
|
+
for (const [fileName, content] of files) {
|
|
5427
|
+
const filePath = join14(storeDir, fileName);
|
|
5428
|
+
writeFile2(filePath, content);
|
|
5429
|
+
written.push(filePath);
|
|
5430
|
+
}
|
|
5431
|
+
return written;
|
|
5432
|
+
}
|
|
5433
|
+
|
|
5434
|
+
// src/emitters/frontend/emit-fields.ts
|
|
5435
|
+
import { join as join15 } from "path";
|
|
5436
|
+
|
|
5437
|
+
// src/emitters/frontend/field-meta.ts
|
|
5438
|
+
var CAMEL2 = (s) => s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
5439
|
+
function formatLabel(fieldName) {
|
|
5440
|
+
return fieldName.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
5441
|
+
}
|
|
5442
|
+
function inferUiType(field) {
|
|
5443
|
+
if (field.ui.type) return field.ui.type;
|
|
5444
|
+
if (Array.isArray(field.choices) && field.choices.length > 0) return "enum";
|
|
5445
|
+
if (field.foreignKey) return "reference";
|
|
5446
|
+
const nameLower = field.name.toLowerCase();
|
|
5447
|
+
if (nameLower.includes("email")) return "email";
|
|
5448
|
+
if (nameLower.includes("url") || nameLower.includes("website")) return "url";
|
|
5449
|
+
if (nameLower.includes("password")) return "password";
|
|
5450
|
+
if (nameLower.includes("price") || nameLower.includes("amount") || nameLower.includes("cost") || nameLower.includes("value") || nameLower.includes("revenue")) {
|
|
5451
|
+
return "money";
|
|
5452
|
+
}
|
|
5453
|
+
if (nameLower.includes("percent") || nameLower.includes("rate")) {
|
|
5454
|
+
return "percentage";
|
|
5455
|
+
}
|
|
5456
|
+
switch (field.type) {
|
|
5457
|
+
case "string":
|
|
5458
|
+
return field.constraints.maxLength && field.constraints.maxLength > 500 ? "textarea" : "text";
|
|
5459
|
+
case "integer":
|
|
5460
|
+
case "decimal":
|
|
5461
|
+
return "number";
|
|
5462
|
+
case "boolean":
|
|
5463
|
+
return "boolean";
|
|
5464
|
+
case "uuid":
|
|
5465
|
+
return "text";
|
|
5466
|
+
case "date":
|
|
5467
|
+
return "date";
|
|
5468
|
+
case "datetime":
|
|
5469
|
+
return "datetime";
|
|
5470
|
+
case "json":
|
|
5471
|
+
return "json";
|
|
5472
|
+
default:
|
|
5473
|
+
return "text";
|
|
5474
|
+
}
|
|
5475
|
+
}
|
|
5476
|
+
function inferUiImportance(field) {
|
|
5477
|
+
if (field.ui.importance) return field.ui.importance;
|
|
5478
|
+
const nameLower = field.name.toLowerCase();
|
|
5479
|
+
if (["id", "created_at", "updated_at", "deleted_at"].includes(nameLower)) {
|
|
5480
|
+
return "tertiary";
|
|
5481
|
+
}
|
|
5482
|
+
if (field.foreignKey && nameLower.endsWith("_id")) return "secondary";
|
|
5483
|
+
if (field.required) return "primary";
|
|
5484
|
+
if (nameLower.includes("name") || nameLower.includes("title")) return "primary";
|
|
5485
|
+
return "secondary";
|
|
5486
|
+
}
|
|
5487
|
+
function isEntityRefField(field) {
|
|
5488
|
+
if (field.type === "entity_ref") return true;
|
|
5489
|
+
return field.name.endsWith("_entity_type") || field.name.endsWith("_entity_id");
|
|
5490
|
+
}
|
|
5491
|
+
function deriveFieldMeta(field) {
|
|
5492
|
+
const hasChoices = Array.isArray(field.choices) && field.choices.length > 0;
|
|
5493
|
+
const meta = {
|
|
5494
|
+
field: CAMEL2(field.name),
|
|
5495
|
+
label: field.ui.label ?? formatLabel(field.name),
|
|
5496
|
+
type: inferUiType(field),
|
|
5497
|
+
importance: inferUiImportance(field),
|
|
5498
|
+
sortable: field.ui.sortable ?? false,
|
|
5499
|
+
filterable: field.ui.filterable ?? false
|
|
5500
|
+
};
|
|
5501
|
+
if (hasChoices) meta.choices = field.choices;
|
|
5502
|
+
if (field.foreignKey) meta.reference = field.foreignKey.table;
|
|
5503
|
+
return meta;
|
|
5504
|
+
}
|
|
5505
|
+
|
|
5506
|
+
// src/emitters/frontend/emit-fields.ts
|
|
5507
|
+
var SOURCE_DESC_SET5 = "the entity set";
|
|
5508
|
+
function buildFieldMetaTypeFile() {
|
|
5509
|
+
const body = `/**
|
|
5510
|
+
* Field metadata types for DataGrid, forms, and admin surfaces.
|
|
5511
|
+
*/
|
|
5512
|
+
|
|
5513
|
+
export type FieldType =
|
|
5514
|
+
| 'text'
|
|
5515
|
+
| 'textarea'
|
|
5516
|
+
| 'number'
|
|
5517
|
+
| 'boolean'
|
|
5518
|
+
| 'date'
|
|
5519
|
+
| 'datetime'
|
|
5520
|
+
| 'email'
|
|
5521
|
+
| 'url'
|
|
5522
|
+
| 'password'
|
|
5523
|
+
| 'money'
|
|
5524
|
+
| 'percentage'
|
|
5525
|
+
| 'json'
|
|
5526
|
+
| 'enum'
|
|
5527
|
+
| 'reference'
|
|
5528
|
+
| 'entity';
|
|
5529
|
+
|
|
5530
|
+
export type FieldImportance = 'primary' | 'secondary' | 'tertiary';
|
|
5531
|
+
|
|
5532
|
+
export interface FieldMeta<T = unknown> {
|
|
5533
|
+
/** Property key on the entity (\`keyof T\` for typed access). */
|
|
5534
|
+
field: keyof T & string;
|
|
5535
|
+
label: string;
|
|
5536
|
+
type: FieldType;
|
|
5537
|
+
importance: FieldImportance;
|
|
5538
|
+
sortable?: boolean;
|
|
5539
|
+
filterable?: boolean;
|
|
5540
|
+
format?: Record<string, unknown>;
|
|
5541
|
+
choices?: string[];
|
|
5542
|
+
reference?: string;
|
|
5543
|
+
}
|
|
5544
|
+
`;
|
|
5545
|
+
return withBanner(SOURCE_DESC_SET5, body);
|
|
5546
|
+
}
|
|
5547
|
+
function hasTimestamps(parsed) {
|
|
5548
|
+
return parsed?.behaviors.includes("timestamps") ?? false;
|
|
5549
|
+
}
|
|
5550
|
+
function displayFields(parsed) {
|
|
5551
|
+
if (!parsed) return [];
|
|
5552
|
+
const out = [];
|
|
5553
|
+
for (const field of parsed.fields.values()) {
|
|
5554
|
+
if (field.name === "id") continue;
|
|
5555
|
+
if (isEntityRefField(field)) continue;
|
|
5556
|
+
out.push(deriveFieldMeta(field));
|
|
5557
|
+
}
|
|
5558
|
+
return out;
|
|
5559
|
+
}
|
|
5560
|
+
function renderFieldMeta(meta) {
|
|
5561
|
+
const lines = [
|
|
5562
|
+
` field: '${meta.field}',`,
|
|
5563
|
+
` label: '${meta.label}',`,
|
|
5564
|
+
` type: '${meta.type}' as FieldType,`,
|
|
5565
|
+
` importance: '${meta.importance}' as FieldImportance,`
|
|
5566
|
+
];
|
|
5567
|
+
if (meta.sortable) lines.push(" sortable: true,");
|
|
5568
|
+
if (meta.filterable) lines.push(" filterable: true,");
|
|
5569
|
+
if (meta.choices) lines.push(` choices: ${JSON.stringify(meta.choices)},`);
|
|
5570
|
+
if (meta.reference) lines.push(` reference: '${meta.reference}',`);
|
|
5571
|
+
return ` ${meta.field}: {
|
|
5572
|
+
${lines.join("\n")}
|
|
5573
|
+
},`;
|
|
5574
|
+
}
|
|
5575
|
+
function humanizeClass(className) {
|
|
5576
|
+
return className.replace(/([A-Z])/g, " $1").trim();
|
|
5577
|
+
}
|
|
5578
|
+
function buildEntityFieldsFile(entity, ctx) {
|
|
5579
|
+
const parsed = ctx.parsed.get(entity.name);
|
|
5580
|
+
const { camelName, className, classNamePlural, name, plural } = entity;
|
|
5581
|
+
const fields = displayFields(parsed);
|
|
5582
|
+
const rels = resolvableRels(entity, ctx);
|
|
5583
|
+
const ts3 = hasTimestamps(parsed);
|
|
5584
|
+
const fieldEntries = fields.map(renderFieldMeta);
|
|
5585
|
+
const relEntries = rels.map(
|
|
5586
|
+
(r) => ` ${r.propertyName}: {
|
|
5587
|
+
field: '${r.propertyName}',
|
|
5588
|
+
label: '${humanizeClass(r.target.className)}',
|
|
5589
|
+
type: 'entity' as FieldType,
|
|
5590
|
+
importance: 'secondary' as FieldImportance,
|
|
5591
|
+
reference: '${r.target.plural}',
|
|
5592
|
+
},`
|
|
5593
|
+
);
|
|
5594
|
+
const tsEntries = ts3 ? [
|
|
5595
|
+
` createdAt: {
|
|
5596
|
+
field: 'createdAt',
|
|
5597
|
+
label: 'Created',
|
|
5598
|
+
type: 'datetime' as FieldType,
|
|
5599
|
+
importance: 'tertiary' as FieldImportance,
|
|
5600
|
+
format: { dateFormat: 'relative' },
|
|
5601
|
+
},`,
|
|
5602
|
+
` updatedAt: {
|
|
5603
|
+
field: 'updatedAt',
|
|
5604
|
+
label: 'Updated',
|
|
5605
|
+
type: 'datetime' as FieldType,
|
|
5606
|
+
importance: 'tertiary' as FieldImportance,
|
|
5607
|
+
format: { dateFormat: 'relative' },
|
|
5608
|
+
},`
|
|
5609
|
+
] : [];
|
|
5610
|
+
const allEntries = [...fieldEntries, ...relEntries, ...tsEntries].join("\n");
|
|
5611
|
+
const primaryFields = fields.filter((f) => f.importance === "primary").map((f) => ` '${f.field}',`).join("\n");
|
|
5612
|
+
const searchFields = fields.filter((f) => f.filterable).map((f) => ` '${f.field}',`).join("\n");
|
|
5613
|
+
const defaultSortField = ts3 ? "createdAt" : "id";
|
|
5614
|
+
const expose = parsed?.expose ?? ["repository", "rest", "trpc"];
|
|
5615
|
+
const canWrite = expose.includes("repository") || expose.includes("trpc");
|
|
5616
|
+
const body = `import type { FieldMeta, FieldType, FieldImportance } from './field-meta';
|
|
5617
|
+
import type { ${className} } from '${ctx.config.dbEntitiesImport}/${name}';
|
|
5618
|
+
|
|
5619
|
+
export const ${camelName}Fields: Record<string, FieldMeta<${className}>> = {
|
|
5620
|
+
${allEntries}
|
|
5621
|
+
};
|
|
5622
|
+
|
|
5623
|
+
export const ${camelName}Metadata = {
|
|
5624
|
+
name: '${name}',
|
|
5625
|
+
plural: '${plural}',
|
|
5626
|
+
displayName: '${humanizeClass(className)}',
|
|
5627
|
+
displayNamePlural: '${humanizeClass(classNamePlural)}',
|
|
5628
|
+
|
|
5629
|
+
fields: ${camelName}Fields,
|
|
5630
|
+
|
|
5631
|
+
primaryFields: [
|
|
5632
|
+
${primaryFields}
|
|
5633
|
+
],
|
|
5634
|
+
searchFields: [
|
|
5635
|
+
${searchFields}
|
|
5636
|
+
],
|
|
5637
|
+
defaultSort: { field: '${defaultSortField}', direction: 'desc' as const },
|
|
5638
|
+
|
|
5639
|
+
capabilities: {
|
|
5640
|
+
create: ${canWrite},
|
|
5641
|
+
update: ${canWrite},
|
|
5642
|
+
delete: ${canWrite},
|
|
5643
|
+
list: true,
|
|
5644
|
+
get: true,
|
|
5645
|
+
},
|
|
5646
|
+
} as const;
|
|
5647
|
+
`;
|
|
5648
|
+
return withBanner(`entities/${name}.yaml`, body);
|
|
5649
|
+
}
|
|
5650
|
+
function buildFieldsIndexFile(ctx) {
|
|
5651
|
+
const entities = sortEntities(ctx.entities);
|
|
5652
|
+
const lines = entities.map((e) => `export * from './${e.name}';`);
|
|
5653
|
+
return withBanner(SOURCE_DESC_SET5, `${lines.join("\n")}
|
|
5654
|
+
`);
|
|
5655
|
+
}
|
|
5656
|
+
function emitFields(ctx, outDir) {
|
|
5657
|
+
const fieldsDir = join15(outDir, "fields");
|
|
5658
|
+
const entities = sortEntities(ctx.entities);
|
|
5659
|
+
const written = [];
|
|
5660
|
+
const typePath = join15(fieldsDir, "field-meta.ts");
|
|
5661
|
+
writeFile2(typePath, buildFieldMetaTypeFile());
|
|
5662
|
+
written.push(typePath);
|
|
5663
|
+
for (const entity of entities) {
|
|
5664
|
+
const filePath = join15(fieldsDir, `${entity.name}.ts`);
|
|
5665
|
+
writeFile2(filePath, buildEntityFieldsFile(entity, ctx));
|
|
5666
|
+
written.push(filePath);
|
|
5667
|
+
}
|
|
5668
|
+
const indexPath = join15(fieldsDir, "index.ts");
|
|
5669
|
+
writeFile2(indexPath, buildFieldsIndexFile(ctx));
|
|
5670
|
+
written.push(indexPath);
|
|
5671
|
+
return written;
|
|
5672
|
+
}
|
|
5673
|
+
|
|
5674
|
+
// src/emitters/frontend/emit-index.ts
|
|
5675
|
+
import { join as join16 } from "path";
|
|
5676
|
+
|
|
5677
|
+
// src/emitters/frontend/deps.ts
|
|
5678
|
+
var FRONTEND_EMITTED_DEPS = {
|
|
5679
|
+
"@pattern-stack/frontend-patterns": "^0.2.0-alpha.18",
|
|
5680
|
+
"@tanstack/react-db": "^0.1.55",
|
|
5681
|
+
"@tanstack/electric-db-collection": "^0.2.11",
|
|
5682
|
+
"@tanstack/query-db-collection": "^1.0.6",
|
|
5683
|
+
"@tanstack/react-query": "^5.0.0"
|
|
5684
|
+
};
|
|
5685
|
+
|
|
5686
|
+
// src/emitters/frontend/emit-index.ts
|
|
5687
|
+
var SOURCE_DESC_SET6 = "the entity set";
|
|
5688
|
+
function buildVersionPairingComment() {
|
|
5689
|
+
const entries = Object.entries(FRONTEND_EMITTED_DEPS);
|
|
5690
|
+
const nameWidth = Math.max(...entries.map(([name]) => name.length));
|
|
5691
|
+
const rows = entries.map(([name, range]) => ` * ${name.padEnd(nameWidth)} ${range}`).join("\n");
|
|
5692
|
+
return ` * Version pairing \u2014 the emitted imports require these package ranges in the
|
|
5693
|
+
* consumer's frontend package.json:
|
|
5694
|
+
*
|
|
5695
|
+
${rows}`;
|
|
5696
|
+
}
|
|
5697
|
+
function buildRootIndexFile(ctx) {
|
|
5698
|
+
const entities = sortEntities(ctx.entities);
|
|
5699
|
+
const entityList = entities.map((e) => ` * - ${e.className}`).join("\n");
|
|
5700
|
+
const body = `/**
|
|
5701
|
+
* Generated frontend data layer.
|
|
5702
|
+
*
|
|
5703
|
+
* Entities:
|
|
5704
|
+
${entityList || " * (none)"}
|
|
5705
|
+
*
|
|
5706
|
+
${buildVersionPairingComment()}
|
|
5707
|
+
*/
|
|
5708
|
+
|
|
5709
|
+
// Per-entity sync configuration + runtime overrides
|
|
5710
|
+
export * from './config';
|
|
5711
|
+
|
|
5712
|
+
// Shared TanStack QueryClient
|
|
5713
|
+
export * from './query-client';
|
|
5714
|
+
|
|
5715
|
+
// REST api client
|
|
5716
|
+
export * from './api/index';
|
|
5717
|
+
|
|
5718
|
+
// TanStack DB collections (per-entity sync mode)
|
|
5719
|
+
export * from './collections/index';
|
|
5720
|
+
|
|
5721
|
+
// Entity hooks (createEntityHooks wiring)
|
|
5722
|
+
export * from './entities/index';
|
|
5723
|
+
|
|
5724
|
+
// Field metadata (DataGrid / forms / admin)
|
|
5725
|
+
export * from './fields/index';
|
|
5726
|
+
|
|
5727
|
+
// Unified store (entities + collections + resolvers + lookups)
|
|
5728
|
+
export * from './store/module-index';
|
|
5729
|
+
`;
|
|
5730
|
+
return withBanner(SOURCE_DESC_SET6, body);
|
|
5731
|
+
}
|
|
5732
|
+
function emitIndex(ctx, outDir) {
|
|
5733
|
+
const indexPath = join16(outDir, "index.ts");
|
|
5734
|
+
writeFile2(indexPath, buildRootIndexFile(ctx));
|
|
5735
|
+
return [indexPath];
|
|
5736
|
+
}
|
|
5737
|
+
|
|
5738
|
+
// src/emitters/frontend/load-context.ts
|
|
4823
5739
|
import path12 from "path";
|
|
5740
|
+
|
|
5741
|
+
// src/schema/codegen-config.schema.ts
|
|
5742
|
+
import { z } from "zod";
|
|
5743
|
+
var GenerateConfigSchema = z.object({
|
|
5744
|
+
/**
|
|
5745
|
+
* Backend architecture to generate. One of:
|
|
5746
|
+
* - 'clean' — Full Clean Architecture (domain + application + infrastructure + presentation)
|
|
5747
|
+
* - 'clean-lite-ps' — Clean-Lite-PS modules/{plural}/ layout
|
|
5748
|
+
*
|
|
5749
|
+
* Default: 'clean'.
|
|
5750
|
+
*/
|
|
5751
|
+
architecture: z.enum(["clean", "clean-lite-ps"]).default("clean"),
|
|
5752
|
+
/**
|
|
5753
|
+
* Whether to emit the frontend pipeline (collections, hooks, entity metadata).
|
|
5754
|
+
* Default: false — backend-only projects opt out by default.
|
|
5755
|
+
*/
|
|
5756
|
+
frontend: z.boolean().default(false),
|
|
5757
|
+
/**
|
|
5758
|
+
* Analytics backend to generate.
|
|
5759
|
+
* - 'none': no analytics layer (default)
|
|
5760
|
+
* - 'cube': generate cube.js semantic layer and analytics providers
|
|
5761
|
+
*/
|
|
5762
|
+
analytics: z.enum(["none", "cube"]).default("none")
|
|
5763
|
+
}).passthrough();
|
|
5764
|
+
var PathsConfigSchema = z.object({
|
|
5765
|
+
events_dir: z.string().optional(),
|
|
5766
|
+
generated: z.string().default("src/generated")
|
|
5767
|
+
}).passthrough();
|
|
5768
|
+
var PatternsConfigSchema = z.array(z.string()).optional().default(["src/patterns/*.pattern.ts"]);
|
|
5769
|
+
var RuntimeModeSchema = z.enum(["package", "vendored"]).default("package");
|
|
5770
|
+
var FrontendAuthConfigSchema = z.object({
|
|
5771
|
+
function: z.string().nullable().default("getAuthorizationHeader")
|
|
5772
|
+
}).default({ function: "getAuthorizationHeader" });
|
|
5773
|
+
var FrontendSyncConfigSchema = z.object({
|
|
5774
|
+
mode: z.enum(["api", "electric"]).default("electric"),
|
|
5775
|
+
shapeUrl: z.string().default("/v1/shape"),
|
|
5776
|
+
useTableParam: z.boolean().default(true),
|
|
5777
|
+
columnMapper: z.string().nullable().default("snakeCamelMapper"),
|
|
5778
|
+
columnMapperNeedsCall: z.boolean().default(true),
|
|
5779
|
+
apiBaseUrlImport: z.string().nullable().default(null),
|
|
5780
|
+
apiUrl: z.string().default("/api")
|
|
5781
|
+
}).default({});
|
|
5782
|
+
var FrontendConfigSchema = z.object({
|
|
5783
|
+
auth: FrontendAuthConfigSchema,
|
|
5784
|
+
parsers: z.record(z.string()).default({ timestamptz: "(date: string) => new Date(date)" }),
|
|
5785
|
+
sync: FrontendSyncConfigSchema
|
|
5786
|
+
}).strict().default({});
|
|
5787
|
+
|
|
5788
|
+
// src/emitters/frontend/load-context.ts
|
|
5789
|
+
var DEFAULT_DB_ENTITIES = {
|
|
5790
|
+
path: "packages/db/src/entities",
|
|
5791
|
+
import: "@repo/db/entities"
|
|
5792
|
+
};
|
|
5793
|
+
var DEFAULT_FRONTEND_GENERATED = {
|
|
5794
|
+
path: "apps/frontend/src/generated",
|
|
5795
|
+
import: "@/generated"
|
|
5796
|
+
};
|
|
5797
|
+
var DEFAULT_FRONTEND_COLLECTIONS_AUTH = {
|
|
5798
|
+
path: "apps/frontend/src/lib/collections/auth",
|
|
5799
|
+
import: "@/lib/collections/auth"
|
|
5800
|
+
};
|
|
5801
|
+
function resolveLocation(config, key, fallback) {
|
|
5802
|
+
const override = config.locations?.[key];
|
|
5803
|
+
return {
|
|
5804
|
+
path: override?.path ?? fallback.path,
|
|
5805
|
+
import: override?.import ?? fallback.import
|
|
5806
|
+
};
|
|
5807
|
+
}
|
|
5808
|
+
function mapFrontendEmitConfig(config) {
|
|
5809
|
+
const parsed = FrontendConfigSchema.safeParse(config.frontend ?? {});
|
|
5810
|
+
const fe = parsed.success ? parsed.data : FrontendConfigSchema.parse({});
|
|
5811
|
+
const dbEntities = resolveLocation(config, "dbEntities", DEFAULT_DB_ENTITIES);
|
|
5812
|
+
const collectionsAuth = resolveLocation(
|
|
5813
|
+
config,
|
|
5814
|
+
"frontendCollectionsAuth",
|
|
5815
|
+
DEFAULT_FRONTEND_COLLECTIONS_AUTH
|
|
5816
|
+
);
|
|
5817
|
+
const architecture = config.generate?.architecture === "clean-lite-ps" ? "clean-lite-ps" : "clean";
|
|
5818
|
+
return {
|
|
5819
|
+
globalSyncMode: fe.sync.mode,
|
|
5820
|
+
// auth.function: absent → 'getAuthorizationHeader' (schema default), explicit
|
|
5821
|
+
// null → disabled (no header lines emitted).
|
|
5822
|
+
authFunction: fe.auth.function,
|
|
5823
|
+
authImport: collectionsAuth.import,
|
|
5824
|
+
shapeUrl: fe.sync.shapeUrl,
|
|
5825
|
+
useTableParam: fe.sync.useTableParam,
|
|
5826
|
+
columnMapper: fe.sync.columnMapper,
|
|
5827
|
+
columnMapperNeedsCall: fe.sync.columnMapperNeedsCall,
|
|
5828
|
+
apiUrl: fe.sync.apiUrl,
|
|
5829
|
+
apiBaseUrlImport: fe.sync.apiBaseUrlImport,
|
|
5830
|
+
parsers: fe.parsers,
|
|
5831
|
+
architecture,
|
|
5832
|
+
dbEntitiesImport: dbEntities.import
|
|
5833
|
+
};
|
|
5834
|
+
}
|
|
5835
|
+
function loadFrontendEmitContext(cwd, config, opts = {}) {
|
|
5836
|
+
const entitiesDir = opts.entitiesDir ?? path12.resolve(cwd, config.paths?.entities_dir ?? "entities");
|
|
5837
|
+
const { registry } = loadEntityRegistry(entitiesDir);
|
|
5838
|
+
const entities = sortEntities([...registry.values()]);
|
|
5839
|
+
if (entities.length === 0) {
|
|
5840
|
+
return {
|
|
5841
|
+
skip: `no entities found in ${path12.relative(cwd, entitiesDir) || entitiesDir}`
|
|
5842
|
+
};
|
|
5843
|
+
}
|
|
5844
|
+
const parsedList = loadEntities(entitiesDir).entities;
|
|
5845
|
+
const parsed = new Map(
|
|
5846
|
+
parsedList.map((p) => [p.name, p])
|
|
5847
|
+
);
|
|
5848
|
+
const emitConfig = mapFrontendEmitConfig(config);
|
|
5849
|
+
const generated = resolveLocation(
|
|
5850
|
+
config,
|
|
5851
|
+
"frontendGenerated",
|
|
5852
|
+
DEFAULT_FRONTEND_GENERATED
|
|
5853
|
+
);
|
|
5854
|
+
const outDir = path12.resolve(cwd, generated.path);
|
|
5855
|
+
return {
|
|
5856
|
+
skip: void 0,
|
|
5857
|
+
ctx: { entities, parsed, config: emitConfig },
|
|
5858
|
+
outDir
|
|
5859
|
+
};
|
|
5860
|
+
}
|
|
5861
|
+
|
|
5862
|
+
// src/emitters/frontend/index.ts
|
|
5863
|
+
function emitFrontendSet(ctx, outDir) {
|
|
5864
|
+
return [
|
|
5865
|
+
...emitBase(ctx, outDir),
|
|
5866
|
+
...emitApi(ctx, outDir),
|
|
5867
|
+
...emitCollections(ctx, outDir),
|
|
5868
|
+
...emitEntities(ctx, outDir),
|
|
5869
|
+
...emitStore(ctx, outDir),
|
|
5870
|
+
...emitFields(ctx, outDir),
|
|
5871
|
+
...emitIndex(ctx, outDir)
|
|
5872
|
+
];
|
|
5873
|
+
}
|
|
5874
|
+
|
|
5875
|
+
// src/cli/shared/events-path.ts
|
|
5876
|
+
import path13 from "path";
|
|
4824
5877
|
var FALLBACK = "events";
|
|
4825
5878
|
function resolveEventsDirFromConfig(cwd, config) {
|
|
4826
5879
|
const configured = config?.paths?.events_dir;
|
|
4827
5880
|
if (typeof configured === "string" && configured.length > 0) {
|
|
4828
|
-
return
|
|
5881
|
+
return path13.resolve(cwd, configured);
|
|
4829
5882
|
}
|
|
4830
|
-
return
|
|
5883
|
+
return path13.resolve(cwd, FALLBACK);
|
|
4831
5884
|
}
|
|
4832
5885
|
function resolveEventsDir(ctx) {
|
|
4833
5886
|
return resolveEventsDirFromConfig(ctx.cwd, ctx.config);
|
|
@@ -4856,7 +5909,7 @@ function printInfo(msg) {
|
|
|
4856
5909
|
// src/cli/commands/entity.ts
|
|
4857
5910
|
function resolveProvidersDir(ctx) {
|
|
4858
5911
|
const fromConfig = ctx.config?.paths?.providers;
|
|
4859
|
-
return fromConfig != null ?
|
|
5912
|
+
return fromConfig != null ? path14.resolve(ctx.cwd, fromConfig) : path14.resolve(ctx.cwd, "definitions/providers");
|
|
4860
5913
|
}
|
|
4861
5914
|
function listEntityYamls2(dir, providersDir) {
|
|
4862
5915
|
if (!fs10.existsSync(dir)) return [];
|
|
@@ -4938,10 +5991,10 @@ async function hints(ctx) {
|
|
|
4938
5991
|
{ command: "codegen entity validate", description: "Validate YAML definitions" },
|
|
4939
5992
|
{ command: "codegen entity list", description: "List entities as a table" }
|
|
4940
5993
|
];
|
|
4941
|
-
const providersDir = ctx.config?.paths?.providers != null ?
|
|
5994
|
+
const providersDir = ctx.config?.paths?.providers != null ? path14.resolve(
|
|
4942
5995
|
ctx.cwd,
|
|
4943
5996
|
ctx.config.paths.providers
|
|
4944
|
-
) :
|
|
5997
|
+
) : path14.resolve(ctx.cwd, "definitions/providers");
|
|
4945
5998
|
if (fs10.existsSync(providersDir)) {
|
|
4946
5999
|
baseHints.push({
|
|
4947
6000
|
command: "codegen entity new --all",
|
|
@@ -4997,14 +6050,14 @@ var EntityNewCommand = class extends Command2 {
|
|
|
4997
6050
|
}
|
|
4998
6051
|
let targets = [];
|
|
4999
6052
|
if (this.all) {
|
|
5000
|
-
const dir = ctx.entitiesDir ??
|
|
6053
|
+
const dir = ctx.entitiesDir ?? path14.resolve(ctx.cwd, "entities");
|
|
5001
6054
|
targets = listEntityYamls2(dir, resolveProvidersDir(ctx));
|
|
5002
6055
|
if (targets.length === 0) {
|
|
5003
6056
|
printError(`No entity YAML files found in ${dir}`);
|
|
5004
6057
|
return 1;
|
|
5005
6058
|
}
|
|
5006
6059
|
} else if (this.yaml) {
|
|
5007
|
-
targets = [
|
|
6060
|
+
targets = [path14.resolve(ctx.cwd, this.yaml)];
|
|
5008
6061
|
} else {
|
|
5009
6062
|
printError("Missing YAML path. Pass a file or --all.");
|
|
5010
6063
|
return 2;
|
|
@@ -5021,7 +6074,7 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5021
6074
|
}
|
|
5022
6075
|
if (invalid.length > 0 && !this.continueOnError) {
|
|
5023
6076
|
for (const i of invalid) {
|
|
5024
|
-
printError(`${
|
|
6077
|
+
printError(`${path14.basename(i.file)} \u2014 ${i.message}`);
|
|
5025
6078
|
for (const detail of i.details ?? []) {
|
|
5026
6079
|
printError(` \u2022 ${detail}`);
|
|
5027
6080
|
}
|
|
@@ -5030,7 +6083,7 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5030
6083
|
return 1;
|
|
5031
6084
|
}
|
|
5032
6085
|
}
|
|
5033
|
-
const entitiesDirForEmits = ctx.entitiesDir ??
|
|
6086
|
+
const entitiesDirForEmits = ctx.entitiesDir ?? path14.resolve(ctx.cwd, "entities");
|
|
5034
6087
|
const eventsDirForEmits = resolveEventsDir(ctx);
|
|
5035
6088
|
const allEntitiesForEmits = loadEntities(entitiesDirForEmits, {
|
|
5036
6089
|
excludeDirs: [resolveProvidersDir(ctx)]
|
|
@@ -5079,29 +6132,29 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5079
6132
|
if (!isJsonMode()) return 1;
|
|
5080
6133
|
}
|
|
5081
6134
|
}
|
|
5082
|
-
const entitiesDir = ctx.entitiesDir ??
|
|
5083
|
-
const relationshipsDir =
|
|
6135
|
+
const entitiesDir = ctx.entitiesDir ?? path14.resolve(ctx.cwd, "entities");
|
|
6136
|
+
const relationshipsDir = path14.resolve(ctx.cwd, "relationships");
|
|
5084
6137
|
const generatedDir = resolveGeneratedDir(ctx);
|
|
5085
6138
|
const architecture = resolveArchitecture(ctx);
|
|
5086
6139
|
const subsystemsRoot = resolveSubsystemsRoot(ctx);
|
|
5087
6140
|
const runtimeMode = resolveRuntimeMode(ctx.config);
|
|
5088
|
-
const scopeEntityTypePath = runtimeMode === "package" ?
|
|
6141
|
+
const scopeEntityTypePath = runtimeMode === "package" ? path14.resolve(generatedDir, "scope-entity-type.ts") : path14.resolve(subsystemsRoot, "jobs/generated/scope-entity-type.ts");
|
|
5089
6142
|
const eventsDir = resolveEventsDir(ctx);
|
|
5090
|
-
const eventCodegenOutputDir = runtimeMode === "package" ?
|
|
6143
|
+
const eventCodegenOutputDir = runtimeMode === "package" ? path14.resolve(generatedDir, "events") : path14.resolve(subsystemsRoot, "events/generated");
|
|
5091
6144
|
const bridgeInstalledForRegistry = configuredSubsystemNames(
|
|
5092
6145
|
ctx.config
|
|
5093
6146
|
).includes("bridge");
|
|
5094
|
-
const bridgeRegistryOutputDir = runtimeMode === "package" ? generatedDir :
|
|
6147
|
+
const bridgeRegistryOutputDir = runtimeMode === "package" ? generatedDir : path14.resolve(subsystemsRoot, "bridge/generated");
|
|
5095
6148
|
const backendSrcForHandlers = ctx.config?.paths?.backend_src ?? "src";
|
|
5096
|
-
const bridgeHandlersDir =
|
|
6149
|
+
const bridgeHandlersDir = path14.resolve(
|
|
5097
6150
|
ctx.cwd,
|
|
5098
6151
|
backendSrcForHandlers,
|
|
5099
6152
|
"jobs"
|
|
5100
6153
|
);
|
|
5101
6154
|
const orchestrationConfigured = ctx.config?.paths?.orchestration_src;
|
|
5102
|
-
const orchestrationOutputRoot =
|
|
6155
|
+
const orchestrationOutputRoot = path14.resolve(
|
|
5103
6156
|
ctx.cwd,
|
|
5104
|
-
typeof orchestrationConfigured === "string" && orchestrationConfigured.length > 0 ? orchestrationConfigured :
|
|
6157
|
+
typeof orchestrationConfigured === "string" && orchestrationConfigured.length > 0 ? orchestrationConfigured : path14.join(backendSrcForHandlers, "orchestration")
|
|
5105
6158
|
);
|
|
5106
6159
|
const orchestrationGlobs = (() => {
|
|
5107
6160
|
const fromCfg = ctx.config?.patterns;
|
|
@@ -5232,7 +6285,7 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5232
6285
|
}
|
|
5233
6286
|
if (invalid.length > 0) {
|
|
5234
6287
|
for (const i of invalid) {
|
|
5235
|
-
printWarning(`${
|
|
6288
|
+
printWarning(`${path14.basename(i.file)} \u2014 ${i.message}`);
|
|
5236
6289
|
}
|
|
5237
6290
|
}
|
|
5238
6291
|
console.log("");
|
|
@@ -5250,7 +6303,7 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5250
6303
|
}
|
|
5251
6304
|
const succeeded = [];
|
|
5252
6305
|
const failed = [
|
|
5253
|
-
...invalid.map((i) => ({ name:
|
|
6306
|
+
...invalid.map((i) => ({ name: path14.basename(i.file), file: i.file, message: i.message }))
|
|
5254
6307
|
];
|
|
5255
6308
|
for (const v of validated) {
|
|
5256
6309
|
if (!isJsonMode()) {
|
|
@@ -5373,10 +6426,40 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5373
6426
|
}
|
|
5374
6427
|
}
|
|
5375
6428
|
}
|
|
6429
|
+
let frontendResult = null;
|
|
6430
|
+
const frontendEnabled = ctx.config?.generate?.frontend === true;
|
|
6431
|
+
if (frontendEnabled) {
|
|
6432
|
+
try {
|
|
6433
|
+
const loaded = loadFrontendEmitContext(
|
|
6434
|
+
ctx.cwd,
|
|
6435
|
+
ctx.config,
|
|
6436
|
+
{ entitiesDir }
|
|
6437
|
+
);
|
|
6438
|
+
if (loaded.skip !== void 0) {
|
|
6439
|
+
if (!isJsonMode()) {
|
|
6440
|
+
printInfo(`frontend emission skipped \u2014 ${loaded.skip}`);
|
|
6441
|
+
}
|
|
6442
|
+
} else {
|
|
6443
|
+
const { ctx: frontendCtx, outDir: frontendOutDir } = loaded;
|
|
6444
|
+
const written = emitFrontendSet(frontendCtx, frontendOutDir);
|
|
6445
|
+
frontendResult = { written, outDir: frontendOutDir };
|
|
6446
|
+
if (!isJsonMode()) {
|
|
6447
|
+
printInfo(
|
|
6448
|
+
`frontend emitted (${written.length} files) \u2192 ${path14.relative(ctx.cwd, frontendOutDir)}`
|
|
6449
|
+
);
|
|
6450
|
+
}
|
|
6451
|
+
}
|
|
6452
|
+
} catch (err) {
|
|
6453
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6454
|
+
if (!isJsonMode()) {
|
|
6455
|
+
printWarning(`frontend emission failed \u2014 ${msg}`);
|
|
6456
|
+
}
|
|
6457
|
+
}
|
|
6458
|
+
}
|
|
5376
6459
|
let providerResult = null;
|
|
5377
6460
|
try {
|
|
5378
6461
|
const providersDir = resolveProvidersDir(ctx);
|
|
5379
|
-
const providerOutputRoot =
|
|
6462
|
+
const providerOutputRoot = path14.resolve(
|
|
5380
6463
|
ctx.cwd,
|
|
5381
6464
|
backendSrcForHandlers,
|
|
5382
6465
|
"integrations/providers"
|
|
@@ -5418,7 +6501,7 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5418
6501
|
try {
|
|
5419
6502
|
if (providerResult && !providerResult.skipped && providerResult.issues.length === 0) {
|
|
5420
6503
|
const providersDir = providerResult.providersDir;
|
|
5421
|
-
const adapterOutputRoot =
|
|
6504
|
+
const adapterOutputRoot = path14.resolve(
|
|
5422
6505
|
ctx.cwd,
|
|
5423
6506
|
backendSrcForHandlers,
|
|
5424
6507
|
"integrations"
|
|
@@ -5437,7 +6520,7 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5437
6520
|
providers: loadedProviders,
|
|
5438
6521
|
entities: entityDefs,
|
|
5439
6522
|
outputRoot: adapterOutputRoot,
|
|
5440
|
-
backendSrcAbs:
|
|
6523
|
+
backendSrcAbs: path14.resolve(ctx.cwd, backendSrcForHandlers),
|
|
5441
6524
|
aliases: assemblyTsAliases?.aliases ?? {},
|
|
5442
6525
|
mode: runtimeMode
|
|
5443
6526
|
});
|
|
@@ -5514,6 +6597,11 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5514
6597
|
relativePath: f.relativePath
|
|
5515
6598
|
}))
|
|
5516
6599
|
} : null,
|
|
6600
|
+
frontend: frontendResult ? {
|
|
6601
|
+
outDir: frontendResult.outDir,
|
|
6602
|
+
written: frontendResult.written,
|
|
6603
|
+
fileCount: frontendResult.written.length
|
|
6604
|
+
} : null,
|
|
5517
6605
|
emits: {
|
|
5518
6606
|
warnings: emitsWarnings.map((w) => ({
|
|
5519
6607
|
entity: w.entity ?? null,
|
|
@@ -5539,22 +6627,27 @@ var EntityNewCommand = class extends Command2 {
|
|
|
5539
6627
|
}
|
|
5540
6628
|
if (barrelResult) {
|
|
5541
6629
|
printInfo(
|
|
5542
|
-
`barrels regenerated (${barrelResult.entityCount} entities) \u2192 ${
|
|
6630
|
+
`barrels regenerated (${barrelResult.entityCount} entities) \u2192 ${path14.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path14.relative(ctx.cwd, barrelResult.schemaBarrel)}`
|
|
5543
6631
|
);
|
|
5544
6632
|
}
|
|
5545
6633
|
if (scopeResult) {
|
|
5546
6634
|
printInfo(
|
|
5547
|
-
`scope-entity-type regenerated (${scopeResult.scopeableNames.length} scopeable) \u2192 ${
|
|
6635
|
+
`scope-entity-type regenerated (${scopeResult.scopeableNames.length} scopeable) \u2192 ${path14.relative(ctx.cwd, scopeResult.outputPath)}`
|
|
5548
6636
|
);
|
|
5549
6637
|
}
|
|
5550
6638
|
if (eventCodegenResult) {
|
|
5551
6639
|
printInfo(
|
|
5552
|
-
`event codegen regenerated (${eventCodegenResult.eventCount} events) \u2192 ${
|
|
6640
|
+
`event codegen regenerated (${eventCodegenResult.eventCount} events) \u2192 ${path14.relative(ctx.cwd, eventCodegenResult.outputDir)}`
|
|
5553
6641
|
);
|
|
5554
6642
|
}
|
|
5555
6643
|
if (orchestrationResult && orchestrationResult.patterns.length > 0) {
|
|
5556
6644
|
printInfo(
|
|
5557
|
-
`orchestration regenerated (${orchestrationResult.patterns.length} patterns, ${orchestrationResult.files.length} files) \u2192 ${
|
|
6645
|
+
`orchestration regenerated (${orchestrationResult.patterns.length} patterns, ${orchestrationResult.files.length} files) \u2192 ${path14.relative(ctx.cwd, orchestrationResult.outputRoot)}`
|
|
6646
|
+
);
|
|
6647
|
+
}
|
|
6648
|
+
if (frontendResult) {
|
|
6649
|
+
printInfo(
|
|
6650
|
+
`frontend regenerated (${frontendResult.written.length} files) \u2192 ${path14.relative(ctx.cwd, frontendResult.outDir)}`
|
|
5558
6651
|
);
|
|
5559
6652
|
}
|
|
5560
6653
|
}
|
|
@@ -5640,7 +6733,7 @@ var EntityValidateCommand = class extends Command2 {
|
|
|
5640
6733
|
json: this.json,
|
|
5641
6734
|
skipDetection: true
|
|
5642
6735
|
});
|
|
5643
|
-
const targetDir = this.dir ?
|
|
6736
|
+
const targetDir = this.dir ? path14.resolve(ctx.cwd, this.dir) : ctx.entitiesDir ?? path14.resolve(ctx.cwd, "entities");
|
|
5644
6737
|
if (!fs10.existsSync(targetDir)) {
|
|
5645
6738
|
printError(`Directory not found: ${targetDir}`);
|
|
5646
6739
|
return 1;
|
|
@@ -5693,7 +6786,7 @@ var entity_default = entityNoun;
|
|
|
5693
6786
|
|
|
5694
6787
|
// src/cli/commands/subsystem.ts
|
|
5695
6788
|
import fs13 from "fs";
|
|
5696
|
-
import
|
|
6789
|
+
import path24 from "path";
|
|
5697
6790
|
import { Command as Command3, Option as Option3 } from "clipanion";
|
|
5698
6791
|
|
|
5699
6792
|
// src/cli/shared/config-block-detect.ts
|
|
@@ -5726,26 +6819,26 @@ function stripConfigBlock(yamlSource, subsystem) {
|
|
|
5726
6819
|
}
|
|
5727
6820
|
|
|
5728
6821
|
// src/cli/shared/events-scaffold-locals.ts
|
|
5729
|
-
import
|
|
6822
|
+
import path15 from "path";
|
|
5730
6823
|
function resolveEventsScaffoldLocals(input) {
|
|
5731
6824
|
const { cwd, config } = input;
|
|
5732
6825
|
void input.fileExists;
|
|
5733
6826
|
const eventsBlock = config?.events ?? {};
|
|
5734
6827
|
const subsystemsRoot = resolveSubsystemsRootFromConfig(cwd, config);
|
|
5735
|
-
const configPath =
|
|
5736
|
-
const schemaPath =
|
|
6828
|
+
const configPath = path15.resolve(cwd, "codegen.config.yaml");
|
|
6829
|
+
const schemaPath = path15.resolve(
|
|
5737
6830
|
subsystemsRoot,
|
|
5738
6831
|
"events",
|
|
5739
6832
|
"domain-events.schema.ts"
|
|
5740
6833
|
);
|
|
5741
|
-
const generatedKeepPath =
|
|
6834
|
+
const generatedKeepPath = path15.resolve(
|
|
5742
6835
|
subsystemsRoot,
|
|
5743
6836
|
"events",
|
|
5744
6837
|
"generated",
|
|
5745
6838
|
".gitkeep"
|
|
5746
6839
|
);
|
|
5747
6840
|
return {
|
|
5748
|
-
appName:
|
|
6841
|
+
appName: path15.basename(cwd),
|
|
5749
6842
|
multiTenant: normaliseMultiTenant(eventsBlock.multi_tenant),
|
|
5750
6843
|
configPath,
|
|
5751
6844
|
schemaPath,
|
|
@@ -5771,7 +6864,7 @@ function localsToHygenArgs(locals) {
|
|
|
5771
6864
|
}
|
|
5772
6865
|
|
|
5773
6866
|
// src/cli/shared/jobs-scaffold-locals.ts
|
|
5774
|
-
import
|
|
6867
|
+
import path16 from "path";
|
|
5775
6868
|
var MAIN_HOOK_SENTINEL = "JOBS \u2014 Embedded worker mode (optional)";
|
|
5776
6869
|
function workerSkipValue(exists) {
|
|
5777
6870
|
return exists ? "true" : "";
|
|
@@ -5780,10 +6873,10 @@ function resolveJobsScaffoldLocals(input) {
|
|
|
5780
6873
|
const { cwd, config, fileExists, readFile } = input;
|
|
5781
6874
|
const jobsBlock = config?.jobs ?? {};
|
|
5782
6875
|
const subsystemsRoot = resolveSubsystemsRootFromConfig(cwd, config);
|
|
5783
|
-
const workerPath =
|
|
5784
|
-
const mainTsPath =
|
|
5785
|
-
const configPath =
|
|
5786
|
-
const schemaPath =
|
|
6876
|
+
const workerPath = path16.resolve(cwd, "worker.ts");
|
|
6877
|
+
const mainTsPath = path16.resolve(cwd, "src/main.ts");
|
|
6878
|
+
const configPath = path16.resolve(cwd, "codegen.config.yaml");
|
|
6879
|
+
const schemaPath = path16.resolve(
|
|
5787
6880
|
subsystemsRoot,
|
|
5788
6881
|
"jobs",
|
|
5789
6882
|
"job-orchestration.schema.ts"
|
|
@@ -5791,7 +6884,7 @@ function resolveJobsScaffoldLocals(input) {
|
|
|
5791
6884
|
const mainContent = readFile(mainTsPath);
|
|
5792
6885
|
const mainHookInjected = mainContent !== null && mainContent.includes(MAIN_HOOK_SENTINEL);
|
|
5793
6886
|
return {
|
|
5794
|
-
appName:
|
|
6887
|
+
appName: path16.basename(cwd),
|
|
5795
6888
|
workerMode: normaliseWorkerMode(jobsBlock.worker_mode),
|
|
5796
6889
|
multiTenant: normaliseMultiTenant2(jobsBlock.multi_tenant),
|
|
5797
6890
|
mainTsPath,
|
|
@@ -5833,20 +6926,20 @@ function localsToHygenArgs2(locals) {
|
|
|
5833
6926
|
}
|
|
5834
6927
|
|
|
5835
6928
|
// src/cli/shared/integration-scaffold-locals.ts
|
|
5836
|
-
import
|
|
6929
|
+
import path17 from "path";
|
|
5837
6930
|
function resolveIntegrationScaffoldLocals(input) {
|
|
5838
6931
|
const { cwd, config } = input;
|
|
5839
6932
|
void input.fileExists;
|
|
5840
6933
|
const integrationBlock = config?.integration ?? {};
|
|
5841
6934
|
const subsystemsRoot = resolveSubsystemsRootFromConfig(cwd, config);
|
|
5842
|
-
const configPath =
|
|
5843
|
-
const schemaPath =
|
|
6935
|
+
const configPath = path17.resolve(cwd, "codegen.config.yaml");
|
|
6936
|
+
const schemaPath = path17.resolve(
|
|
5844
6937
|
subsystemsRoot,
|
|
5845
6938
|
"integration",
|
|
5846
6939
|
"integration-audit.schema.ts"
|
|
5847
6940
|
);
|
|
5848
6941
|
return {
|
|
5849
|
-
appName:
|
|
6942
|
+
appName: path17.basename(cwd),
|
|
5850
6943
|
multiTenant: normaliseMultiTenant3(integrationBlock.multi_tenant),
|
|
5851
6944
|
configPath,
|
|
5852
6945
|
schemaPath
|
|
@@ -5869,21 +6962,21 @@ function localsToHygenArgs3(locals) {
|
|
|
5869
6962
|
}
|
|
5870
6963
|
|
|
5871
6964
|
// src/cli/shared/bridge-scaffold-locals.ts
|
|
5872
|
-
import
|
|
6965
|
+
import path18 from "path";
|
|
5873
6966
|
function resolveBridgeScaffoldLocals(input) {
|
|
5874
6967
|
const { cwd, config } = input;
|
|
5875
6968
|
void input.fileExists;
|
|
5876
6969
|
const bridgeBlock = config?.bridge ?? {};
|
|
5877
6970
|
const subsystemsRoot = resolveSubsystemsRootFromConfig(cwd, config);
|
|
5878
|
-
const configPath =
|
|
5879
|
-
const generatedKeepPath =
|
|
6971
|
+
const configPath = path18.resolve(cwd, "codegen.config.yaml");
|
|
6972
|
+
const generatedKeepPath = path18.resolve(
|
|
5880
6973
|
subsystemsRoot,
|
|
5881
6974
|
"bridge",
|
|
5882
6975
|
"generated",
|
|
5883
6976
|
".gitkeep"
|
|
5884
6977
|
);
|
|
5885
6978
|
return {
|
|
5886
|
-
appName:
|
|
6979
|
+
appName: path18.basename(cwd),
|
|
5887
6980
|
multiTenant: normaliseMultiTenant4(bridgeBlock.multi_tenant),
|
|
5888
6981
|
configPath,
|
|
5889
6982
|
generatedKeepPath
|
|
@@ -5906,19 +6999,19 @@ function localsToHygenArgs4(locals) {
|
|
|
5906
6999
|
}
|
|
5907
7000
|
|
|
5908
7001
|
// src/cli/shared/observability-scaffold-locals.ts
|
|
5909
|
-
import
|
|
7002
|
+
import path19 from "path";
|
|
5910
7003
|
var FALLBACK_BACKEND_SRC2 = "src";
|
|
5911
7004
|
function resolveObservabilityScaffoldLocals(input) {
|
|
5912
7005
|
const { cwd, config } = input;
|
|
5913
7006
|
void input.fileExists;
|
|
5914
7007
|
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 =
|
|
7008
|
+
const appModulePath = path19.resolve(cwd, backendSrc, "app.module.ts");
|
|
7009
|
+
const configPath = path19.resolve(cwd, "codegen.config.yaml");
|
|
5917
7010
|
const obsBlock = config?.observability ?? {};
|
|
5918
7011
|
const reporters = obsBlock.reporters ?? {};
|
|
5919
7012
|
const bridgeMetrics = reporters.bridgeMetrics ?? {};
|
|
5920
7013
|
return {
|
|
5921
|
-
appName:
|
|
7014
|
+
appName: path19.basename(cwd),
|
|
5922
7015
|
appModulePath,
|
|
5923
7016
|
configPath,
|
|
5924
7017
|
bridgeMetricsEnabled: bridgeMetrics.enabled === true
|
|
@@ -5939,7 +7032,7 @@ function localsToHygenArgs5(locals) {
|
|
|
5939
7032
|
|
|
5940
7033
|
// src/cli/shared/auth-scaffold-locals.ts
|
|
5941
7034
|
import crypto from "crypto";
|
|
5942
|
-
import
|
|
7035
|
+
import path20 from "path";
|
|
5943
7036
|
var FALLBACK_BACKEND_SRC3 = "src";
|
|
5944
7037
|
var DEFAULT_REDIRECT_URI_BASE = "http://localhost:3000";
|
|
5945
7038
|
function resolveAuthScaffoldLocals(input) {
|
|
@@ -5951,15 +7044,15 @@ function resolveAuthScaffoldLocals(input) {
|
|
|
5951
7044
|
const redirectUriBase = typeof redirectRaw === "string" && redirectRaw.length > 0 ? redirectRaw : DEFAULT_REDIRECT_URI_BASE;
|
|
5952
7045
|
const tokenEncryptionKey = crypto.randomBytes(32).toString("base64");
|
|
5953
7046
|
return {
|
|
5954
|
-
appName:
|
|
5955
|
-
configPath:
|
|
5956
|
-
schemaPath:
|
|
7047
|
+
appName: path20.basename(cwd),
|
|
7048
|
+
configPath: path20.resolve(cwd, "codegen.config.yaml"),
|
|
7049
|
+
schemaPath: path20.resolve(
|
|
5957
7050
|
subsystemsRoot,
|
|
5958
7051
|
"auth",
|
|
5959
7052
|
"auth-oauth-state.schema.ts"
|
|
5960
7053
|
),
|
|
5961
|
-
appModulePath:
|
|
5962
|
-
envConfigPath:
|
|
7054
|
+
appModulePath: path20.resolve(cwd, backendSrc, "app.module.ts"),
|
|
7055
|
+
envConfigPath: path20.resolve(cwd, ".env.config"),
|
|
5963
7056
|
redirectUriBase,
|
|
5964
7057
|
tokenEncryptionKey
|
|
5965
7058
|
};
|
|
@@ -5984,7 +7077,7 @@ function localsToHygenArgs6(locals) {
|
|
|
5984
7077
|
}
|
|
5985
7078
|
|
|
5986
7079
|
// src/cli/shared/auth-integrations-scaffold-locals.ts
|
|
5987
|
-
import
|
|
7080
|
+
import path21 from "path";
|
|
5988
7081
|
var FALLBACK_BACKEND_SRC4 = "src";
|
|
5989
7082
|
var DEFAULT_MODULES_DIR = "modules";
|
|
5990
7083
|
var DEFAULT_DEFINITIONS_DIR = "definitions/entities";
|
|
@@ -5993,17 +7086,17 @@ function resolveAuthIntegrationsScaffoldLocals(input) {
|
|
|
5993
7086
|
const backendSrc = typeof config?.paths?.backend_src === "string" && config.paths.backend_src.length > 0 ? config.paths.backend_src : FALLBACK_BACKEND_SRC4;
|
|
5994
7087
|
const pathsAny = config?.paths;
|
|
5995
7088
|
const modulesConfigured = pathsAny?.modules_dir;
|
|
5996
|
-
const vendorRoot = typeof modulesConfigured === "string" && modulesConfigured.length > 0 ?
|
|
7089
|
+
const vendorRoot = typeof modulesConfigured === "string" && modulesConfigured.length > 0 ? path21.resolve(cwd, modulesConfigured) : path21.resolve(cwd, backendSrc, DEFAULT_MODULES_DIR);
|
|
5997
7090
|
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 =
|
|
7091
|
+
const definitionsPath = entitiesConfigured !== null ? path21.resolve(cwd, entitiesConfigured, "connection.yaml") : path21.resolve(cwd, DEFAULT_DEFINITIONS_DIR, "connection.yaml");
|
|
7092
|
+
const appModulePath = path21.resolve(cwd, backendSrc, "app.module.ts");
|
|
6000
7093
|
let authModuleRegistered = false;
|
|
6001
7094
|
const appModuleSource = input.readFile(appModulePath);
|
|
6002
7095
|
if (appModuleSource && appModuleSource.includes("AuthModule.forRoot")) {
|
|
6003
7096
|
authModuleRegistered = true;
|
|
6004
7097
|
}
|
|
6005
7098
|
return {
|
|
6006
|
-
appName:
|
|
7099
|
+
appName: path21.basename(cwd),
|
|
6007
7100
|
appModulePath,
|
|
6008
7101
|
vendorRoot,
|
|
6009
7102
|
definitionsPath,
|
|
@@ -6021,7 +7114,7 @@ function localsToHygenArgs7(locals) {
|
|
|
6021
7114
|
|
|
6022
7115
|
// src/cli/shared/runtime-copier.ts
|
|
6023
7116
|
import fs11 from "fs";
|
|
6024
|
-
import
|
|
7117
|
+
import path22 from "path";
|
|
6025
7118
|
function readIfExists(p) {
|
|
6026
7119
|
try {
|
|
6027
7120
|
return fs11.readFileSync(p, "utf-8");
|
|
@@ -6039,8 +7132,8 @@ function extractRelativeImports(source) {
|
|
|
6039
7132
|
return out;
|
|
6040
7133
|
}
|
|
6041
7134
|
function resolveSourceImport(sourceFile, specifier) {
|
|
6042
|
-
const base =
|
|
6043
|
-
const candidates = [base + ".ts", base + ".tsx",
|
|
7135
|
+
const base = path22.resolve(path22.dirname(sourceFile), specifier);
|
|
7136
|
+
const candidates = [base + ".ts", base + ".tsx", path22.join(base, "index.ts")];
|
|
6044
7137
|
for (const c of candidates) {
|
|
6045
7138
|
if (fs11.existsSync(c)) return c;
|
|
6046
7139
|
}
|
|
@@ -6051,8 +7144,8 @@ async function copyRuntime(opts) {
|
|
|
6051
7144
|
if (!fs11.existsSync(sourceDir) || !fs11.statSync(sourceDir).isDirectory()) {
|
|
6052
7145
|
throw new Error(`runtime source directory not found: ${sourceDir}`);
|
|
6053
7146
|
}
|
|
6054
|
-
const runtimeRoot4 = opts.runtimeRoot ?
|
|
6055
|
-
const depsTargetRoot = opts.depsTargetRoot ??
|
|
7147
|
+
const runtimeRoot4 = opts.runtimeRoot ? path22.resolve(opts.runtimeRoot) : path22.resolve(sourceDir, "..", "..");
|
|
7148
|
+
const depsTargetRoot = opts.depsTargetRoot ?? path22.resolve(targetDir, "..");
|
|
6056
7149
|
const result = {
|
|
6057
7150
|
written: [],
|
|
6058
7151
|
updated: [],
|
|
@@ -6063,7 +7156,7 @@ async function copyRuntime(opts) {
|
|
|
6063
7156
|
const queue = [];
|
|
6064
7157
|
function walk(dir) {
|
|
6065
7158
|
for (const entry of fs11.readdirSync(dir)) {
|
|
6066
|
-
const src =
|
|
7159
|
+
const src = path22.join(dir, entry);
|
|
6067
7160
|
const stat = fs11.statSync(src);
|
|
6068
7161
|
if (stat.isDirectory()) {
|
|
6069
7162
|
if (entry === "generated") continue;
|
|
@@ -6072,9 +7165,9 @@ async function copyRuntime(opts) {
|
|
|
6072
7165
|
}
|
|
6073
7166
|
if (!stat.isFile()) continue;
|
|
6074
7167
|
if (!entry.endsWith(".ts") && !entry.endsWith(".tsx")) continue;
|
|
6075
|
-
const rel2 =
|
|
7168
|
+
const rel2 = path22.relative(sourceDir, src);
|
|
6076
7169
|
if (filter && !filter(rel2) && !filter(entry)) continue;
|
|
6077
|
-
queue.push({ src, dest:
|
|
7170
|
+
queue.push({ src, dest: path22.join(targetDir, rel2), isDep: false });
|
|
6078
7171
|
}
|
|
6079
7172
|
}
|
|
6080
7173
|
walk(sourceDir);
|
|
@@ -6095,18 +7188,18 @@ async function copyRuntime(opts) {
|
|
|
6095
7188
|
else result.unchanged.push(next.dest);
|
|
6096
7189
|
if (next.isDep) result.dependenciesCopied.push(next.dest);
|
|
6097
7190
|
if (!dryRun && status !== "unchanged") {
|
|
6098
|
-
fs11.mkdirSync(
|
|
7191
|
+
fs11.mkdirSync(path22.dirname(next.dest), { recursive: true });
|
|
6099
7192
|
fs11.writeFileSync(next.dest, content);
|
|
6100
7193
|
}
|
|
6101
7194
|
if (resolveDeps) {
|
|
6102
7195
|
for (const spec of extractRelativeImports(content)) {
|
|
6103
7196
|
const resolvedSrc = resolveSourceImport(next.src, spec);
|
|
6104
7197
|
if (!resolvedSrc) continue;
|
|
6105
|
-
const relToRuntime =
|
|
6106
|
-
if (relToRuntime.startsWith("..") ||
|
|
6107
|
-
const relToSource =
|
|
6108
|
-
if (!relToSource.startsWith("..") && !
|
|
6109
|
-
const depDest =
|
|
7198
|
+
const relToRuntime = path22.relative(runtimeRoot4, resolvedSrc);
|
|
7199
|
+
if (relToRuntime.startsWith("..") || path22.isAbsolute(relToRuntime)) continue;
|
|
7200
|
+
const relToSource = path22.relative(sourceDir, resolvedSrc);
|
|
7201
|
+
if (!relToSource.startsWith("..") && !path22.isAbsolute(relToSource)) continue;
|
|
7202
|
+
const depDest = path22.join(depsTargetRoot, relToRuntime);
|
|
6110
7203
|
queue.push({ src: resolvedSrc, dest: depDest, isDep: true });
|
|
6111
7204
|
}
|
|
6112
7205
|
}
|
|
@@ -6116,7 +7209,7 @@ async function copyRuntime(opts) {
|
|
|
6116
7209
|
|
|
6117
7210
|
// src/cli/shared/subsystems-install-config.ts
|
|
6118
7211
|
import fs12 from "fs";
|
|
6119
|
-
import
|
|
7212
|
+
import path23 from "path";
|
|
6120
7213
|
import yaml2 from "yaml";
|
|
6121
7214
|
function readInstallList(config) {
|
|
6122
7215
|
const raw = config?.subsystems?.install;
|
|
@@ -6125,7 +7218,7 @@ function readInstallList(config) {
|
|
|
6125
7218
|
}
|
|
6126
7219
|
function ensureSubsystemInstalled(configPath, name) {
|
|
6127
7220
|
if (!fs12.existsSync(configPath)) {
|
|
6128
|
-
fs12.mkdirSync(
|
|
7221
|
+
fs12.mkdirSync(path23.dirname(configPath), { recursive: true });
|
|
6129
7222
|
fs12.writeFileSync(
|
|
6130
7223
|
configPath,
|
|
6131
7224
|
`subsystems:
|
|
@@ -6176,13 +7269,13 @@ ${indent}- ${name}${after}`;
|
|
|
6176
7269
|
|
|
6177
7270
|
// src/cli/commands/subsystem.ts
|
|
6178
7271
|
function runtimeRoot() {
|
|
6179
|
-
const pkgRoot =
|
|
6180
|
-
const topLevel =
|
|
7272
|
+
const pkgRoot = path24.resolve(import.meta.dirname, "..", "..", "..");
|
|
7273
|
+
const topLevel = path24.join(pkgRoot, "runtime");
|
|
6181
7274
|
if (fs13.existsSync(topLevel)) return topLevel;
|
|
6182
|
-
return
|
|
7275
|
+
return path24.join(pkgRoot, "dist", "runtime");
|
|
6183
7276
|
}
|
|
6184
7277
|
function subsystemSource(name) {
|
|
6185
|
-
return
|
|
7278
|
+
return path24.join(runtimeRoot(), "subsystems", name);
|
|
6186
7279
|
}
|
|
6187
7280
|
function describeSubsystem(name) {
|
|
6188
7281
|
return SUBSYSTEMS.find((s) => s.name === name) ?? null;
|
|
@@ -6217,7 +7310,7 @@ async function summary2(ctx) {
|
|
|
6217
7310
|
}
|
|
6218
7311
|
body.push(theme.muted("Installed:"));
|
|
6219
7312
|
for (const i of installed) {
|
|
6220
|
-
const rel2 =
|
|
7313
|
+
const rel2 = path24.relative(ctx.cwd, i.path) || i.path;
|
|
6221
7314
|
body.push(
|
|
6222
7315
|
` ${theme.success(icons.check)} ${i.name.padEnd(10)} ${theme.muted(
|
|
6223
7316
|
`${i.backend} backend`
|
|
@@ -6360,14 +7453,14 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6360
7453
|
return 0;
|
|
6361
7454
|
}
|
|
6362
7455
|
const targetRoot = resolveSubsystemsRoot(ctx, this.target);
|
|
6363
|
-
const subsystemTarget =
|
|
7456
|
+
const subsystemTarget = path24.join(targetRoot, desc.name);
|
|
6364
7457
|
const source = subsystemSource(desc.name);
|
|
6365
7458
|
if (!fs13.existsSync(source)) {
|
|
6366
7459
|
printError(`Runtime subsystem source missing: ${source}`);
|
|
6367
7460
|
return 1;
|
|
6368
7461
|
}
|
|
6369
7462
|
if (!this.force) {
|
|
6370
|
-
const gitCheck = checkGitSafety([
|
|
7463
|
+
const gitCheck = checkGitSafety([path24.relative(ctx.cwd, subsystemTarget) || subsystemTarget], ctx.cwd);
|
|
6371
7464
|
if (gitCheck.inRepo && !gitCheck.clean) {
|
|
6372
7465
|
printWarning(
|
|
6373
7466
|
`Uncommitted changes under ${subsystemTarget}. Pass --force to overwrite.`
|
|
@@ -6376,7 +7469,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6376
7469
|
}
|
|
6377
7470
|
}
|
|
6378
7471
|
if (!isJsonMode()) {
|
|
6379
|
-
printInfo(`target = ${
|
|
7472
|
+
printInfo(`target = ${path24.relative(ctx.cwd, subsystemTarget) || subsystemTarget}`);
|
|
6380
7473
|
printInfo(`backend = ${backend}`);
|
|
6381
7474
|
}
|
|
6382
7475
|
const result = await copyRuntime({
|
|
@@ -6385,7 +7478,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6385
7478
|
filter: backendFileFilter(backend, desc.name),
|
|
6386
7479
|
resolveDeps: true,
|
|
6387
7480
|
runtimeRoot: runtimeRoot(),
|
|
6388
|
-
depsTargetRoot:
|
|
7481
|
+
depsTargetRoot: path24.resolve(targetRoot, ".."),
|
|
6389
7482
|
dryRun: this.dryRun
|
|
6390
7483
|
});
|
|
6391
7484
|
const jobsScaffold = desc.name === "jobs" ? runJobsScaffold(ctx.cwd, ctx.config, {
|
|
@@ -6480,14 +7573,14 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6480
7573
|
if (this.dryRun) {
|
|
6481
7574
|
printInfo(`Dry run \u2014 ${result.planned.length} files would be written`);
|
|
6482
7575
|
for (const p of result.planned) {
|
|
6483
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7576
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6484
7577
|
}
|
|
6485
7578
|
if (jobsScaffold?.planned?.length) {
|
|
6486
7579
|
printInfo(
|
|
6487
7580
|
`Jobs scaffold \u2014 ${jobsScaffold.planned.length} template targets`
|
|
6488
7581
|
);
|
|
6489
7582
|
for (const p of jobsScaffold.planned) {
|
|
6490
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7583
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6491
7584
|
}
|
|
6492
7585
|
}
|
|
6493
7586
|
if (eventsScaffold?.planned?.length) {
|
|
@@ -6495,7 +7588,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6495
7588
|
`Events scaffold \u2014 ${eventsScaffold.planned.length} template targets`
|
|
6496
7589
|
);
|
|
6497
7590
|
for (const p of eventsScaffold.planned) {
|
|
6498
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7591
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6499
7592
|
}
|
|
6500
7593
|
}
|
|
6501
7594
|
if (integrationScaffold?.planned?.length) {
|
|
@@ -6503,7 +7596,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6503
7596
|
`Integration scaffold \u2014 ${integrationScaffold.planned.length} template targets`
|
|
6504
7597
|
);
|
|
6505
7598
|
for (const p of integrationScaffold.planned) {
|
|
6506
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7599
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6507
7600
|
}
|
|
6508
7601
|
}
|
|
6509
7602
|
if (bridgeScaffold?.planned?.length) {
|
|
@@ -6511,7 +7604,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6511
7604
|
`Bridge scaffold \u2014 ${bridgeScaffold.planned.length} template targets`
|
|
6512
7605
|
);
|
|
6513
7606
|
for (const p of bridgeScaffold.planned) {
|
|
6514
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7607
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6515
7608
|
}
|
|
6516
7609
|
}
|
|
6517
7610
|
if (observabilityScaffold?.planned?.length) {
|
|
@@ -6519,7 +7612,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6519
7612
|
`Observability scaffold \u2014 ${observabilityScaffold.planned.length} template targets`
|
|
6520
7613
|
);
|
|
6521
7614
|
for (const p of observabilityScaffold.planned) {
|
|
6522
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7615
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6523
7616
|
}
|
|
6524
7617
|
}
|
|
6525
7618
|
if (authScaffold?.planned?.length) {
|
|
@@ -6527,7 +7620,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6527
7620
|
`Auth scaffold \u2014 ${authScaffold.planned.length} template targets`
|
|
6528
7621
|
);
|
|
6529
7622
|
for (const p of authScaffold.planned) {
|
|
6530
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7623
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`);
|
|
6531
7624
|
}
|
|
6532
7625
|
}
|
|
6533
7626
|
return 0;
|
|
@@ -6657,7 +7750,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6657
7750
|
* `--force-config` and always regenerates the barrels).
|
|
6658
7751
|
*/
|
|
6659
7752
|
async executePackageMode(ctx, desc, backend) {
|
|
6660
|
-
const configPath =
|
|
7753
|
+
const configPath = path24.join(ctx.cwd, "codegen.config.yaml");
|
|
6661
7754
|
const installed = configuredSubsystemNames(
|
|
6662
7755
|
ctx.config
|
|
6663
7756
|
);
|
|
@@ -6784,7 +7877,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6784
7877
|
* semantics as jobs/events/integration/bridge.
|
|
6785
7878
|
*/
|
|
6786
7879
|
async executeOpenApiConfig(ctx) {
|
|
6787
|
-
const configPath =
|
|
7880
|
+
const configPath = path24.join(ctx.cwd, "codegen.config.yaml");
|
|
6788
7881
|
const outcome = planConfigBlockAction(configPath, "openapi", this.forceConfig);
|
|
6789
7882
|
if (outcome === "parse-error") {
|
|
6790
7883
|
printError(
|
|
@@ -6803,7 +7896,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6803
7896
|
});
|
|
6804
7897
|
} else {
|
|
6805
7898
|
printInfo(`Dry run \u2014 openapi config block would be ${outcome}`);
|
|
6806
|
-
console.log(` ${theme.muted(icons.arrow)} ${
|
|
7899
|
+
console.log(` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, configPath) || configPath}`);
|
|
6807
7900
|
}
|
|
6808
7901
|
return 0;
|
|
6809
7902
|
}
|
|
@@ -6898,7 +7991,7 @@ var SubsystemInstallCommand = class extends Command3 {
|
|
|
6898
7991
|
);
|
|
6899
7992
|
for (const p of scaffold.planned) {
|
|
6900
7993
|
console.log(
|
|
6901
|
-
` ${theme.muted(icons.arrow)} ${
|
|
7994
|
+
` ${theme.muted(icons.arrow)} ${path24.relative(ctx.cwd, p) || p}`
|
|
6902
7995
|
);
|
|
6903
7996
|
}
|
|
6904
7997
|
return 0;
|
|
@@ -7281,7 +8374,7 @@ function runAuthScaffold(cwd, config, opts) {
|
|
|
7281
8374
|
return { ok: true, planned, configBlockOutcome };
|
|
7282
8375
|
}
|
|
7283
8376
|
if (!fs13.existsSync(locals.envConfigPath)) {
|
|
7284
|
-
fs13.mkdirSync(
|
|
8377
|
+
fs13.mkdirSync(path24.dirname(locals.envConfigPath), { recursive: true });
|
|
7285
8378
|
fs13.writeFileSync(locals.envConfigPath, "", "utf-8");
|
|
7286
8379
|
}
|
|
7287
8380
|
const result = invokeHygen({
|
|
@@ -7318,10 +8411,10 @@ function runAuthScaffold(cwd, config, opts) {
|
|
|
7318
8411
|
return { ok: true, planned, configBlockOutcome };
|
|
7319
8412
|
}
|
|
7320
8413
|
function authIntegrationsExamplesRoot() {
|
|
7321
|
-
const pkgRoot =
|
|
7322
|
-
const topLevel =
|
|
8414
|
+
const pkgRoot = path24.resolve(import.meta.dirname, "..", "..", "..");
|
|
8415
|
+
const topLevel = path24.join(pkgRoot, "examples", "auth-integrations");
|
|
7323
8416
|
if (fs13.existsSync(topLevel)) return topLevel;
|
|
7324
|
-
return
|
|
8417
|
+
return path24.join(pkgRoot, "dist", "examples", "auth-integrations");
|
|
7325
8418
|
}
|
|
7326
8419
|
function copyTreeIdempotent(srcDir, destDir, force, transform) {
|
|
7327
8420
|
const written = [];
|
|
@@ -7329,8 +8422,8 @@ function copyTreeIdempotent(srcDir, destDir, force, transform) {
|
|
|
7329
8422
|
const walk = (src, dest) => {
|
|
7330
8423
|
const entries = fs13.readdirSync(src, { withFileTypes: true });
|
|
7331
8424
|
for (const entry of entries) {
|
|
7332
|
-
const srcPath =
|
|
7333
|
-
const destPath =
|
|
8425
|
+
const srcPath = path24.join(src, entry.name);
|
|
8426
|
+
const destPath = path24.join(dest, entry.name);
|
|
7334
8427
|
if (entry.isDirectory()) {
|
|
7335
8428
|
fs13.mkdirSync(destPath, { recursive: true });
|
|
7336
8429
|
walk(srcPath, destPath);
|
|
@@ -7341,7 +8434,7 @@ function copyTreeIdempotent(srcDir, destDir, force, transform) {
|
|
|
7341
8434
|
skipped.push(destPath);
|
|
7342
8435
|
continue;
|
|
7343
8436
|
}
|
|
7344
|
-
fs13.mkdirSync(
|
|
8437
|
+
fs13.mkdirSync(path24.dirname(destPath), { recursive: true });
|
|
7345
8438
|
const isTextSource = transform && (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx"));
|
|
7346
8439
|
if (isTextSource && transform) {
|
|
7347
8440
|
const raw = fs13.readFileSync(srcPath, "utf-8");
|
|
@@ -7359,16 +8452,16 @@ function copyTreeIdempotent(srcDir, destDir, force, transform) {
|
|
|
7359
8452
|
}
|
|
7360
8453
|
var AUTH_BARE_IMPORT_RE = /(['"])@pattern-stack\/codegen\/runtime\/subsystems\/auth\1/g;
|
|
7361
8454
|
function buildAuthImportRewriter(subsystemsRoot) {
|
|
7362
|
-
const authRoot =
|
|
8455
|
+
const authRoot = path24.join(subsystemsRoot, "auth");
|
|
7363
8456
|
return (content, destPath) => {
|
|
7364
8457
|
if (!AUTH_BARE_IMPORT_RE.test(content)) {
|
|
7365
8458
|
AUTH_BARE_IMPORT_RE.lastIndex = 0;
|
|
7366
8459
|
return content;
|
|
7367
8460
|
}
|
|
7368
8461
|
AUTH_BARE_IMPORT_RE.lastIndex = 0;
|
|
7369
|
-
let rel2 =
|
|
8462
|
+
let rel2 = path24.relative(path24.dirname(destPath), authRoot);
|
|
7370
8463
|
if (!rel2.startsWith(".")) rel2 = `./${rel2}`;
|
|
7371
|
-
const relPosix = rel2.split(
|
|
8464
|
+
const relPosix = rel2.split(path24.sep).join("/");
|
|
7372
8465
|
return content.replace(
|
|
7373
8466
|
AUTH_BARE_IMPORT_RE,
|
|
7374
8467
|
(_match, quote) => `${quote}${relPosix}${quote}`
|
|
@@ -7390,9 +8483,9 @@ function runAuthIntegrationsScaffold(cwd, config, opts) {
|
|
|
7390
8483
|
error: `auth-integrations starter source missing: ${examplesRoot}`
|
|
7391
8484
|
};
|
|
7392
8485
|
}
|
|
7393
|
-
const adaptersSrc =
|
|
7394
|
-
const adaptersDest =
|
|
7395
|
-
const connectionYamlSrc =
|
|
8486
|
+
const adaptersSrc = path24.join(examplesRoot, "runtime", "connections");
|
|
8487
|
+
const adaptersDest = path24.join(locals.vendorRoot, "connections");
|
|
8488
|
+
const connectionYamlSrc = path24.join(
|
|
7396
8489
|
examplesRoot,
|
|
7397
8490
|
"definitions",
|
|
7398
8491
|
"entities",
|
|
@@ -7424,7 +8517,7 @@ function runAuthIntegrationsScaffold(cwd, config, opts) {
|
|
|
7424
8517
|
if (fs13.existsSync(connectionYamlDest) && !opts.force) {
|
|
7425
8518
|
yamlSkipped = true;
|
|
7426
8519
|
} else if (fs13.existsSync(connectionYamlSrc)) {
|
|
7427
|
-
fs13.mkdirSync(
|
|
8520
|
+
fs13.mkdirSync(path24.dirname(connectionYamlDest), { recursive: true });
|
|
7428
8521
|
fs13.copyFileSync(connectionYamlSrc, connectionYamlDest);
|
|
7429
8522
|
yamlWritten = true;
|
|
7430
8523
|
}
|
|
@@ -7490,7 +8583,7 @@ var SubsystemListCommand = class extends Command3 {
|
|
|
7490
8583
|
name: s.name,
|
|
7491
8584
|
status: inst ? inst.status : "available",
|
|
7492
8585
|
backend: inst ? inst.backend : null,
|
|
7493
|
-
path: inst ?
|
|
8586
|
+
path: inst ? path24.relative(ctx.cwd, inst.path) || inst.path : null
|
|
7494
8587
|
};
|
|
7495
8588
|
});
|
|
7496
8589
|
if (isJsonMode()) {
|
|
@@ -7586,7 +8679,7 @@ var SubsystemRemoveCommand = class extends Command3 {
|
|
|
7586
8679
|
return 1;
|
|
7587
8680
|
}
|
|
7588
8681
|
if (!this.force) {
|
|
7589
|
-
const rel2 =
|
|
8682
|
+
const rel2 = path24.relative(ctx.cwd, subsystemDir) || subsystemDir;
|
|
7590
8683
|
const gitCheck = checkGitSafety([rel2], ctx.cwd);
|
|
7591
8684
|
if (gitCheck.inRepo && !gitCheck.clean) {
|
|
7592
8685
|
printWarning(
|
|
@@ -7622,7 +8715,7 @@ var SubsystemRemoveCommand = class extends Command3 {
|
|
|
7622
8715
|
return 0;
|
|
7623
8716
|
}
|
|
7624
8717
|
printSuccess(
|
|
7625
|
-
`${desc.name} subsystem removed (${
|
|
8718
|
+
`${desc.name} subsystem removed (${path24.relative(ctx.cwd, subsystemDir) || subsystemDir}).`
|
|
7626
8719
|
);
|
|
7627
8720
|
if (barrelRegenerated) {
|
|
7628
8721
|
printInfo("Regenerated <generated>/subsystems.ts barrel.");
|
|
@@ -7651,27 +8744,27 @@ var subsystem_default = subsystemNoun;
|
|
|
7651
8744
|
|
|
7652
8745
|
// src/cli/commands/project.ts
|
|
7653
8746
|
import fs19 from "fs";
|
|
7654
|
-
import
|
|
8747
|
+
import path30 from "path";
|
|
7655
8748
|
import readline from "readline";
|
|
7656
8749
|
import { Command as Command7, Option as Option7 } from "clipanion";
|
|
7657
8750
|
import { stringify as stringifyYaml2 } from "yaml";
|
|
7658
8751
|
|
|
7659
8752
|
// src/cli/shared/init-scaffold.ts
|
|
7660
8753
|
import fs14 from "fs";
|
|
7661
|
-
import
|
|
8754
|
+
import path25 from "path";
|
|
7662
8755
|
import { stringify as stringifyYaml } from "yaml";
|
|
7663
8756
|
function runtimeRoot2() {
|
|
7664
|
-
const pkgRoot =
|
|
7665
|
-
const topLevel =
|
|
8757
|
+
const pkgRoot = path25.resolve(import.meta.dirname, "..", "..", "..");
|
|
8758
|
+
const topLevel = path25.join(pkgRoot, "runtime");
|
|
7666
8759
|
if (fs14.existsSync(topLevel)) return topLevel;
|
|
7667
|
-
return
|
|
8760
|
+
return path25.join(pkgRoot, "dist", "runtime");
|
|
7668
8761
|
}
|
|
7669
8762
|
function resolveRuntimePath(cwd) {
|
|
7670
|
-
const shimDir =
|
|
7671
|
-
return
|
|
8763
|
+
const shimDir = path25.join(cwd, "src", "shared", "base-classes");
|
|
8764
|
+
return path25.relative(shimDir, runtimeRoot2());
|
|
7672
8765
|
}
|
|
7673
8766
|
function loadRuntimeFile(relPath) {
|
|
7674
|
-
return fs14.readFileSync(
|
|
8767
|
+
return fs14.readFileSync(path25.join(runtimeRoot2(), relPath), "utf-8");
|
|
7675
8768
|
}
|
|
7676
8769
|
var VENDORED_RUNTIME_FILES = [
|
|
7677
8770
|
// base-classes — consumer-facing inheritance targets
|
|
@@ -8087,8 +9180,38 @@ function mergeTsconfig(raw) {
|
|
|
8087
9180
|
unchanged: false
|
|
8088
9181
|
};
|
|
8089
9182
|
}
|
|
9183
|
+
function mergeFrontendDeps(raw) {
|
|
9184
|
+
let parsed;
|
|
9185
|
+
try {
|
|
9186
|
+
parsed = JSON.parse(raw);
|
|
9187
|
+
} catch (err) {
|
|
9188
|
+
return {
|
|
9189
|
+
content: raw,
|
|
9190
|
+
added: [],
|
|
9191
|
+
unchanged: true,
|
|
9192
|
+
parseError: err instanceof Error ? err.message : String(err)
|
|
9193
|
+
};
|
|
9194
|
+
}
|
|
9195
|
+
const deps = parsed.dependencies ?? {};
|
|
9196
|
+
const added = [];
|
|
9197
|
+
for (const [pkg, range] of Object.entries(FRONTEND_EMITTED_DEPS)) {
|
|
9198
|
+
if (!(pkg in deps)) {
|
|
9199
|
+
deps[pkg] = range;
|
|
9200
|
+
added.push(pkg);
|
|
9201
|
+
}
|
|
9202
|
+
}
|
|
9203
|
+
if (added.length === 0) {
|
|
9204
|
+
return { content: raw, added: [], unchanged: true };
|
|
9205
|
+
}
|
|
9206
|
+
parsed.dependencies = deps;
|
|
9207
|
+
return {
|
|
9208
|
+
content: JSON.stringify(parsed, null, 2) + "\n",
|
|
9209
|
+
added,
|
|
9210
|
+
unchanged: false
|
|
9211
|
+
};
|
|
9212
|
+
}
|
|
8090
9213
|
function relOf(cwd, abs) {
|
|
8091
|
-
return
|
|
9214
|
+
return path25.relative(cwd, abs) || abs;
|
|
8092
9215
|
}
|
|
8093
9216
|
function fileEntry(cwd, absPath, content, opts) {
|
|
8094
9217
|
const exists = fs14.existsSync(absPath);
|
|
@@ -8139,7 +9262,7 @@ async function buildInitPlan(ctx, options) {
|
|
|
8139
9262
|
const runtimePath = options.runtimePath ?? resolveRuntimePath(cwd);
|
|
8140
9263
|
const entries = [];
|
|
8141
9264
|
{
|
|
8142
|
-
const configPath =
|
|
9265
|
+
const configPath = path25.join(cwd, "codegen.config.yaml");
|
|
8143
9266
|
const config = {
|
|
8144
9267
|
// Runtime mode (ADR-037). `package` (default) imports the runtime from
|
|
8145
9268
|
// `@pattern-stack/codegen/*`; `vendored` imports it via `@shared/*` and
|
|
@@ -8174,7 +9297,7 @@ async function buildInitPlan(ctx, options) {
|
|
|
8174
9297
|
entries.push(fileEntry(cwd, configPath, content, { force }));
|
|
8175
9298
|
}
|
|
8176
9299
|
{
|
|
8177
|
-
const tsconfigPath =
|
|
9300
|
+
const tsconfigPath = path25.join(cwd, "tsconfig.json");
|
|
8178
9301
|
if (fs14.existsSync(tsconfigPath)) {
|
|
8179
9302
|
const raw = fs14.readFileSync(tsconfigPath, "utf-8");
|
|
8180
9303
|
const merged = mergeTsconfig(raw);
|
|
@@ -8221,7 +9344,7 @@ async function buildInitPlan(ctx, options) {
|
|
|
8221
9344
|
entries.push(
|
|
8222
9345
|
fileEntry(
|
|
8223
9346
|
cwd,
|
|
8224
|
-
|
|
9347
|
+
path25.join(cwd, "src", "shared", "database", "database.module.ts"),
|
|
8225
9348
|
databaseModuleContent(runtimeMode),
|
|
8226
9349
|
{ force }
|
|
8227
9350
|
)
|
|
@@ -8229,14 +9352,14 @@ async function buildInitPlan(ctx, options) {
|
|
|
8229
9352
|
if (runtimeMode === "vendored") {
|
|
8230
9353
|
for (const v of VENDORED_RUNTIME_FILES) {
|
|
8231
9354
|
entries.push(
|
|
8232
|
-
fileEntry(cwd,
|
|
9355
|
+
fileEntry(cwd, path25.join(cwd, v.target), loadRuntimeFile(v.runtime), { force })
|
|
8233
9356
|
);
|
|
8234
9357
|
}
|
|
8235
9358
|
}
|
|
8236
9359
|
entries.push(
|
|
8237
9360
|
fileEntry(
|
|
8238
9361
|
cwd,
|
|
8239
|
-
|
|
9362
|
+
path25.join(cwd, "src", "generated", "modules.ts"),
|
|
8240
9363
|
emptyModulesBarrel(),
|
|
8241
9364
|
{ force }
|
|
8242
9365
|
)
|
|
@@ -8244,13 +9367,13 @@ async function buildInitPlan(ctx, options) {
|
|
|
8244
9367
|
entries.push(
|
|
8245
9368
|
fileEntry(
|
|
8246
9369
|
cwd,
|
|
8247
|
-
|
|
9370
|
+
path25.join(cwd, "src", "generated", "schema.ts"),
|
|
8248
9371
|
emptySchemaBarrel(),
|
|
8249
9372
|
{ force }
|
|
8250
9373
|
)
|
|
8251
9374
|
);
|
|
8252
9375
|
{
|
|
8253
|
-
const appModulePath =
|
|
9376
|
+
const appModulePath = path25.join(cwd, "src", "app.module.ts");
|
|
8254
9377
|
if (!fs14.existsSync(appModulePath)) {
|
|
8255
9378
|
entries.push({
|
|
8256
9379
|
path: appModulePath,
|
|
@@ -8268,7 +9391,7 @@ async function buildInitPlan(ctx, options) {
|
|
|
8268
9391
|
}
|
|
8269
9392
|
}
|
|
8270
9393
|
{
|
|
8271
|
-
const mainPath =
|
|
9394
|
+
const mainPath = path25.join(cwd, "src", "main.ts");
|
|
8272
9395
|
if (!fs14.existsSync(mainPath)) {
|
|
8273
9396
|
entries.push({
|
|
8274
9397
|
path: mainPath,
|
|
@@ -8286,7 +9409,7 @@ async function buildInitPlan(ctx, options) {
|
|
|
8286
9409
|
}
|
|
8287
9410
|
}
|
|
8288
9411
|
{
|
|
8289
|
-
const schemaPath =
|
|
9412
|
+
const schemaPath = path25.join(cwd, "src", "schema.ts");
|
|
8290
9413
|
if (!fs14.existsSync(schemaPath)) {
|
|
8291
9414
|
entries.push({
|
|
8292
9415
|
path: schemaPath,
|
|
@@ -8303,12 +9426,12 @@ async function buildInitPlan(ctx, options) {
|
|
|
8303
9426
|
});
|
|
8304
9427
|
}
|
|
8305
9428
|
}
|
|
8306
|
-
entries.push(dirEntry(cwd,
|
|
9429
|
+
entries.push(dirEntry(cwd, path25.join(cwd, "entities")));
|
|
8307
9430
|
{
|
|
8308
|
-
const entitiesDir =
|
|
8309
|
-
const examplePath =
|
|
9431
|
+
const entitiesDir = path25.join(cwd, "entities");
|
|
9432
|
+
const examplePath = path25.join(entitiesDir, "example.yaml");
|
|
8310
9433
|
const hasOtherYamls = fs14.existsSync(entitiesDir) && findYamlFiles(entitiesDir).some(
|
|
8311
|
-
(f) =>
|
|
9434
|
+
(f) => path25.basename(f) !== "example.yaml"
|
|
8312
9435
|
);
|
|
8313
9436
|
if (fs14.existsSync(examplePath)) {
|
|
8314
9437
|
entries.push({
|
|
@@ -8333,6 +9456,46 @@ async function buildInitPlan(ctx, options) {
|
|
|
8333
9456
|
});
|
|
8334
9457
|
}
|
|
8335
9458
|
}
|
|
9459
|
+
if (frontend) {
|
|
9460
|
+
const frontendSrc = ctx.config?.paths?.frontend_src ?? "apps/frontend/src";
|
|
9461
|
+
const frontendRoot = path25.dirname(path25.join(cwd, frontendSrc));
|
|
9462
|
+
const pkgPath = path25.join(frontendRoot, "package.json");
|
|
9463
|
+
const depsList = Object.entries(FRONTEND_EMITTED_DEPS).map(([p, r]) => `${p}@${r}`).join(", ");
|
|
9464
|
+
if (fs14.existsSync(pkgPath)) {
|
|
9465
|
+
const raw = fs14.readFileSync(pkgPath, "utf-8");
|
|
9466
|
+
const merged = mergeFrontendDeps(raw);
|
|
9467
|
+
if (merged.parseError) {
|
|
9468
|
+
entries.push({
|
|
9469
|
+
path: pkgPath,
|
|
9470
|
+
relPath: relOf(cwd, pkgPath),
|
|
9471
|
+
action: "skip",
|
|
9472
|
+
reason: `unable to parse (${merged.parseError}); add frontend deps manually: ${depsList}`
|
|
9473
|
+
});
|
|
9474
|
+
} else if (merged.unchanged) {
|
|
9475
|
+
entries.push({
|
|
9476
|
+
path: pkgPath,
|
|
9477
|
+
relPath: relOf(cwd, pkgPath),
|
|
9478
|
+
action: "skip",
|
|
9479
|
+
reason: "frontend deps already present"
|
|
9480
|
+
});
|
|
9481
|
+
} else {
|
|
9482
|
+
entries.push({
|
|
9483
|
+
path: pkgPath,
|
|
9484
|
+
relPath: relOf(cwd, pkgPath),
|
|
9485
|
+
action: "merge",
|
|
9486
|
+
content: merged.content,
|
|
9487
|
+
reason: `add frontend deps: ${merged.added.join(", ")}`
|
|
9488
|
+
});
|
|
9489
|
+
}
|
|
9490
|
+
} else {
|
|
9491
|
+
entries.push({
|
|
9492
|
+
path: pkgPath,
|
|
9493
|
+
relPath: relOf(cwd, pkgPath),
|
|
9494
|
+
action: "skip",
|
|
9495
|
+
reason: `frontend enabled but ${relOf(cwd, pkgPath)} not found \u2014 install: ${depsList}`
|
|
9496
|
+
});
|
|
9497
|
+
}
|
|
9498
|
+
}
|
|
8336
9499
|
return {
|
|
8337
9500
|
entries,
|
|
8338
9501
|
summary: {
|
|
@@ -8364,7 +9527,7 @@ function writePlan(plan) {
|
|
|
8364
9527
|
skipped.push(e);
|
|
8365
9528
|
continue;
|
|
8366
9529
|
}
|
|
8367
|
-
fs14.mkdirSync(
|
|
9530
|
+
fs14.mkdirSync(path25.dirname(e.path), { recursive: true });
|
|
8368
9531
|
fs14.writeFileSync(e.path, e.content, "utf-8");
|
|
8369
9532
|
if (e.action === "create") created.push(e);
|
|
8370
9533
|
else if (e.action === "merge") merged.push(e);
|
|
@@ -8375,7 +9538,7 @@ function writePlan(plan) {
|
|
|
8375
9538
|
|
|
8376
9539
|
// src/cli/commands/project-upgrade-openapi.ts
|
|
8377
9540
|
import fs15 from "fs";
|
|
8378
|
-
import
|
|
9541
|
+
import path26 from "path";
|
|
8379
9542
|
import { Command as Command4, Option as Option4 } from "clipanion";
|
|
8380
9543
|
import { Project, IndentationText, QuoteKind, NewLineKind } from "ts-morph";
|
|
8381
9544
|
|
|
@@ -8614,31 +9777,31 @@ var MAIN_SWAGGER_IMPORTS = [
|
|
|
8614
9777
|
"import { OPENAPI_REGISTRY, OpenApiRegistry } from './shared/openapi';"
|
|
8615
9778
|
];
|
|
8616
9779
|
function runtimeRoot3() {
|
|
8617
|
-
const pkgRoot =
|
|
8618
|
-
const topLevel =
|
|
9780
|
+
const pkgRoot = path26.resolve(import.meta.dirname, "..", "..", "..");
|
|
9781
|
+
const topLevel = path26.join(pkgRoot, "runtime");
|
|
8619
9782
|
if (fs15.existsSync(topLevel)) return topLevel;
|
|
8620
|
-
return
|
|
9783
|
+
return path26.join(pkgRoot, "dist", "runtime");
|
|
8621
9784
|
}
|
|
8622
9785
|
function loadRuntimeFile2(rel2) {
|
|
8623
|
-
return fs15.readFileSync(
|
|
9786
|
+
return fs15.readFileSync(path26.join(runtimeRoot3(), rel2), "utf-8");
|
|
8624
9787
|
}
|
|
8625
9788
|
function resolveProjectRoot(startDir) {
|
|
8626
|
-
let dir =
|
|
9789
|
+
let dir = path26.resolve(startDir);
|
|
8627
9790
|
for (let i = 0; i < 16; i++) {
|
|
8628
|
-
if (fs15.existsSync(
|
|
9791
|
+
if (fs15.existsSync(path26.join(dir, "codegen.config.yaml")) || fs15.existsSync(path26.join(dir, "package.json"))) {
|
|
8629
9792
|
return dir;
|
|
8630
9793
|
}
|
|
8631
|
-
const parent =
|
|
9794
|
+
const parent = path26.dirname(dir);
|
|
8632
9795
|
if (parent === dir) break;
|
|
8633
9796
|
dir = parent;
|
|
8634
9797
|
}
|
|
8635
|
-
return
|
|
9798
|
+
return path26.resolve(startDir);
|
|
8636
9799
|
}
|
|
8637
9800
|
async function runUpgradeOpenapi(opts) {
|
|
8638
9801
|
const { projectRoot, dryRun, force } = opts;
|
|
8639
9802
|
const changes = [];
|
|
8640
9803
|
for (const v of OPENAPI_VENDORED_FILES) {
|
|
8641
|
-
const target =
|
|
9804
|
+
const target = path26.join(projectRoot, v.target);
|
|
8642
9805
|
const exists = fs15.existsSync(target);
|
|
8643
9806
|
const newContent = loadRuntimeFile2(v.runtime);
|
|
8644
9807
|
if (exists && !force) {
|
|
@@ -8654,7 +9817,7 @@ async function runUpgradeOpenapi(opts) {
|
|
|
8654
9817
|
}
|
|
8655
9818
|
} else {
|
|
8656
9819
|
if (!dryRun) {
|
|
8657
|
-
fs15.mkdirSync(
|
|
9820
|
+
fs15.mkdirSync(path26.dirname(target), { recursive: true });
|
|
8658
9821
|
fs15.writeFileSync(target, newContent);
|
|
8659
9822
|
}
|
|
8660
9823
|
changes.push({
|
|
@@ -8663,7 +9826,7 @@ async function runUpgradeOpenapi(opts) {
|
|
|
8663
9826
|
});
|
|
8664
9827
|
}
|
|
8665
9828
|
}
|
|
8666
|
-
const appModulePath =
|
|
9829
|
+
const appModulePath = path26.join(projectRoot, "src", "app.module.ts");
|
|
8667
9830
|
if (!fs15.existsSync(appModulePath)) {
|
|
8668
9831
|
return {
|
|
8669
9832
|
projectRoot,
|
|
@@ -8747,7 +9910,7 @@ async function runUpgradeOpenapi(opts) {
|
|
|
8747
9910
|
} else {
|
|
8748
9911
|
changes.push({ path: "src/app.module.ts", action: "unchanged" });
|
|
8749
9912
|
}
|
|
8750
|
-
const mainPath =
|
|
9913
|
+
const mainPath = path26.join(projectRoot, "src", "main.ts");
|
|
8751
9914
|
if (fs15.existsSync(mainPath)) {
|
|
8752
9915
|
const mainSource = project.addSourceFileAtPath(mainPath);
|
|
8753
9916
|
const mainBefore = mainSource.getFullText();
|
|
@@ -8828,7 +9991,7 @@ var ProjectUpgradeOpenapiCommand = class extends Command4 {
|
|
|
8828
9991
|
json = Option4.Boolean("--json", false);
|
|
8829
9992
|
async execute() {
|
|
8830
9993
|
if (this.json) setJsonMode(true);
|
|
8831
|
-
const startDir = this.pathOpt ?
|
|
9994
|
+
const startDir = this.pathOpt ? path26.resolve(this.pathOpt) : process.cwd();
|
|
8832
9995
|
if (!fs15.existsSync(startDir)) {
|
|
8833
9996
|
printError(`Directory not found: ${startDir}`);
|
|
8834
9997
|
return 1;
|
|
@@ -8905,17 +10068,17 @@ ${CONSUMER_SETUP_POINTER}
|
|
|
8905
10068
|
|
|
8906
10069
|
// src/cli/commands/project-update.ts
|
|
8907
10070
|
import fs18 from "fs";
|
|
8908
|
-
import
|
|
10071
|
+
import path29 from "path";
|
|
8909
10072
|
import { Command as Command6, Option as Option6 } from "clipanion";
|
|
8910
10073
|
|
|
8911
10074
|
// src/cli/commands/skills.ts
|
|
8912
10075
|
import fs17 from "fs";
|
|
8913
|
-
import
|
|
10076
|
+
import path28 from "path";
|
|
8914
10077
|
import { Command as Command5, Option as Option5 } from "clipanion";
|
|
8915
10078
|
|
|
8916
10079
|
// src/cli/shared/tree-copier.ts
|
|
8917
10080
|
import fs16 from "fs";
|
|
8918
|
-
import
|
|
10081
|
+
import path27 from "path";
|
|
8919
10082
|
var TEXT_EXTENSIONS = [".ts", ".tsx", ".md", ".mdx", ".yaml", ".yml", ".json"];
|
|
8920
10083
|
function isTextFile(name) {
|
|
8921
10084
|
return TEXT_EXTENSIONS.some((ext) => name.endsWith(ext));
|
|
@@ -8932,17 +10095,17 @@ function copyTreeWithReport(opts) {
|
|
|
8932
10095
|
throw new Error(`tree-copier source directory not found: ${srcDir}`);
|
|
8933
10096
|
}
|
|
8934
10097
|
const walk = (relDir) => {
|
|
8935
|
-
const absSrcDir =
|
|
10098
|
+
const absSrcDir = path27.join(srcDir, relDir);
|
|
8936
10099
|
for (const entry of fs16.readdirSync(absSrcDir, { withFileTypes: true })) {
|
|
8937
|
-
const relPath = relDir ?
|
|
8938
|
-
const absSrc =
|
|
10100
|
+
const relPath = relDir ? path27.posix.join(relDir, entry.name) : entry.name;
|
|
10101
|
+
const absSrc = path27.join(srcDir, relPath);
|
|
8939
10102
|
if (entry.isDirectory()) {
|
|
8940
10103
|
walk(relPath);
|
|
8941
10104
|
continue;
|
|
8942
10105
|
}
|
|
8943
10106
|
if (!entry.isFile()) continue;
|
|
8944
10107
|
if (include && !include(relPath)) continue;
|
|
8945
|
-
const dest =
|
|
10108
|
+
const dest = path27.join(destDir, relPath);
|
|
8946
10109
|
let content = fs16.readFileSync(absSrc, "utf-8");
|
|
8947
10110
|
if (transform && isTextFile(entry.name)) {
|
|
8948
10111
|
content = transform(content, dest);
|
|
@@ -8957,7 +10120,7 @@ function copyTreeWithReport(opts) {
|
|
|
8957
10120
|
action = "updated";
|
|
8958
10121
|
}
|
|
8959
10122
|
if (!dryRun && action !== "unchanged") {
|
|
8960
|
-
fs16.mkdirSync(
|
|
10123
|
+
fs16.mkdirSync(path27.dirname(dest), { recursive: true });
|
|
8961
10124
|
fs16.writeFileSync(dest, content, "utf-8");
|
|
8962
10125
|
}
|
|
8963
10126
|
const record = { relPath, dest, action };
|
|
@@ -8971,13 +10134,13 @@ function copyTreeWithReport(opts) {
|
|
|
8971
10134
|
|
|
8972
10135
|
// src/cli/commands/skills.ts
|
|
8973
10136
|
function consumerSkillsRoot() {
|
|
8974
|
-
const pkgRoot =
|
|
8975
|
-
const topLevel =
|
|
10137
|
+
const pkgRoot = path28.resolve(import.meta.dirname, "..", "..", "..");
|
|
10138
|
+
const topLevel = path28.join(pkgRoot, "consumer-skills");
|
|
8976
10139
|
if (fs17.existsSync(topLevel)) return topLevel;
|
|
8977
|
-
return
|
|
10140
|
+
return path28.join(pkgRoot, "dist", "consumer-skills");
|
|
8978
10141
|
}
|
|
8979
10142
|
function skillsTargetDir(cwd) {
|
|
8980
|
-
return
|
|
10143
|
+
return path28.join(cwd, ".claude", "skills");
|
|
8981
10144
|
}
|
|
8982
10145
|
function availableSkills() {
|
|
8983
10146
|
const root = consumerSkillsRoot();
|
|
@@ -9024,13 +10187,13 @@ async function summary3(ctx) {
|
|
|
9024
10187
|
return {
|
|
9025
10188
|
title: "skills",
|
|
9026
10189
|
body,
|
|
9027
|
-
footer: `${installedCount} of ${skills.length} skills installed \u2192 ${
|
|
10190
|
+
footer: `${installedCount} of ${skills.length} skills installed \u2192 ${path28.relative(ctx.cwd, targetDir) || targetDir}`
|
|
9028
10191
|
};
|
|
9029
10192
|
}
|
|
9030
10193
|
async function hints3(ctx) {
|
|
9031
10194
|
const skills = availableSkills();
|
|
9032
10195
|
const targetDir = skillsTargetDir(ctx.cwd);
|
|
9033
|
-
const allPresent = skills.length > 0 && fs17.existsSync(targetDir) && skills.every((s) => fs17.existsSync(
|
|
10196
|
+
const allPresent = skills.length > 0 && fs17.existsSync(targetDir) && skills.every((s) => fs17.existsSync(path28.join(targetDir, s)));
|
|
9034
10197
|
if (allPresent) {
|
|
9035
10198
|
return [
|
|
9036
10199
|
{ command: "codegen update", description: "Re-sync skills + runtime after a package bump" }
|
|
@@ -9105,7 +10268,7 @@ var SkillsInstallCommand = class extends Command5 {
|
|
|
9105
10268
|
});
|
|
9106
10269
|
return 0;
|
|
9107
10270
|
}
|
|
9108
|
-
printInfo(`target = ${
|
|
10271
|
+
printInfo(`target = ${path28.relative(ctx.cwd, result.targetDir) || result.targetDir}`);
|
|
9109
10272
|
console.log("");
|
|
9110
10273
|
renderTreeReport(report);
|
|
9111
10274
|
console.log("");
|
|
@@ -9133,7 +10296,7 @@ var SkillsListCommand = class extends Command5 {
|
|
|
9133
10296
|
const skills = availableSkills();
|
|
9134
10297
|
const targetDir = skillsTargetDir(ctx.cwd);
|
|
9135
10298
|
const rows = skills.map((name) => {
|
|
9136
|
-
const dir =
|
|
10299
|
+
const dir = path28.join(targetDir, name);
|
|
9137
10300
|
return { name, status: fs17.existsSync(dir) ? "installed" : "available" };
|
|
9138
10301
|
});
|
|
9139
10302
|
if (isJsonMode()) {
|
|
@@ -9163,7 +10326,7 @@ var NON_RUNTIME_SUBSYSTEMS = /* @__PURE__ */ new Set(["openapi-config", "auth-in
|
|
|
9163
10326
|
function syncVendoredRuntime(cwd, write) {
|
|
9164
10327
|
const changes = [];
|
|
9165
10328
|
for (const v of VENDORED_RUNTIME_FILES) {
|
|
9166
|
-
const dest =
|
|
10329
|
+
const dest = path29.join(cwd, v.target);
|
|
9167
10330
|
const content = loadRuntimeFile(v.runtime);
|
|
9168
10331
|
const existing = fs18.existsSync(dest) ? fs18.readFileSync(dest, "utf-8") : null;
|
|
9169
10332
|
let action;
|
|
@@ -9171,7 +10334,7 @@ function syncVendoredRuntime(cwd, write) {
|
|
|
9171
10334
|
else if (existing === content) action = "unchanged";
|
|
9172
10335
|
else action = "updated";
|
|
9173
10336
|
if (write && action !== "unchanged") {
|
|
9174
|
-
fs18.mkdirSync(
|
|
10337
|
+
fs18.mkdirSync(path29.dirname(dest), { recursive: true });
|
|
9175
10338
|
fs18.writeFileSync(dest, content, "utf-8");
|
|
9176
10339
|
}
|
|
9177
10340
|
changes.push({ path: v.target, action });
|
|
@@ -9186,14 +10349,14 @@ async function syncSubsystemRuntime(cwd, inst, write) {
|
|
|
9186
10349
|
if (!fs18.existsSync(source)) {
|
|
9187
10350
|
return { name: inst.name, changes: [], skippedReason: "no runtime source in package" };
|
|
9188
10351
|
}
|
|
9189
|
-
const subsystemsRoot =
|
|
10352
|
+
const subsystemsRoot = path29.dirname(inst.path);
|
|
9190
10353
|
const result = await copyRuntime({
|
|
9191
10354
|
sourceDir: source,
|
|
9192
10355
|
targetDir: inst.path,
|
|
9193
10356
|
filter: backendFileFilter(inst.backend, inst.name),
|
|
9194
10357
|
resolveDeps: true,
|
|
9195
10358
|
runtimeRoot: runtimeRoot2(),
|
|
9196
|
-
depsTargetRoot:
|
|
10359
|
+
depsTargetRoot: path29.resolve(subsystemsRoot, ".."),
|
|
9197
10360
|
dryRun: !write,
|
|
9198
10361
|
// Refresh files already vendored for this subsystem; never install new
|
|
9199
10362
|
// ones (that's `subsystem install`). copyRuntime classifies accurately
|
|
@@ -9207,7 +10370,7 @@ async function syncSubsystemRuntime(cwd, inst, write) {
|
|
|
9207
10370
|
return { name: inst.name, changes };
|
|
9208
10371
|
}
|
|
9209
10372
|
function rel(cwd, abs) {
|
|
9210
|
-
return
|
|
10373
|
+
return path29.relative(cwd, abs) || abs;
|
|
9211
10374
|
}
|
|
9212
10375
|
var ProjectUpdateCommand = class extends Command6 {
|
|
9213
10376
|
static paths = [["project", "update"]];
|
|
@@ -9246,7 +10409,7 @@ var ProjectUpdateCommand = class extends Command6 {
|
|
|
9246
10409
|
const installed = this.skipSubsystems ? [] : await detectInstalledSubsystems(ctx);
|
|
9247
10410
|
if (!this.dryRun && !this.force) {
|
|
9248
10411
|
const vendoredDry = syncVendoredRuntime(ctx.cwd, false);
|
|
9249
|
-
const vendoredDirtyCandidates = vendoredDry.filter((c) => c.action === "updated").map((c) =>
|
|
10412
|
+
const vendoredDirtyCandidates = vendoredDry.filter((c) => c.action === "updated").map((c) => path29.join(ctx.cwd, c.path));
|
|
9250
10413
|
const skillDirtyCandidates = this.skipSkills ? [] : runSkillsInstall({ cwd: ctx.cwd, dryRun: true }).report?.updated.map((e) => e.dest) ?? [];
|
|
9251
10414
|
const subsystemDirs = installed.filter((i) => !NON_RUNTIME_SUBSYSTEMS.has(i.name)).map((i) => i.path);
|
|
9252
10415
|
const gate = checkGitSafety(
|
|
@@ -9597,8 +10760,8 @@ var ProjectScanCommand = class extends Command7 {
|
|
|
9597
10760
|
cwd = Option7.String("--cwd", { required: false });
|
|
9598
10761
|
async execute() {
|
|
9599
10762
|
if (this.json) setJsonMode(true);
|
|
9600
|
-
const baseCwd = this.cwd ?
|
|
9601
|
-
const target = this.directory ?
|
|
10763
|
+
const baseCwd = this.cwd ? path30.resolve(this.cwd) : process.cwd();
|
|
10764
|
+
const target = this.directory ? path30.resolve(baseCwd, this.directory) : baseCwd;
|
|
9602
10765
|
if (!fs19.existsSync(target)) {
|
|
9603
10766
|
printError(`Directory not found: ${target}`);
|
|
9604
10767
|
return 1;
|
|
@@ -9649,7 +10812,7 @@ var ProjectScanCommand = class extends Command7 {
|
|
|
9649
10812
|
`architecture: ${profile.architecture.evidence.join(", ") || "\u2014"}`
|
|
9650
10813
|
]);
|
|
9651
10814
|
}
|
|
9652
|
-
const outPath =
|
|
10815
|
+
const outPath = path30.join(target, "codegen.config.yaml");
|
|
9653
10816
|
const existsNow = fs19.existsSync(outPath);
|
|
9654
10817
|
if (this.dryRun) {
|
|
9655
10818
|
console.log("");
|
|
@@ -9770,8 +10933,8 @@ var ProjectInspectCommand = class extends Command7 {
|
|
|
9770
10933
|
return 2;
|
|
9771
10934
|
}
|
|
9772
10935
|
resolveEntitiesDir(ctx) {
|
|
9773
|
-
if (this.dir) return
|
|
9774
|
-
return ctx.entitiesDir ??
|
|
10936
|
+
if (this.dir) return path30.resolve(ctx.cwd, this.dir);
|
|
10937
|
+
return ctx.entitiesDir ?? path30.resolve(ctx.cwd, "entities");
|
|
9775
10938
|
}
|
|
9776
10939
|
async runAnalysis(ctx, kind) {
|
|
9777
10940
|
const entitiesDir = this.resolveEntitiesDir(ctx);
|
|
@@ -9974,15 +11137,15 @@ var ProjectGraphCommand = class extends Command7 {
|
|
|
9974
11137
|
json: this.json,
|
|
9975
11138
|
skipDetection: true
|
|
9976
11139
|
});
|
|
9977
|
-
const entitiesDir = this.dir ?
|
|
11140
|
+
const entitiesDir = this.dir ? path30.resolve(ctx.cwd, this.dir) : ctx.entitiesDir ?? path30.resolve(ctx.cwd, "entities");
|
|
9978
11141
|
if (!fs19.existsSync(entitiesDir)) {
|
|
9979
11142
|
printError(`Entity directory not found: ${entitiesDir}`);
|
|
9980
11143
|
return 1;
|
|
9981
11144
|
}
|
|
9982
11145
|
const relCandidates = [
|
|
9983
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
11146
|
+
path30.resolve(path30.dirname(entitiesDir), "relationships"),
|
|
11147
|
+
path30.resolve(entitiesDir, "relationships"),
|
|
11148
|
+
path30.resolve(ctx.cwd, "relationships")
|
|
9986
11149
|
];
|
|
9987
11150
|
const relationshipsDir = relCandidates.find((d) => fs19.existsSync(d));
|
|
9988
11151
|
const result = await analyzeDomain(entitiesDir, relationshipsDir);
|
|
@@ -9998,20 +11161,20 @@ var ProjectGraphCommand = class extends Command7 {
|
|
|
9998
11161
|
return 0;
|
|
9999
11162
|
}
|
|
10000
11163
|
if (this.output) {
|
|
10001
|
-
const outPath =
|
|
11164
|
+
const outPath = path30.resolve(ctx.cwd, this.output);
|
|
10002
11165
|
fs19.writeFileSync(outPath, JSON.stringify(serialized, null, 2));
|
|
10003
11166
|
printSuccess(`Graph written to ${outPath}`);
|
|
10004
11167
|
printInfo(`${result.entities.length} entities, ${result.relationshipDefinitions.length} relationships, ${result.graph.edges.length} edges`);
|
|
10005
11168
|
return 0;
|
|
10006
11169
|
}
|
|
10007
11170
|
const os = await import("os");
|
|
10008
|
-
const tmpDir = fs19.mkdtempSync(
|
|
10009
|
-
const graphPath =
|
|
11171
|
+
const tmpDir = fs19.mkdtempSync(path30.join(os.default.tmpdir(), "codegen-graph-"));
|
|
11172
|
+
const graphPath = path30.join(tmpDir, "graph.json");
|
|
10010
11173
|
fs19.writeFileSync(graphPath, JSON.stringify(serialized, null, 2));
|
|
10011
|
-
const viewerDir =
|
|
10012
|
-
const viewerDist =
|
|
11174
|
+
const viewerDir = path30.resolve(import.meta.dirname, "..", "..", "..", "tools", "schema-graph-viewer");
|
|
11175
|
+
const viewerDist = path30.join(viewerDir, "dist", "index.html");
|
|
10013
11176
|
if (fs19.existsSync(viewerDist)) {
|
|
10014
|
-
fs19.copyFileSync(graphPath,
|
|
11177
|
+
fs19.copyFileSync(graphPath, path30.join(viewerDir, "dist", "graph.json"));
|
|
10015
11178
|
printSuccess("Graph exported");
|
|
10016
11179
|
printInfo(`${result.entities.length} entities, ${result.relationshipDefinitions.length} relationships, ${result.graph.edges.length} edges`);
|
|
10017
11180
|
printInfo(`Graph JSON: ${graphPath}`);
|
|
@@ -10043,7 +11206,7 @@ var project_default = projectNoun;
|
|
|
10043
11206
|
|
|
10044
11207
|
// src/cli/commands/dev.ts
|
|
10045
11208
|
import fs20 from "fs";
|
|
10046
|
-
import
|
|
11209
|
+
import path31 from "path";
|
|
10047
11210
|
import { execSync as execSync3, spawn, spawnSync } from "child_process";
|
|
10048
11211
|
import { Command as Command8, Option as Option8 } from "clipanion";
|
|
10049
11212
|
var DEFAULT_APP_PORT = 3e3;
|
|
@@ -10076,14 +11239,14 @@ function getRedisPort(_ctx) {
|
|
|
10076
11239
|
return Number(process.env.DEV_REDIS_PORT ?? DEFAULT_REDIS_PORT);
|
|
10077
11240
|
}
|
|
10078
11241
|
function composeFilePath(cwd) {
|
|
10079
|
-
const devPath =
|
|
11242
|
+
const devPath = path31.join(cwd, COMPOSE_FILE);
|
|
10080
11243
|
if (fs20.existsSync(devPath)) return devPath;
|
|
10081
|
-
const rootPath =
|
|
11244
|
+
const rootPath = path31.join(cwd, "docker-compose.yml");
|
|
10082
11245
|
if (fs20.existsSync(rootPath)) return rootPath;
|
|
10083
11246
|
return devPath;
|
|
10084
11247
|
}
|
|
10085
11248
|
function pidFilePath(cwd) {
|
|
10086
|
-
return
|
|
11249
|
+
return path31.join(cwd, PID_FILE);
|
|
10087
11250
|
}
|
|
10088
11251
|
function readAppPid(cwd) {
|
|
10089
11252
|
const p = pidFilePath(cwd);
|
|
@@ -10144,7 +11307,7 @@ function checkApp(cwd, port) {
|
|
|
10144
11307
|
function listEntityNames(ctx) {
|
|
10145
11308
|
if (!ctx.entitiesDir || !fs20.existsSync(ctx.entitiesDir)) return [];
|
|
10146
11309
|
return findYamlFiles(ctx.entitiesDir).map(
|
|
10147
|
-
(f) =>
|
|
11310
|
+
(f) => path31.basename(f).replace(/\.ya?ml$/, "")
|
|
10148
11311
|
);
|
|
10149
11312
|
}
|
|
10150
11313
|
function formatServiceLine(svc) {
|
|
@@ -10154,7 +11317,7 @@ function formatServiceLine(svc) {
|
|
|
10154
11317
|
return `${icon} ${svc.name.padEnd(12)} ${theme.muted(`${svc.host}:${svc.port}`)} ${status}${pidStr}`;
|
|
10155
11318
|
}
|
|
10156
11319
|
function ensureComposeFile(cwd, pgPort, redisPort) {
|
|
10157
|
-
const composePath =
|
|
11320
|
+
const composePath = path31.join(cwd, COMPOSE_FILE);
|
|
10158
11321
|
if (fs20.existsSync(composePath)) return composePath;
|
|
10159
11322
|
const content = `# Auto-generated by codegen dev
|
|
10160
11323
|
# Ports offset from defaults to avoid conflicts with other local services.
|
|
@@ -10239,7 +11402,7 @@ var DevUpCommand = class extends Command8 {
|
|
|
10239
11402
|
if (!pgReady) printWarning("postgres did not become healthy in time");
|
|
10240
11403
|
if (!redisReady) printWarning("redis did not become healthy in time");
|
|
10241
11404
|
const drizzleConfig = ["drizzle.config.ts", "drizzle.config.js"].find(
|
|
10242
|
-
(f) => fs20.existsSync(
|
|
11405
|
+
(f) => fs20.existsSync(path31.join(ctx.cwd, f))
|
|
10243
11406
|
);
|
|
10244
11407
|
if (drizzleConfig) {
|
|
10245
11408
|
if (!isJsonMode()) printInfo("pushing database schema...");
|
|
@@ -10259,7 +11422,7 @@ var DevUpCommand = class extends Command8 {
|
|
|
10259
11422
|
if (!isJsonMode()) printInfo("starting NestJS app...");
|
|
10260
11423
|
const dbUrl = `postgres://postgres:postgres@localhost:${pgPort}/codegen_dev`;
|
|
10261
11424
|
const redisUrl = `redis://localhost:${redisPort}`;
|
|
10262
|
-
const logFile =
|
|
11425
|
+
const logFile = path31.join(ctx.cwd, ".dev-app.log");
|
|
10263
11426
|
const logFd = fs20.openSync(logFile, "a");
|
|
10264
11427
|
const child = spawn("bun", ["src/main.ts"], {
|
|
10265
11428
|
cwd: ctx.cwd,
|
|
@@ -10417,7 +11580,7 @@ var DevLogsCommand = class extends Command8 {
|
|
|
10417
11580
|
}
|
|
10418
11581
|
return 0;
|
|
10419
11582
|
}
|
|
10420
|
-
const logFile =
|
|
11583
|
+
const logFile = path31.join(ctx.cwd, ".dev-app.log");
|
|
10421
11584
|
if (!fs20.existsSync(logFile)) {
|
|
10422
11585
|
printInfo("no app logs found \u2014 is the app running?");
|
|
10423
11586
|
return 0;
|
|
@@ -10461,7 +11624,7 @@ var DevRestartCommand = class extends Command8 {
|
|
|
10461
11624
|
spawnSync("sleep", ["1"]);
|
|
10462
11625
|
const dbUrl = `postgres://postgres:postgres@localhost:${pgPort}/codegen_dev`;
|
|
10463
11626
|
const redisUrl = `redis://localhost:${redisPort}`;
|
|
10464
|
-
const logFile =
|
|
11627
|
+
const logFile = path31.join(ctx.cwd, ".dev-app.log");
|
|
10465
11628
|
const logFd = fs20.openSync(logFile, "a");
|
|
10466
11629
|
const child = spawn("bun", ["src/main.ts"], {
|
|
10467
11630
|
cwd: ctx.cwd,
|
|
@@ -10591,7 +11754,7 @@ var dev_default = devNoun;
|
|
|
10591
11754
|
|
|
10592
11755
|
// src/cli/commands/relationship.ts
|
|
10593
11756
|
import fs21 from "fs";
|
|
10594
|
-
import
|
|
11757
|
+
import path32 from "path";
|
|
10595
11758
|
import { Command as Command9, Option as Option9 } from "clipanion";
|
|
10596
11759
|
function listRelationshipYamls2(dir) {
|
|
10597
11760
|
if (!fs21.existsSync(dir)) return [];
|
|
@@ -10618,7 +11781,7 @@ function padRight2(s, n) {
|
|
|
10618
11781
|
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
10619
11782
|
}
|
|
10620
11783
|
async function summary6(ctx) {
|
|
10621
|
-
const relDir =
|
|
11784
|
+
const relDir = path32.resolve(ctx.cwd, "relationships");
|
|
10622
11785
|
const files = listRelationshipYamls2(relDir);
|
|
10623
11786
|
if (files.length === 0) {
|
|
10624
11787
|
return {
|
|
@@ -10688,14 +11851,14 @@ var RelationshipNewCommand = class extends Command9 {
|
|
|
10688
11851
|
}
|
|
10689
11852
|
let targets = [];
|
|
10690
11853
|
if (this.all) {
|
|
10691
|
-
const dir =
|
|
11854
|
+
const dir = path32.resolve(ctx.cwd, "relationships");
|
|
10692
11855
|
targets = listRelationshipYamls2(dir);
|
|
10693
11856
|
if (targets.length === 0) {
|
|
10694
11857
|
printError(`No relationship YAML files found in ${dir}`);
|
|
10695
11858
|
return 1;
|
|
10696
11859
|
}
|
|
10697
11860
|
} else if (this.yaml) {
|
|
10698
|
-
targets = [
|
|
11861
|
+
targets = [path32.resolve(ctx.cwd, this.yaml)];
|
|
10699
11862
|
} else {
|
|
10700
11863
|
printError("Missing YAML path. Pass a file or --all.");
|
|
10701
11864
|
return 2;
|
|
@@ -10712,7 +11875,7 @@ var RelationshipNewCommand = class extends Command9 {
|
|
|
10712
11875
|
}
|
|
10713
11876
|
if (invalid.length > 0) {
|
|
10714
11877
|
for (const i of invalid) {
|
|
10715
|
-
printError(`${
|
|
11878
|
+
printError(`${path32.basename(i.file)} \u2014 ${i.message}`);
|
|
10716
11879
|
}
|
|
10717
11880
|
if (!isJsonMode()) return 1;
|
|
10718
11881
|
}
|
|
@@ -10743,7 +11906,7 @@ var RelationshipNewCommand = class extends Command9 {
|
|
|
10743
11906
|
}
|
|
10744
11907
|
const succeeded = [];
|
|
10745
11908
|
const failed = [
|
|
10746
|
-
...invalid.map((i) => ({ name:
|
|
11909
|
+
...invalid.map((i) => ({ name: path32.basename(i.file), file: i.file, message: i.message }))
|
|
10747
11910
|
];
|
|
10748
11911
|
for (const v of validated) {
|
|
10749
11912
|
if (!isJsonMode()) {
|
|
@@ -10762,8 +11925,8 @@ var RelationshipNewCommand = class extends Command9 {
|
|
|
10762
11925
|
if (!isJsonMode()) printError(`${v.name} \u2014 ${res.stderr ?? "failed"}`);
|
|
10763
11926
|
}
|
|
10764
11927
|
}
|
|
10765
|
-
const entitiesDir = ctx.entitiesDir ??
|
|
10766
|
-
const relationshipsDir =
|
|
11928
|
+
const entitiesDir = ctx.entitiesDir ?? path32.resolve(ctx.cwd, "entities");
|
|
11929
|
+
const relationshipsDir = path32.resolve(ctx.cwd, "relationships");
|
|
10767
11930
|
const generatedDir = resolveGeneratedDir(ctx);
|
|
10768
11931
|
const architecture = resolveArchitecture(ctx);
|
|
10769
11932
|
let barrelResult = null;
|
|
@@ -10808,7 +11971,7 @@ var RelationshipNewCommand = class extends Command9 {
|
|
|
10808
11971
|
}
|
|
10809
11972
|
if (barrelResult) {
|
|
10810
11973
|
printInfo(
|
|
10811
|
-
`barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${
|
|
11974
|
+
`barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path32.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path32.relative(ctx.cwd, barrelResult.schemaBarrel)}`
|
|
10812
11975
|
);
|
|
10813
11976
|
}
|
|
10814
11977
|
}
|
|
@@ -10831,7 +11994,7 @@ var RelationshipListCommand = class extends Command9 {
|
|
|
10831
11994
|
json: this.json,
|
|
10832
11995
|
skipDetection: true
|
|
10833
11996
|
});
|
|
10834
|
-
const relDir =
|
|
11997
|
+
const relDir = path32.resolve(ctx.cwd, "relationships");
|
|
10835
11998
|
const files = listRelationshipYamls2(relDir);
|
|
10836
11999
|
if (files.length === 0) {
|
|
10837
12000
|
printInfo("No relationship definitions found.");
|
|
@@ -10871,7 +12034,7 @@ var relationshipNoun = {
|
|
|
10871
12034
|
var relationship_default = relationshipNoun;
|
|
10872
12035
|
|
|
10873
12036
|
// src/cli/commands/junction.ts
|
|
10874
|
-
import
|
|
12037
|
+
import path33 from "path";
|
|
10875
12038
|
import { Command as Command10, Option as Option10 } from "clipanion";
|
|
10876
12039
|
function summarizeJunctionFile(filePath) {
|
|
10877
12040
|
const result = loadJunctionFromYaml(filePath);
|
|
@@ -10894,7 +12057,7 @@ function padRight3(s, n) {
|
|
|
10894
12057
|
return s.length >= n ? s : s + " ".repeat(n - s.length);
|
|
10895
12058
|
}
|
|
10896
12059
|
async function summary7(ctx) {
|
|
10897
|
-
const junctionDir =
|
|
12060
|
+
const junctionDir = path33.resolve(ctx.cwd, "junctions");
|
|
10898
12061
|
const files = listJunctionYamls(junctionDir);
|
|
10899
12062
|
if (files.length === 0) {
|
|
10900
12063
|
return {
|
|
@@ -10964,14 +12127,14 @@ var JunctionNewCommand = class extends Command10 {
|
|
|
10964
12127
|
}
|
|
10965
12128
|
let targets = [];
|
|
10966
12129
|
if (this.all) {
|
|
10967
|
-
const dir =
|
|
12130
|
+
const dir = path33.resolve(ctx.cwd, "junctions");
|
|
10968
12131
|
targets = listJunctionYamls(dir);
|
|
10969
12132
|
if (targets.length === 0) {
|
|
10970
12133
|
printError(`No junction YAML files found in ${dir}`);
|
|
10971
12134
|
return 1;
|
|
10972
12135
|
}
|
|
10973
12136
|
} else if (this.yaml) {
|
|
10974
|
-
targets = [
|
|
12137
|
+
targets = [path33.resolve(ctx.cwd, this.yaml)];
|
|
10975
12138
|
} else {
|
|
10976
12139
|
printError("Missing YAML path. Pass a file or --all.");
|
|
10977
12140
|
return 2;
|
|
@@ -10990,7 +12153,7 @@ var JunctionNewCommand = class extends Command10 {
|
|
|
10990
12153
|
}
|
|
10991
12154
|
if (invalid.length > 0) {
|
|
10992
12155
|
for (const i of invalid) {
|
|
10993
|
-
printError(`${
|
|
12156
|
+
printError(`${path33.basename(i.file)} \u2014 ${i.message}`);
|
|
10994
12157
|
}
|
|
10995
12158
|
if (!isJsonMode()) return 1;
|
|
10996
12159
|
}
|
|
@@ -11021,7 +12184,7 @@ var JunctionNewCommand = class extends Command10 {
|
|
|
11021
12184
|
}
|
|
11022
12185
|
const succeeded = [];
|
|
11023
12186
|
const failed = [
|
|
11024
|
-
...invalid.map((i) => ({ name:
|
|
12187
|
+
...invalid.map((i) => ({ name: path33.basename(i.file), file: i.file, message: i.message }))
|
|
11025
12188
|
];
|
|
11026
12189
|
for (const v of validated) {
|
|
11027
12190
|
if (!isJsonMode()) {
|
|
@@ -11040,9 +12203,9 @@ var JunctionNewCommand = class extends Command10 {
|
|
|
11040
12203
|
if (!isJsonMode()) printError(`${v.name} \u2014 ${res.stderr ?? "failed"}`);
|
|
11041
12204
|
}
|
|
11042
12205
|
}
|
|
11043
|
-
const entitiesDir = ctx.entitiesDir ??
|
|
11044
|
-
const relationshipsDir =
|
|
11045
|
-
const junctionsDir =
|
|
12206
|
+
const entitiesDir = ctx.entitiesDir ?? path33.resolve(ctx.cwd, "entities");
|
|
12207
|
+
const relationshipsDir = path33.resolve(ctx.cwd, "relationships");
|
|
12208
|
+
const junctionsDir = path33.resolve(ctx.cwd, "junctions");
|
|
11046
12209
|
const generatedDir = resolveGeneratedDir(ctx);
|
|
11047
12210
|
const architecture = resolveArchitecture(ctx);
|
|
11048
12211
|
let barrelResult = null;
|
|
@@ -11088,7 +12251,7 @@ var JunctionNewCommand = class extends Command10 {
|
|
|
11088
12251
|
}
|
|
11089
12252
|
if (barrelResult) {
|
|
11090
12253
|
printInfo(
|
|
11091
|
-
`barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${
|
|
12254
|
+
`barrels regenerated (${barrelResult.entityCount} modules) \u2192 ${path33.relative(ctx.cwd, barrelResult.modulesBarrel)}, ${path33.relative(ctx.cwd, barrelResult.schemaBarrel)}`
|
|
11092
12255
|
);
|
|
11093
12256
|
}
|
|
11094
12257
|
}
|
|
@@ -11111,7 +12274,7 @@ var JunctionListCommand = class extends Command10 {
|
|
|
11111
12274
|
json: this.json,
|
|
11112
12275
|
skipDetection: true
|
|
11113
12276
|
});
|
|
11114
|
-
const junctionDir =
|
|
12277
|
+
const junctionDir = path33.resolve(ctx.cwd, "junctions");
|
|
11115
12278
|
const files = listJunctionYamls(junctionDir);
|
|
11116
12279
|
if (files.length === 0) {
|
|
11117
12280
|
printInfo("No junction definitions found.");
|
|
@@ -11152,7 +12315,7 @@ var junction_default = junctionNoun;
|
|
|
11152
12315
|
|
|
11153
12316
|
// src/cli/commands/events.ts
|
|
11154
12317
|
import fs22 from "fs";
|
|
11155
|
-
import
|
|
12318
|
+
import path34 from "path";
|
|
11156
12319
|
import ts2 from "typescript";
|
|
11157
12320
|
import { Command as Command11, Option as Option11 } from "clipanion";
|
|
11158
12321
|
function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
|
|
@@ -11288,7 +12451,7 @@ function suggestEventTypes(target, known, limit = 3) {
|
|
|
11288
12451
|
return known.map((t) => ({ t, d: levenshtein(target, t) })).sort((a, b) => a.d - b.d).slice(0, limit).map((x) => x.t);
|
|
11289
12452
|
}
|
|
11290
12453
|
function renderConsumerReport(result, cwd) {
|
|
11291
|
-
const rel2 = (p) =>
|
|
12454
|
+
const rel2 = (p) => path34.relative(cwd, p) || p;
|
|
11292
12455
|
const lines = [];
|
|
11293
12456
|
const total = result.tier3.length + result.tier2.length + result.tier1.length;
|
|
11294
12457
|
lines.push(`Event: ${result.eventType}`);
|
|
@@ -11324,7 +12487,7 @@ function renderConsumerReport(result, cwd) {
|
|
|
11324
12487
|
return lines;
|
|
11325
12488
|
}
|
|
11326
12489
|
function runConsumersScan(opts) {
|
|
11327
|
-
const scanRoot = opts.scanRoot ??
|
|
12490
|
+
const scanRoot = opts.scanRoot ?? path34.join(opts.cwd, "src");
|
|
11328
12491
|
const handlersDir = opts.handlersDir ?? scanRoot;
|
|
11329
12492
|
const allTriggers = scanHandlerFiles(handlersDir);
|
|
11330
12493
|
const tier3 = allTriggers.filter((t) => t.event === opts.eventType).map((t) => ({
|
|
@@ -11334,7 +12497,7 @@ function runConsumersScan(opts) {
|
|
|
11334
12497
|
sourceLine: t.sourceLine
|
|
11335
12498
|
}));
|
|
11336
12499
|
const tier21 = fs22.existsSync(scanRoot) ? scanDirectoryForConsumers(scanRoot, opts.eventType) : { tier2: [], tier1: [], hasEventFlowImport: false };
|
|
11337
|
-
const eventsGeneratedDir = opts.eventsGeneratedDir ??
|
|
12500
|
+
const eventsGeneratedDir = opts.eventsGeneratedDir ?? path34.join(
|
|
11338
12501
|
resolveSubsystemsRootFromContext(opts.cwd, opts.config),
|
|
11339
12502
|
"events",
|
|
11340
12503
|
"generated"
|
|
@@ -11355,11 +12518,11 @@ function runConsumersScan(opts) {
|
|
|
11355
12518
|
function resolveSubsystemsRootFromContext(cwd, config) {
|
|
11356
12519
|
const configured = config?.paths?.subsystems;
|
|
11357
12520
|
if (typeof configured === "string" && configured.length > 0) {
|
|
11358
|
-
return
|
|
12521
|
+
return path34.resolve(cwd, configured);
|
|
11359
12522
|
}
|
|
11360
12523
|
const backendSrc = config?.paths?.backend_src;
|
|
11361
12524
|
const base = typeof backendSrc === "string" && backendSrc.length > 0 ? backendSrc : "src";
|
|
11362
|
-
return
|
|
12525
|
+
return path34.resolve(cwd, base, "shared", "subsystems");
|
|
11363
12526
|
}
|
|
11364
12527
|
var EventsConsumersCommand = class extends Command11 {
|
|
11365
12528
|
static paths = [["events", "consumers"]];
|
|
@@ -11448,7 +12611,7 @@ var eventsNoun = {
|
|
|
11448
12611
|
var events_default = eventsNoun;
|
|
11449
12612
|
|
|
11450
12613
|
// src/cli/commands/orchestration.ts
|
|
11451
|
-
import
|
|
12614
|
+
import path35 from "path";
|
|
11452
12615
|
import { Command as Command12, Option as Option12 } from "clipanion";
|
|
11453
12616
|
var DEFAULT_PATTERN_GLOBS = ["src/patterns/*.pattern.ts"];
|
|
11454
12617
|
function resolvePatternGlobs(ctx) {
|
|
@@ -11462,10 +12625,10 @@ function resolveOrchestrationOutputRoot(ctx) {
|
|
|
11462
12625
|
const paths = ctx.config?.paths;
|
|
11463
12626
|
const explicit = paths?.orchestration_src;
|
|
11464
12627
|
if (typeof explicit === "string" && explicit.length > 0) {
|
|
11465
|
-
return
|
|
12628
|
+
return path35.resolve(ctx.cwd, explicit);
|
|
11466
12629
|
}
|
|
11467
12630
|
const backendSrc = typeof paths?.backend_src === "string" && paths.backend_src.length > 0 ? paths.backend_src : "app/backend/src";
|
|
11468
|
-
return
|
|
12631
|
+
return path35.resolve(ctx.cwd, backendSrc, "orchestration");
|
|
11469
12632
|
}
|
|
11470
12633
|
async function reloadRegistry(ctx) {
|
|
11471
12634
|
_resetRegistryForTests({ includeLibrary: false });
|
|
@@ -11554,12 +12717,12 @@ var OrchestrationGenCommand = class extends Command12 {
|
|
|
11554
12717
|
);
|
|
11555
12718
|
for (const f of result.files) {
|
|
11556
12719
|
console.log(
|
|
11557
|
-
` ${theme.muted(icons.arrow)} ${
|
|
12720
|
+
` ${theme.muted(icons.arrow)} ${path35.relative(ctx.cwd, f.outputPath)}`
|
|
11558
12721
|
);
|
|
11559
12722
|
}
|
|
11560
12723
|
} else {
|
|
11561
12724
|
printSuccess(
|
|
11562
|
-
`Emitted ${result.files.length} file(s) across ${targets.length} pattern(s) \u2192 ${
|
|
12725
|
+
`Emitted ${result.files.length} file(s) across ${targets.length} pattern(s) \u2192 ${path35.relative(ctx.cwd, outputRoot)}`
|
|
11563
12726
|
);
|
|
11564
12727
|
}
|
|
11565
12728
|
return 0;
|
|
@@ -11725,7 +12888,7 @@ var update_default = UpdateShortcut;
|
|
|
11725
12888
|
// src/cli/index.ts
|
|
11726
12889
|
function readVersion() {
|
|
11727
12890
|
try {
|
|
11728
|
-
const pkgPath =
|
|
12891
|
+
const pkgPath = join17(import.meta.dirname, "..", "..", "package.json");
|
|
11729
12892
|
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
11730
12893
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
11731
12894
|
} catch {
|