@topogram/cli 0.3.59 → 0.3.60
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/package.json
CHANGED
|
@@ -182,7 +182,8 @@ function buildContractsForContext(context) {
|
|
|
182
182
|
}
|
|
183
183
|
if (surface === "native" || surface === "ios_surface" || surface === "android_surface") {
|
|
184
184
|
return {
|
|
185
|
-
uiSurface: generateUiSurfaceContract(context.graph, { ...(context.options || {}), projectionId })
|
|
185
|
+
uiSurface: generateUiSurfaceContract(context.graph, { ...(context.options || {}), projectionId }),
|
|
186
|
+
api: generateApiContractGraph(context.graph, {})
|
|
186
187
|
};
|
|
187
188
|
}
|
|
188
189
|
return {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
generateNativeBundle,
|
|
2
3
|
generateServerBundle,
|
|
3
4
|
generateWebBundle,
|
|
4
5
|
getDefaultEnvironmentProjections,
|
|
@@ -49,6 +50,12 @@ function buildCompileCheckPlan(graph, options = {}) {
|
|
|
49
50
|
command: "npm run build"
|
|
50
51
|
}
|
|
51
52
|
]);
|
|
53
|
+
const nativeChecks = topology.nativeRuntimes.map((component, index) => ({
|
|
54
|
+
id: index === 0 ? "native_swift_build" : `native_swift_build_${component.id}`,
|
|
55
|
+
cwd: topology.nativeDir(component),
|
|
56
|
+
install: null,
|
|
57
|
+
command: "swift build"
|
|
58
|
+
}));
|
|
52
59
|
return {
|
|
53
60
|
type: "compile_check_plan",
|
|
54
61
|
name: compileCheckName(graph, options),
|
|
@@ -65,7 +72,7 @@ function buildCompileCheckPlan(graph, options = {}) {
|
|
|
65
72
|
generator: runtime.generator
|
|
66
73
|
}))
|
|
67
74
|
},
|
|
68
|
-
checks: [...apiChecks, ...webChecks]
|
|
75
|
+
checks: [...apiChecks, ...webChecks, ...nativeChecks]
|
|
69
76
|
};
|
|
70
77
|
}
|
|
71
78
|
|
|
@@ -92,13 +99,14 @@ ${runtimeReference.environment.envExample || ""}
|
|
|
92
99
|
function renderCompileCheckReadme(graph, options = {}) {
|
|
93
100
|
return `# ${compileCheckName(graph, options).replace("Plan", "Bundle")}
|
|
94
101
|
|
|
95
|
-
This bundle verifies that
|
|
102
|
+
This bundle verifies that generated server, web, and native projects compile.
|
|
96
103
|
|
|
97
104
|
## Checks
|
|
98
105
|
|
|
99
106
|
- server TypeScript check
|
|
100
107
|
- web TypeScript check
|
|
101
108
|
- web production build
|
|
109
|
+
- native Swift build
|
|
102
110
|
|
|
103
111
|
## Usage
|
|
104
112
|
|
|
@@ -124,15 +132,17 @@ function renderCompileCheckScript(plan) {
|
|
|
124
132
|
""
|
|
125
133
|
];
|
|
126
134
|
if (plan.checks.length === 0) {
|
|
127
|
-
lines.push('echo "No API or
|
|
135
|
+
lines.push('echo "No API, web, or native runtimes are configured; compile check is a no-op."');
|
|
128
136
|
}
|
|
129
137
|
for (const check of plan.checks) {
|
|
130
138
|
const label = check.id.includes("web")
|
|
131
139
|
? check.id.includes("build") ? "Building generated web" : "Checking generated web"
|
|
132
|
-
: "Checking generated server";
|
|
140
|
+
: check.id.includes("native") ? "Building generated native app" : "Checking generated server";
|
|
133
141
|
lines.push(`echo "${label} (${check.cwd})..."`);
|
|
134
|
-
|
|
135
|
-
|
|
142
|
+
if (check.install) {
|
|
143
|
+
lines.push(`echo "Installing dependencies (${check.cwd})..."`);
|
|
144
|
+
lines.push(`(cd "$ROOT_DIR/${check.cwd}" && ${check.install})`);
|
|
145
|
+
}
|
|
136
146
|
lines.push(`echo "Running ${check.command} (${check.cwd})..."`);
|
|
137
147
|
lines.push(`(cd "$ROOT_DIR/${check.cwd}" && ${check.command})`);
|
|
138
148
|
lines.push("");
|
|
@@ -158,6 +168,10 @@ export function generateCompileCheckBundle(graph, options = {}) {
|
|
|
158
168
|
const webBundle = generateWebBundle(graph, component.projection.id, { ...options, component });
|
|
159
169
|
mergeBundleFiles(files, topology.webDir(component), webBundle);
|
|
160
170
|
}
|
|
171
|
+
for (const component of topology.nativeRuntimes) {
|
|
172
|
+
const nativeBundle = generateNativeBundle(graph, component.projection.id, { ...options, component });
|
|
173
|
+
mergeBundleFiles(files, topology.nativeDir(component), nativeBundle);
|
|
174
|
+
}
|
|
161
175
|
return files;
|
|
162
176
|
}
|
|
163
177
|
|
|
@@ -2,6 +2,7 @@ import { generateDbLifecyclePlan } from "../surfaces/databases/lifecycle-shared.
|
|
|
2
2
|
import { getExampleImplementation } from "../../example-implementation.js";
|
|
3
3
|
import {
|
|
4
4
|
generateDbBundle,
|
|
5
|
+
generateNativeBundle,
|
|
5
6
|
generateServerBundle,
|
|
6
7
|
generateWebBundle,
|
|
7
8
|
dbEnvVarsForComponent,
|
|
@@ -110,6 +111,7 @@ function buildEnvironmentPlan(graph, options = {}) {
|
|
|
110
111
|
server: topology.primaryApi ? topology.serviceDir(topology.primaryApi) : null,
|
|
111
112
|
web: topology.primaryWeb ? topology.webDir(topology.primaryWeb) : null,
|
|
112
113
|
db: topology.primaryDb ? topology.dbDir(topology.primaryDb) : null,
|
|
114
|
+
native: topology.nativeRuntimes[0] ? topology.nativeDir(topology.nativeRuntimes[0]) : null,
|
|
113
115
|
scripts: "scripts"
|
|
114
116
|
},
|
|
115
117
|
runtimes: {
|
|
@@ -130,6 +132,12 @@ function buildEnvironmentPlan(graph, options = {}) {
|
|
|
130
132
|
dir: topology.webDir(component),
|
|
131
133
|
uses_api: component.api
|
|
132
134
|
})),
|
|
135
|
+
natives: topology.nativeRuntimes.map((component) => ({
|
|
136
|
+
id: component.id,
|
|
137
|
+
projection: component.projection.id,
|
|
138
|
+
dir: topology.nativeDir(component),
|
|
139
|
+
uses_api: component.api
|
|
140
|
+
})),
|
|
133
141
|
databases: topology.dbRuntimes.map((component) => ({
|
|
134
142
|
id: component.id,
|
|
135
143
|
projection: component.projection.id,
|
|
@@ -273,6 +281,7 @@ function renderEnvironmentReadme(plan) {
|
|
|
273
281
|
const hasDb = plan.runtimes.databases.length > 0;
|
|
274
282
|
const hasApi = plan.runtimes.apis.length > 0;
|
|
275
283
|
const hasWeb = plan.runtimes.webs.length > 0;
|
|
284
|
+
const hasNative = plan.runtimes.natives.length > 0;
|
|
276
285
|
const localProcessNotes = !hasDb
|
|
277
286
|
? "- This bundle has no generated database surface."
|
|
278
287
|
: plan.projections.db.engine === "sqlite"
|
|
@@ -294,7 +303,7 @@ ${localProcessNotes}
|
|
|
294
303
|
|
|
295
304
|
This bundle packages the generated runtime into one local environment:
|
|
296
305
|
|
|
297
|
-
${hasApi ? "- `services/<api-id>/`: generated API service scaffolds\n" : ""}${hasWeb ? `- \`web/<web-id>/\`: generated ${plan.runtimeProfiles.web === "react" ? "Vite + React Router" : plan.runtimeProfiles.web === "vanilla" ? "vanilla HTML/CSS/JS" : "SvelteKit"} web scaffolds\n` : ""}${hasDb ? "- `db/<db-id>/`: generated DB lifecycle bundles\n" : ""}${plan.files.dockerCompose ? `- \`${plan.files.dockerCompose}\`: local Postgres container` : hasDb ? (plan.projections.db.engine === "sqlite" ? "- local SQLite file orchestration (no Docker files generated)" : "- local-process Postgres orchestration (no Docker files generated)") : "- no DB orchestration is generated"}
|
|
306
|
+
${hasApi ? "- `services/<api-id>/`: generated API service scaffolds\n" : ""}${hasWeb ? `- \`web/<web-id>/\`: generated ${plan.runtimeProfiles.web === "react" ? "Vite + React Router" : plan.runtimeProfiles.web === "vanilla" ? "vanilla HTML/CSS/JS" : "SvelteKit"} web scaffolds\n` : ""}${hasNative ? "- `native/<native-id>/`: generated native app scaffolds\n" : ""}${hasDb ? "- `db/<db-id>/`: generated DB lifecycle bundles\n" : ""}${plan.files.dockerCompose ? `- \`${plan.files.dockerCompose}\`: local Postgres container` : hasDb ? (plan.projections.db.engine === "sqlite" ? "- local SQLite file orchestration (no Docker files generated)" : "- local-process Postgres orchestration (no Docker files generated)") : "- no DB orchestration is generated"}
|
|
298
307
|
|
|
299
308
|
## Quick Start
|
|
300
309
|
|
|
@@ -318,6 +327,7 @@ ${dockerSection}
|
|
|
318
327
|
|
|
319
328
|
- ${hasApi && hasDb ? `The generated server expects ${plan.projections.db.engine === "sqlite" ? "SQLite plus Prisma." : "Postgres plus Prisma."}` : hasApi ? "The generated server is stateless." : "No server surface is generated."}
|
|
320
329
|
- ${hasWeb && hasApi ? "The generated web app talks to `PUBLIC_TOPOGRAM_API_BASE_URL`." : hasWeb ? "The generated web app is standalone." : "No web surface is generated."}
|
|
330
|
+
- ${hasNative ? "Native app scaffolds use the same UI surface contracts as web surfaces." : "No native surface is generated."}
|
|
321
331
|
- If \`.env\` is missing, generated scripts fall back to \`.env.example\`.
|
|
322
332
|
- The DB lifecycle scripts remain the source of truth for greenfield bootstrap and brownfield migration.
|
|
323
333
|
`;
|
|
@@ -635,6 +645,12 @@ export function generateEnvironmentBundle(graph, options = {}) {
|
|
|
635
645
|
[topology.webDir(component)]: webBundle
|
|
636
646
|
});
|
|
637
647
|
}
|
|
648
|
+
for (const component of topology.nativeRuntimes) {
|
|
649
|
+
const nativeBundle = generateNativeBundle(graph, component.projection.id, { ...options, component });
|
|
650
|
+
mergeNamedBundles(files, {
|
|
651
|
+
[topology.nativeDir(component)]: nativeBundle
|
|
652
|
+
});
|
|
653
|
+
}
|
|
638
654
|
for (const component of topology.dbRuntimes) {
|
|
639
655
|
const dbBundle = generateDbBundle(graph, component.projection.id, { ...options, component });
|
|
640
656
|
mergeNamedBundles(files, {
|
|
@@ -43,16 +43,19 @@ import { defaultProjectConfigForGraph, validateProjectConfig } from "../../proje
|
|
|
43
43
|
* @property {RuntimeComponent[]} runtimes
|
|
44
44
|
* @property {RuntimeComponent[]} apiRuntimes
|
|
45
45
|
* @property {RuntimeComponent[]} webRuntimes
|
|
46
|
+
* @property {RuntimeComponent[]} nativeRuntimes
|
|
46
47
|
* @property {RuntimeComponent[]} dbRuntimes
|
|
47
48
|
* @property {RuntimeComponent[]} components Legacy alias for runtimes.
|
|
48
49
|
* @property {RuntimeComponent[]} apiComponents Legacy alias for apiRuntimes.
|
|
49
50
|
* @property {RuntimeComponent[]} webComponents Legacy alias for webRuntimes.
|
|
51
|
+
* @property {RuntimeComponent[]} nativeComponents Legacy alias for nativeRuntimes.
|
|
50
52
|
* @property {RuntimeComponent[]} dbComponents Legacy alias for dbRuntimes.
|
|
51
53
|
* @property {RuntimeComponent|null} primaryApi
|
|
52
54
|
* @property {RuntimeComponent|null} primaryWeb
|
|
53
55
|
* @property {RuntimeComponent|null} primaryDb
|
|
54
56
|
* @property {(component: RuntimeComponent) => string} serviceDir
|
|
55
57
|
* @property {(component: RuntimeComponent) => string} webDir
|
|
58
|
+
* @property {(component: RuntimeComponent) => string} nativeDir
|
|
56
59
|
* @property {(component: RuntimeComponent) => string} dbDir
|
|
57
60
|
*/
|
|
58
61
|
|
|
@@ -371,6 +374,29 @@ export function generateWebBundle(graph, projectionId, options = {}) {
|
|
|
371
374
|
}).files;
|
|
372
375
|
}
|
|
373
376
|
|
|
377
|
+
/**
|
|
378
|
+
* @param {ResolvedGraph} graph
|
|
379
|
+
* @param {string} projectionId
|
|
380
|
+
* @param {RuntimeGenerationOptions} [options]
|
|
381
|
+
* @returns {any}
|
|
382
|
+
*/
|
|
383
|
+
export function generateNativeBundle(graph, projectionId, options = {}) {
|
|
384
|
+
const topology = resolveRuntimeTopology(graph, options);
|
|
385
|
+
const runtime = options.runtime || options.component || topology.nativeRuntimes.find((entry) => entry.projection.id === projectionId);
|
|
386
|
+
if (!runtime) {
|
|
387
|
+
throw new Error(`No native runtime found for projection '${projectionId}'`);
|
|
388
|
+
}
|
|
389
|
+
return generateWithComponentGenerator({
|
|
390
|
+
graph,
|
|
391
|
+
projection: runtime.projection,
|
|
392
|
+
runtime,
|
|
393
|
+
component: runtime,
|
|
394
|
+
topology,
|
|
395
|
+
implementation: options.implementation || null,
|
|
396
|
+
options: { ...options, projectionId }
|
|
397
|
+
}).files;
|
|
398
|
+
}
|
|
399
|
+
|
|
374
400
|
/**
|
|
375
401
|
* @param {ResolvedGraph} graph
|
|
376
402
|
* @param {string} projectionId
|
|
@@ -450,7 +476,7 @@ function decorateRuntimes(graph, config) {
|
|
|
450
476
|
runtime.databaseRuntime = byId.get(runtime.database) || null;
|
|
451
477
|
runtime.databaseComponent = runtime.databaseRuntime;
|
|
452
478
|
}
|
|
453
|
-
if (
|
|
479
|
+
if (["web_surface", "ios_surface", "android_surface"].includes(runtime.kind) && runtime.api) {
|
|
454
480
|
runtime.apiRuntime = byId.get(runtime.api) || null;
|
|
455
481
|
runtime.apiComponent = runtime.apiRuntime;
|
|
456
482
|
}
|
|
@@ -475,6 +501,7 @@ export function resolveRuntimeTopology(graph, options = {}) {
|
|
|
475
501
|
const runtimes = decorateRuntimes(graph, config);
|
|
476
502
|
const apiRuntimes = runtimes.filter((runtime) => runtime.kind === "api_service");
|
|
477
503
|
const webRuntimes = runtimes.filter((runtime) => runtime.kind === "web_surface");
|
|
504
|
+
const nativeRuntimes = runtimes.filter((runtime) => runtime.kind === "ios_surface" || runtime.kind === "android_surface");
|
|
478
505
|
const dbRuntimes = runtimes.filter((runtime) => runtime.kind === "database");
|
|
479
506
|
const primaryApi = apiRuntimes[0] || null;
|
|
480
507
|
const primaryWeb = webRuntimes[0] || null;
|
|
@@ -486,9 +513,11 @@ export function resolveRuntimeTopology(graph, options = {}) {
|
|
|
486
513
|
components: runtimes,
|
|
487
514
|
apiRuntimes,
|
|
488
515
|
webRuntimes,
|
|
516
|
+
nativeRuntimes,
|
|
489
517
|
dbRuntimes,
|
|
490
518
|
apiComponents: apiRuntimes,
|
|
491
519
|
webComponents: webRuntimes,
|
|
520
|
+
nativeComponents: nativeRuntimes,
|
|
492
521
|
dbComponents: dbRuntimes,
|
|
493
522
|
primaryApi,
|
|
494
523
|
primaryWeb,
|
|
@@ -499,6 +528,9 @@ export function resolveRuntimeTopology(graph, options = {}) {
|
|
|
499
528
|
webDir(component) {
|
|
500
529
|
return `web/${component.id}`;
|
|
501
530
|
},
|
|
531
|
+
nativeDir(component) {
|
|
532
|
+
return `native/${component.id}`;
|
|
533
|
+
},
|
|
502
534
|
dbDir(component) {
|
|
503
535
|
return `db/${component.id}`;
|
|
504
536
|
}
|