@topogram/cli 0.3.50 → 0.3.52
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/ARCHITECTURE.md +4 -4
- package/CHANGELOG.md +11 -11
- package/package.json +1 -1
- package/src/adoption/plan.js +2 -2
- package/src/agent-ops/query-builders.js +42 -33
- package/src/cli.js +174 -129
- package/src/generator/adapters.d.ts +1 -0
- package/src/generator/adapters.js +64 -39
- package/src/generator/check.js +19 -12
- package/src/generator/context/diff.js +9 -9
- package/src/generator/context/domain-coverage.js +11 -10
- package/src/generator/context/domain-page.js +6 -6
- package/src/generator/context/shared.js +37 -21
- package/src/generator/context/slice.js +70 -65
- package/src/generator/index.js +12 -12
- package/src/generator/output.js +21 -20
- package/src/generator/registry.js +61 -49
- package/src/generator/runtime/app-bundle.js +15 -15
- package/src/generator/runtime/compile-check.js +7 -7
- package/src/generator/runtime/deployment.js +9 -9
- package/src/generator/runtime/environment.js +39 -39
- package/src/generator/runtime/runtime-check.js +5 -5
- package/src/generator/runtime/shared.js +40 -38
- package/src/generator/runtime/smoke.js +5 -5
- package/src/generator/surfaces/databases/contract.js +1 -1
- package/src/generator/surfaces/databases/lifecycle-shared.js +6 -5
- package/src/generator/surfaces/databases/postgres/drizzle.js +3 -2
- package/src/generator/surfaces/databases/postgres/prisma.js +3 -2
- package/src/generator/surfaces/databases/shared.js +3 -2
- package/src/generator/surfaces/databases/snapshot.js +1 -1
- package/src/generator/surfaces/databases/sqlite/prisma.js +3 -2
- package/src/generator/surfaces/native/swiftui-app.js +3 -3
- package/src/generator/surfaces/native/swiftui-templates/Package.swift.txt +1 -1
- package/src/generator/surfaces/native/swiftui-templates/README.generated.md +3 -3
- package/src/generator/surfaces/native/swiftui-templates/runtime/DynamicScreens.swift +3 -3
- package/src/generator/surfaces/services/persistence-wiring.js +3 -2
- package/src/generator/surfaces/services/server-contract.js +4 -4
- package/src/generator/surfaces/shared.js +2 -2
- package/src/generator/surfaces/web/design-intent.js +1 -1
- package/src/generator/surfaces/web/index.js +7 -7
- package/src/generator/surfaces/web/{react-components.js → react-widgets.js} +53 -53
- package/src/generator/surfaces/web/react.js +36 -36
- package/src/generator/surfaces/web/{sveltekit-components.js → sveltekit-widgets.js} +53 -53
- package/src/generator/surfaces/web/sveltekit.js +34 -34
- package/src/generator/surfaces/web/{ui-web-contract.js → ui-surface-contract.js} +8 -8
- package/src/generator/surfaces/web/vanilla.js +6 -6
- package/src/generator/{component-conformance.js → widget-conformance.js} +129 -128
- package/src/generator/widgets.js +40 -0
- package/src/generator-policy.js +10 -12
- package/src/import/core/runner.js +34 -34
- package/src/import/core/shared.js +1 -1
- package/src/import/extractors/ui/android-compose.js +1 -1
- package/src/import/extractors/ui/blazor.js +1 -1
- package/src/import/extractors/ui/razor-pages.js +1 -1
- package/src/import/extractors/ui/react-router.js +4 -4
- package/src/import/extractors/ui/sveltekit.js +4 -4
- package/src/import/extractors/ui/swiftui.js +1 -1
- package/src/import/extractors/ui/uikit.js +1 -1
- package/src/new-project.js +19 -18
- package/src/project-config.js +92 -42
- package/src/proofs/contract-audit.js +1 -1
- package/src/proofs/ios-parity.js +1 -1
- package/src/proofs/issues-parity.js +1 -1
- package/src/realization/backend/build-backend-runtime-realization.js +2 -2
- package/src/realization/ui/build-ui-shared-realization.js +33 -33
- package/src/realization/ui/build-web-realization.js +23 -20
- package/src/reconcile/journeys.js +1 -1
- package/src/resolver/index.js +148 -65
- package/src/validator/index.js +501 -409
- package/src/validator/kinds.js +36 -36
- package/src/validator/per-kind/{component.js → widget.js} +47 -47
- package/src/{component-behavior.js → widget-behavior.js} +3 -3
- package/src/workflows.js +39 -38
- package/template-helpers/react.js +4 -4
- package/template-helpers/sveltekit.js +4 -4
- package/src/generator/components.js +0 -39
- /package/src/resolver/enrich/{component.js → widget.js} +0 -0
|
@@ -19,24 +19,26 @@ import { defaultProjectConfigForGraph, validateProjectConfig } from "../../proje
|
|
|
19
19
|
/**
|
|
20
20
|
* @typedef {Object} RuntimeComponent
|
|
21
21
|
* @property {string} id
|
|
22
|
-
* @property {"
|
|
22
|
+
* @property {"api_service"|"web_surface"|"ios_surface"|"android_surface"|"database"} kind
|
|
23
|
+
* @property {string} [type]
|
|
23
24
|
* @property {RuntimeStatement} projection
|
|
24
25
|
* @property {import("../../project-config.js").GeneratorBinding} generator
|
|
25
26
|
* @property {number|null} [port]
|
|
26
|
-
* @property {string} [api]
|
|
27
|
-
* @property {string} [database]
|
|
27
|
+
* @property {string|null} [api]
|
|
28
|
+
* @property {string|null} [database]
|
|
28
29
|
* @property {Record<string, string>} [env]
|
|
29
30
|
* @property {RuntimeComponent|null} [apiComponent]
|
|
30
31
|
* @property {RuntimeComponent|null} [databaseComponent]
|
|
31
32
|
*/
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
|
-
* @typedef {import("../../project-config.js").
|
|
35
|
+
* @typedef {import("../../project-config.js").RuntimeTopologyRuntime} RuntimeTopologyComponent
|
|
35
36
|
*/
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* @typedef {Object} RuntimeTopology
|
|
39
40
|
* @property {import("../../project-config.js").ProjectConfig} config
|
|
41
|
+
* @property {RuntimeComponent[]} runtimes
|
|
40
42
|
* @property {RuntimeComponent[]} components
|
|
41
43
|
* @property {RuntimeComponent[]} apiComponents
|
|
42
44
|
* @property {RuntimeComponent[]} webComponents
|
|
@@ -227,17 +229,17 @@ function apiProjectionCandidates(graph) {
|
|
|
227
229
|
*/
|
|
228
230
|
function uiWebProjectionCandidates(graph) {
|
|
229
231
|
return (graph.byKind.projection || []).filter(
|
|
230
|
-
(projection) => projection.platform === "
|
|
232
|
+
(projection) => projection.platform === "web_surface" && (projection.uiRoutes || []).length > 0
|
|
231
233
|
);
|
|
232
234
|
}
|
|
233
235
|
|
|
234
|
-
const WEB_UI_FAMILY_PREFIX = "
|
|
235
|
-
const NATIVE_UI_FAMILY_PREFIX = "
|
|
236
|
+
const WEB_UI_FAMILY_PREFIX = "proj_web_surface__";
|
|
237
|
+
const NATIVE_UI_FAMILY_PREFIX = "proj_ios_surface__";
|
|
236
238
|
|
|
237
239
|
/** Prefer canonical ids when multiple shipped web stacks exist (deterministic, not lexicographic). */
|
|
238
|
-
const DEFAULT_WEB_UI_STACK_ORDER = ["
|
|
240
|
+
const DEFAULT_WEB_UI_STACK_ORDER = ["proj_web_surface__sveltekit", "proj_web_surface__react"];
|
|
239
241
|
|
|
240
|
-
const DEFAULT_NATIVE_UI_PLATFORM_ORDER = ["
|
|
242
|
+
const DEFAULT_NATIVE_UI_PLATFORM_ORDER = ["proj_ios_surface__swiftui"];
|
|
241
243
|
|
|
242
244
|
/**
|
|
243
245
|
* @param {ResolvedGraph} graph
|
|
@@ -245,12 +247,12 @@ const DEFAULT_NATIVE_UI_PLATFORM_ORDER = ["proj_ui_native__ios"];
|
|
|
245
247
|
*/
|
|
246
248
|
function uiIosProjectionCandidates(graph) {
|
|
247
249
|
return (graph.byKind.projection || []).filter(
|
|
248
|
-
(projection) => projection.platform === "
|
|
250
|
+
(projection) => projection.platform === "ios_surface" && (projection.uiRoutes || []).length > 0
|
|
249
251
|
);
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
/**
|
|
253
|
-
* Prefer canonical native projections (`
|
|
255
|
+
* Prefer canonical native projections (`proj_ios_surface__{stack}`); otherwise first routed iOS surface projection.
|
|
254
256
|
*
|
|
255
257
|
* @param {ResolvedGraph} graph
|
|
256
258
|
* @returns {RuntimeStatement|undefined}
|
|
@@ -271,7 +273,7 @@ export function pickDefaultIosUiProjection(graph) {
|
|
|
271
273
|
}
|
|
272
274
|
|
|
273
275
|
/**
|
|
274
|
-
* Prefer canonical shipped web projections (`
|
|
276
|
+
* Prefer canonical shipped web projections (`proj_web_surface__{stack}`); otherwise first routed web surface projection.
|
|
275
277
|
*
|
|
276
278
|
* @param {ResolvedGraph} graph
|
|
277
279
|
* @returns {RuntimeStatement|undefined}
|
|
@@ -288,10 +290,6 @@ export function pickDefaultUiWebProjection(graph) {
|
|
|
288
290
|
}
|
|
289
291
|
return hierarchical.sort((a, b) => a.id.localeCompare(b.id))[0];
|
|
290
292
|
}
|
|
291
|
-
const legacySvelteKitProjection = candidates.find((projection) => projection.id === "proj_ui_web");
|
|
292
|
-
if (legacySvelteKitProjection) {
|
|
293
|
-
return legacySvelteKitProjection;
|
|
294
|
-
}
|
|
295
293
|
return candidates[0];
|
|
296
294
|
}
|
|
297
295
|
|
|
@@ -302,7 +300,7 @@ export function pickDefaultUiWebProjection(graph) {
|
|
|
302
300
|
*/
|
|
303
301
|
export function getDefaultEnvironmentProjections(graph, options = {}) {
|
|
304
302
|
const topology = resolveRuntimeTopology(graph, options);
|
|
305
|
-
const dbCandidates = graph.byKind.projection?.filter((projection) => ["
|
|
303
|
+
const dbCandidates = graph.byKind.projection?.filter((projection) => ["db_contract", "db_contract"].includes(projection.platform)) || [];
|
|
306
304
|
const apiProjection = /** @type {RuntimeStatement|null} */ (topology.primaryApi?.projection ||
|
|
307
305
|
(options.projectionId ? getProjection(graph, options.projectionId) : null) ||
|
|
308
306
|
apiProjectionCandidates(graph).find((projection) => projection.id === "proj_api") ||
|
|
@@ -396,14 +394,14 @@ export function generateRuntimeApiContracts(graph) {
|
|
|
396
394
|
}
|
|
397
395
|
|
|
398
396
|
/**
|
|
399
|
-
* @param {string}
|
|
397
|
+
* @param {string} runtimeId
|
|
400
398
|
* @param {EnvVarOptions} [options]
|
|
401
399
|
* @returns {string}
|
|
402
400
|
*/
|
|
403
|
-
function envVarPrefix(
|
|
404
|
-
return options.primary ||
|
|
401
|
+
function envVarPrefix(runtimeId, options = {}) {
|
|
402
|
+
return options.primary || runtimeId === "db"
|
|
405
403
|
? ""
|
|
406
|
-
: `${
|
|
404
|
+
: `${runtimeId.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_`;
|
|
407
405
|
}
|
|
408
406
|
|
|
409
407
|
/**
|
|
@@ -426,24 +424,27 @@ export function dbEnvVarsForComponent(component, options = {}) {
|
|
|
426
424
|
* @param {import("../../project-config.js").ProjectConfig} config
|
|
427
425
|
* @returns {RuntimeComponent[]}
|
|
428
426
|
*/
|
|
429
|
-
function
|
|
427
|
+
function decorateRuntimes(graph, config) {
|
|
430
428
|
const byProjectionId = new Map((graph.byKind.projection || []).map((projection) => [projection.id, projection]));
|
|
431
|
-
const
|
|
429
|
+
const rawRuntimes = config.topology?.runtimes || config.topology?.components || [];
|
|
432
430
|
/** @type {RuntimeComponent[]} */
|
|
433
|
-
const
|
|
434
|
-
...
|
|
435
|
-
|
|
431
|
+
const runtimes = rawRuntimes.map((runtime) => ({
|
|
432
|
+
...runtime,
|
|
433
|
+
kind: runtime.kind || runtime.type || null,
|
|
434
|
+
api: runtime.uses_api ?? runtime.api ?? null,
|
|
435
|
+
database: runtime.uses_database ?? runtime.database ?? null,
|
|
436
|
+
projection: byProjectionId.get(runtime.projection) || {}
|
|
436
437
|
}));
|
|
437
|
-
const byId = new Map(
|
|
438
|
-
for (const
|
|
439
|
-
if (
|
|
440
|
-
|
|
438
|
+
const byId = new Map(runtimes.map((runtime) => [runtime.id, runtime]));
|
|
439
|
+
for (const runtime of runtimes) {
|
|
440
|
+
if (runtime.kind === "api_service" && runtime.database) {
|
|
441
|
+
runtime.databaseComponent = byId.get(runtime.database) || null;
|
|
441
442
|
}
|
|
442
|
-
if (
|
|
443
|
-
|
|
443
|
+
if (runtime.kind === "web_surface" && runtime.api) {
|
|
444
|
+
runtime.apiComponent = byId.get(runtime.api) || null;
|
|
444
445
|
}
|
|
445
446
|
}
|
|
446
|
-
return
|
|
447
|
+
return runtimes;
|
|
447
448
|
}
|
|
448
449
|
|
|
449
450
|
/**
|
|
@@ -460,17 +461,18 @@ export function resolveRuntimeTopology(graph, options = {}) {
|
|
|
460
461
|
if (!validation.ok) {
|
|
461
462
|
throw new Error(validation.errors.map((error) => error.message).join("\n"));
|
|
462
463
|
}
|
|
463
|
-
const
|
|
464
|
-
const apiComponents =
|
|
465
|
-
const webComponents =
|
|
466
|
-
const dbComponents =
|
|
464
|
+
const runtimes = decorateRuntimes(graph, config);
|
|
465
|
+
const apiComponents = runtimes.filter((runtime) => runtime.kind === "api_service");
|
|
466
|
+
const webComponents = runtimes.filter((runtime) => runtime.kind === "web_surface");
|
|
467
|
+
const dbComponents = runtimes.filter((runtime) => runtime.kind === "database");
|
|
467
468
|
const primaryApi = apiComponents[0] || null;
|
|
468
469
|
const primaryWeb = webComponents[0] || null;
|
|
469
470
|
const primaryDb = primaryApi?.databaseComponent || dbComponents[0] || null;
|
|
470
471
|
|
|
471
472
|
return {
|
|
472
473
|
config,
|
|
473
|
-
|
|
474
|
+
runtimes,
|
|
475
|
+
components: runtimes,
|
|
474
476
|
apiComponents,
|
|
475
477
|
webComponents,
|
|
476
478
|
dbComponents,
|
|
@@ -26,11 +26,11 @@ function buildRuntimeSmokePlan(graph, options = {}) {
|
|
|
26
26
|
ui: uiProjection.id
|
|
27
27
|
},
|
|
28
28
|
topology: {
|
|
29
|
-
|
|
30
|
-
id:
|
|
31
|
-
|
|
32
|
-
projection:
|
|
33
|
-
generator:
|
|
29
|
+
runtimes: topology.runtimes.map((runtime) => ({
|
|
30
|
+
id: runtime.id,
|
|
31
|
+
kind: runtime.kind,
|
|
32
|
+
projection: runtime.projection.id,
|
|
33
|
+
generator: runtime.generator
|
|
34
34
|
}))
|
|
35
35
|
},
|
|
36
36
|
...(verification ? { verification } : {}),
|
|
@@ -16,7 +16,7 @@ export function generateDbContractDebug(graph, options = {}) {
|
|
|
16
16
|
for (const contract of contracts) {
|
|
17
17
|
lines.push(`## \`${contract.projection.id}\` - ${contract.projection.name}`);
|
|
18
18
|
lines.push("");
|
|
19
|
-
lines.push(`
|
|
19
|
+
lines.push(`Type: \`${contract.projection.type}\``);
|
|
20
20
|
lines.push(`Profile: \`${contract.profile}\``);
|
|
21
21
|
lines.push("");
|
|
22
22
|
|
|
@@ -80,15 +80,16 @@ function dbLifecyclePlan(graph, projection, options = {}) {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
function renderEmptySnapshotForProjection(projection) {
|
|
83
|
-
const
|
|
83
|
+
const profile = dbProfileForProjection(projection);
|
|
84
|
+
const engine = profile.startsWith("sqlite") ? "sqlite" : "postgres";
|
|
84
85
|
return {
|
|
85
86
|
type: "db_schema_snapshot",
|
|
86
87
|
projection: {
|
|
87
88
|
id: projection.id,
|
|
88
89
|
name: projection.name || projection.id,
|
|
89
|
-
|
|
90
|
+
type: projection.type || projection.platform
|
|
90
91
|
},
|
|
91
|
-
profile
|
|
92
|
+
profile,
|
|
92
93
|
generatorDefaults: generatorDefaultsMap(projection),
|
|
93
94
|
engine,
|
|
94
95
|
enums: [],
|
|
@@ -97,7 +98,7 @@ function renderEmptySnapshotForProjection(projection) {
|
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
function renderDbLifecycleEnvExample(projection, plan) {
|
|
100
|
-
const engine =
|
|
101
|
+
const engine = plan.engine || (dbProfileForProjection(projection).startsWith("sqlite") ? "sqlite" : "postgres");
|
|
101
102
|
const inputPath = "../../../../topogram";
|
|
102
103
|
if (engine === "sqlite") {
|
|
103
104
|
return `DATABASE_URL=file:./var/${projection.id}.sqlite\nTOPOGRAM_INPUT_PATH=${inputPath}\n`;
|
|
@@ -580,7 +581,7 @@ function generateDbLifecycleBundle(graph, projection, options = {}) {
|
|
|
580
581
|
|
|
581
582
|
if (plan.bundle.prismaSchema) {
|
|
582
583
|
files[plan.bundle.prismaSchema] =
|
|
583
|
-
|
|
584
|
+
plan.engine === "sqlite"
|
|
584
585
|
? generateSqlitePrismaSchema(graph, { projectionId: projection.id })
|
|
585
586
|
: generatePostgresPrismaSchema(graph, { projectionId: projection.id });
|
|
586
587
|
}
|
|
@@ -50,8 +50,9 @@ function drizzleColumnBuilder(column, relation, targetTableVar) {
|
|
|
50
50
|
export function generatePostgresDrizzleSchema(graph, options = {}) {
|
|
51
51
|
resolvePostgresCapabilities(options.profileId);
|
|
52
52
|
const projection = getProjection(graph, options.projectionId);
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
const projectionType = projection.type || projection.platform;
|
|
54
|
+
if (projectionType !== "db_contract") {
|
|
55
|
+
throw new Error(`Drizzle schema generation currently supports db_contract projections only, found '${projectionType}'`);
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
const contract = buildDbProjectionContract(graph, projection);
|
|
@@ -40,8 +40,9 @@ function prismaDefaultForColumn(column, byId) {
|
|
|
40
40
|
export function generatePostgresPrismaSchema(graph, options = {}) {
|
|
41
41
|
resolvePostgresCapabilities(options.profileId);
|
|
42
42
|
const projection = getProjection(graph, options.projectionId);
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const projectionType = projection.type || projection.platform;
|
|
44
|
+
if (projectionType !== "db_contract") {
|
|
45
|
+
throw new Error(`Prisma schema generation currently supports db_contract projections only, found '${projectionType}'`);
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
const byId = indexGraphStatements(graph);
|
|
@@ -42,6 +42,7 @@ export function indexGraphStatements(graph) {
|
|
|
42
42
|
export function dbProjectionCandidates(graph) {
|
|
43
43
|
return (graph.byKind.projection || []).filter(
|
|
44
44
|
(projection) =>
|
|
45
|
+
(projection.type || projection.platform) === "db_contract" ||
|
|
45
46
|
projection.platform?.startsWith("db_") ||
|
|
46
47
|
(projection.dbTables || []).length > 0 ||
|
|
47
48
|
(projection.dbColumns || []).length > 0 ||
|
|
@@ -66,7 +67,7 @@ export function generatorDefaultsMap(projection) {
|
|
|
66
67
|
|
|
67
68
|
export function dbProfileForProjection(projection) {
|
|
68
69
|
const defaults = generatorDefaultsMap(projection);
|
|
69
|
-
return defaults.profile ||
|
|
70
|
+
return defaults.profile || "postgres_sql";
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
function mergeDbKeys(entity, projection) {
|
|
@@ -277,7 +278,7 @@ export function buildDbProjectionContract(graph, projection) {
|
|
|
277
278
|
projection: {
|
|
278
279
|
id: projection.id,
|
|
279
280
|
name: projection.name || projection.id,
|
|
280
|
-
|
|
281
|
+
type: projection.type || projection.platform
|
|
281
282
|
},
|
|
282
283
|
profile: dbProfileForProjection(projection),
|
|
283
284
|
generatorDefaults: generatorDefaultsMap(projection),
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "./shared.js";
|
|
12
12
|
|
|
13
13
|
export function normalizeDbSchemaSnapshot(contract, byId = null) {
|
|
14
|
-
const engine = contract.
|
|
14
|
+
const engine = contract.profile?.startsWith("sqlite") ? "sqlite" : "postgres";
|
|
15
15
|
const tables = [...contract.tables]
|
|
16
16
|
.map((table) => ({
|
|
17
17
|
table: table.table,
|
|
@@ -45,8 +45,9 @@ function prismaDefaultForColumn(column) {
|
|
|
45
45
|
export function generateSqlitePrismaSchema(graph, options = {}) {
|
|
46
46
|
resolveSqliteCapabilities(options.profileId);
|
|
47
47
|
const projection = getProjection(graph, options.projectionId);
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const projectionType = projection.type || projection.platform;
|
|
49
|
+
if (projectionType !== "db_contract") {
|
|
50
|
+
throw new Error(`Prisma schema generation currently supports db_contract projections only, found '${projectionType}'`);
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
const byId = indexGraphStatements(graph);
|
|
@@ -8,13 +8,13 @@ import { pickDefaultIosUiProjection, pickDefaultUiWebProjection } from "../../ru
|
|
|
8
8
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Emits a SwiftPM iOS SwiftUI app driven by the routed UI contract (default: `
|
|
11
|
+
* Emits a SwiftPM iOS SwiftUI app driven by the routed UI contract (default: `proj_ios_surface__swiftui` when present, else first `proj_web_surface__*` projection).
|
|
12
12
|
*/
|
|
13
13
|
export function generateSwiftUiApp(graph, options = {}) {
|
|
14
14
|
const fallbackId =
|
|
15
15
|
pickDefaultIosUiProjection(graph)?.id ||
|
|
16
16
|
pickDefaultUiWebProjection(graph)?.id ||
|
|
17
|
-
"
|
|
17
|
+
"proj_web_surface__sveltekit";
|
|
18
18
|
const projectionId = options.projectionId || fallbackId;
|
|
19
19
|
const realization = buildWebRealization(graph, { projectionId });
|
|
20
20
|
const apiContracts = realization.apiContracts;
|
|
@@ -31,7 +31,7 @@ export function generateSwiftUiApp(graph, options = {}) {
|
|
|
31
31
|
|
|
32
32
|
files["Package.swift"] = fs.readFileSync(path.join(__dirname, "swiftui-templates", "Package.swift.txt"), "utf8");
|
|
33
33
|
files["README.md"] = fs.readFileSync(path.join(__dirname, "swiftui-templates", "README.generated.md"), "utf8");
|
|
34
|
-
files["Sources/TodoSwiftUIApp/Resources/ui-
|
|
34
|
+
files["Sources/TodoSwiftUIApp/Resources/ui-surface-contract.json"] = contractJson;
|
|
35
35
|
files["Sources/TodoSwiftUIApp/Resources/api-contracts.json"] = apiJson;
|
|
36
36
|
|
|
37
37
|
return files;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Todo SwiftUI (generated)
|
|
2
2
|
|
|
3
|
-
Apple SwiftUI client generated from the same **`buildWebRealization`** routed UI contract as the web stacks. Prefer the **`
|
|
3
|
+
Apple SwiftUI client generated from the same **`buildWebRealization`** routed UI contract as the web stacks. Prefer the **`proj_ios_surface__swiftui`** projection when present; otherwise the generator falls back to a **`proj_web_surface__*`** projection (often **`proj_web_surface__sveltekit`**).
|
|
4
4
|
|
|
5
5
|
## Bundle inputs
|
|
6
6
|
|
|
7
|
-
- **`Resources/ui-
|
|
7
|
+
- **`Resources/ui-surface-contract.json`** — same shape as `apps/web/src/lib/topogram/ui-surface-contract.json`
|
|
8
8
|
- **`Resources/api-contracts.json`** — same shape as `apps/web/src/lib/topogram/api-contracts.json`
|
|
9
9
|
|
|
10
10
|
## Run
|
|
@@ -22,5 +22,5 @@ Configure the API base URL and demo auth token via scheme environment variables
|
|
|
22
22
|
From `engine/`:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
topogram ./topogram --generate swiftui-app --projection
|
|
25
|
+
topogram ./topogram --generate swiftui-app --projection proj_ios_surface__swiftui --write --out-dir ./app/ios-swiftui
|
|
26
26
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import SwiftUI
|
|
2
2
|
|
|
3
|
-
/// Holds decoded ui-
|
|
3
|
+
/// Holds decoded ui-surface-contract.json for runtime-driven navigation (parity with web bundle).
|
|
4
4
|
public final class TodoUiContract: ObservableObject {
|
|
5
5
|
public let raw: [String: Any]
|
|
6
6
|
|
|
@@ -12,9 +12,9 @@ public final class TodoUiContract: ObservableObject {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
public static func loadBundled() throws -> Data {
|
|
15
|
-
guard let url = Bundle.module.url(forResource: "ui-
|
|
15
|
+
guard let url = Bundle.module.url(forResource: "ui-surface-contract", withExtension: "json"),
|
|
16
16
|
let data = try? Data(contentsOf: url) else {
|
|
17
|
-
throw NSError(domain: "TodoUiContract", code: 2, userInfo: [NSLocalizedDescriptionKey: "Missing ui-
|
|
17
|
+
throw NSError(domain: "TodoUiContract", code: 2, userInfo: [NSLocalizedDescriptionKey: "Missing ui-surface-contract.json"])
|
|
18
18
|
}
|
|
19
19
|
return data
|
|
20
20
|
}
|
|
@@ -155,8 +155,9 @@ export function generatePersistenceScaffold(graph, options = {}) {
|
|
|
155
155
|
const drizzleRepositoryClassName = repositoryReference.drizzleRepositoryClassName;
|
|
156
156
|
const drizzleHint = repositoryReference.drizzleHint;
|
|
157
157
|
const projection = getProjection(graph, options.projectionId);
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
const projectionType = projection.type || projection.platform;
|
|
159
|
+
if (projectionType !== "db_contract") {
|
|
160
|
+
throw new Error(`Persistence scaffold generation currently supports db_contract projections only, found '${projectionType}'`);
|
|
160
161
|
}
|
|
161
162
|
|
|
162
163
|
const byId = indexStatements(graph);
|
|
@@ -11,7 +11,7 @@ function indexStatements(graph) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
function apiProjectionCandidates(graph) {
|
|
14
|
-
return (graph.byKind.projection || []).filter((projection) => (projection.http || []).length > 0);
|
|
14
|
+
return (graph.byKind.projection || []).filter((projection) => (projection.http || projection.endpoints || []).length > 0);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function repositoryMethodName(capabilityId) {
|
|
@@ -30,7 +30,7 @@ function buildServerContract(graph, projection) {
|
|
|
30
30
|
projection: {
|
|
31
31
|
id: projection.id,
|
|
32
32
|
name: projection.name || projection.id,
|
|
33
|
-
|
|
33
|
+
type: projection.type || projection.platform
|
|
34
34
|
},
|
|
35
35
|
routes: realizedCapabilities.map((capability) => {
|
|
36
36
|
const apiContract = generateApiContractGraph(graph, { capabilityId: capability.id });
|
|
@@ -50,8 +50,8 @@ function buildServerContract(graph, projection) {
|
|
|
50
50
|
preconditions: apiContract.endpoint.preconditions || [],
|
|
51
51
|
idempotency: apiContract.endpoint.idempotency || [],
|
|
52
52
|
cache: apiContract.endpoint.cache || [],
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
asyncJobs: apiContract.endpoint.async || [],
|
|
54
|
+
asyncStatus: apiContract.endpoint.status || [],
|
|
55
55
|
download: apiContract.endpoint.download || []
|
|
56
56
|
}
|
|
57
57
|
};
|
|
@@ -262,7 +262,7 @@ function requiredDesignMarkers(design) {
|
|
|
262
262
|
* @returns {{ coverage: any, diagnostics: any[] }}
|
|
263
263
|
*/
|
|
264
264
|
export function buildDesignIntentCoverage(contract, files, cssPath) {
|
|
265
|
-
const design = normalizeDesignIntent(contract?.
|
|
265
|
+
const design = normalizeDesignIntent(contract?.designTokens);
|
|
266
266
|
const css = files[cssPath] || "";
|
|
267
267
|
const markers = requiredDesignMarkers(design);
|
|
268
268
|
const mapped = markers.filter((item) => css.includes(item.marker));
|
|
@@ -9,9 +9,9 @@ import { generateReactApp } from "./react.js";
|
|
|
9
9
|
import { generateSvelteKitApp } from "./sveltekit.js";
|
|
10
10
|
import { generateVanillaWebApp } from "./vanilla.js";
|
|
11
11
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from "./ui-
|
|
12
|
+
generateUiSurfaceContract,
|
|
13
|
+
generateUiSurfaceDebug
|
|
14
|
+
} from "./ui-surface-contract.js";
|
|
15
15
|
|
|
16
16
|
export function generateWebApp(graph, options = {}) {
|
|
17
17
|
const projection = getProjection(graph, options.projectionId);
|
|
@@ -39,11 +39,11 @@ export function generateWebTarget(target, graph, options = {}) {
|
|
|
39
39
|
if (target === "ui-contract-debug") {
|
|
40
40
|
return generateUiContractDebug(graph, options);
|
|
41
41
|
}
|
|
42
|
-
if (target === "ui-
|
|
43
|
-
return
|
|
42
|
+
if (target === "ui-surface-contract") {
|
|
43
|
+
return generateUiSurfaceContract(graph, options);
|
|
44
44
|
}
|
|
45
|
-
if (target === "ui-
|
|
46
|
-
return
|
|
45
|
+
if (target === "ui-surface-debug") {
|
|
46
|
+
return generateUiSurfaceDebug(graph, options);
|
|
47
47
|
}
|
|
48
48
|
if (target === "sveltekit-app") {
|
|
49
49
|
return generateWebApp(graph, options);
|