@topogram/cli 0.3.59 → 0.3.61
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
package/src/cli.js
CHANGED
|
@@ -153,6 +153,17 @@ const KNOWN_CLI_CONSUMER_WORKFLOWS = {
|
|
|
153
153
|
"topogram-hello": "Topogram Package Verification",
|
|
154
154
|
"topograms": "Catalog Verification"
|
|
155
155
|
};
|
|
156
|
+
const KNOWN_CLI_CONSUMER_WORKFLOW_JOBS = {
|
|
157
|
+
"topograms": [
|
|
158
|
+
"Validate catalog",
|
|
159
|
+
"Smoke native starter",
|
|
160
|
+
"Smoke starter alias (hello-web)",
|
|
161
|
+
"Smoke starter alias (hello-api)",
|
|
162
|
+
"Smoke starter alias (hello-db)",
|
|
163
|
+
"Smoke starter alias (web-api)",
|
|
164
|
+
"Smoke starter alias (web-api-db)"
|
|
165
|
+
]
|
|
166
|
+
};
|
|
156
167
|
const PACKAGE_UPDATE_CLI_CHECK_SCRIPTS = [
|
|
157
168
|
"cli:surface",
|
|
158
169
|
"doctor",
|
|
@@ -3211,6 +3222,14 @@ function expectedConsumerWorkflowName(name) {
|
|
|
3211
3222
|
return KNOWN_CLI_CONSUMER_WORKFLOWS[name] || null;
|
|
3212
3223
|
}
|
|
3213
3224
|
|
|
3225
|
+
/**
|
|
3226
|
+
* @param {string} name
|
|
3227
|
+
* @returns {string[]}
|
|
3228
|
+
*/
|
|
3229
|
+
function expectedConsumerWorkflowJobs(name) {
|
|
3230
|
+
return KNOWN_CLI_CONSUMER_WORKFLOW_JOBS[name] || [];
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3214
3233
|
/**
|
|
3215
3234
|
* @param {string[]} args
|
|
3216
3235
|
* @param {string} cwd
|
|
@@ -3351,16 +3370,18 @@ function waitForConsumerCi(consumer, options = {}) {
|
|
|
3351
3370
|
/**
|
|
3352
3371
|
* @param {{ name: string, root?: string|null, workflow?: string|null }} consumer
|
|
3353
3372
|
* @param {{ strict?: boolean }} [options]
|
|
3354
|
-
* @returns {{ checked: boolean, ok: boolean|null, expectedWorkflow: string|null, headSha: string|null, run: { databaseId?: number, workflowName?: string, status?: string, conclusion?: string, headSha?: string, url?: string }|null, diagnostics: Array<Record<string, any>> }}
|
|
3373
|
+
* @returns {{ checked: boolean, ok: boolean|null, expectedWorkflow: string|null, expectedJobs: string[], headSha: string|null, run: { databaseId?: number, workflowName?: string, status?: string, conclusion?: string, headSha?: string, url?: string, jobs?: Array<Record<string, any>> }|null, diagnostics: Array<Record<string, any>> }}
|
|
3355
3374
|
*/
|
|
3356
3375
|
function inspectConsumerCi(consumer, options = {}) {
|
|
3357
3376
|
const diagnostics = [];
|
|
3358
3377
|
const expectedWorkflow = consumer.workflow || expectedConsumerWorkflowName(consumer.name);
|
|
3378
|
+
const expectedJobs = expectedConsumerWorkflowJobs(consumer.name);
|
|
3359
3379
|
if (!consumer.root || !fs.existsSync(consumer.root)) {
|
|
3360
3380
|
return {
|
|
3361
3381
|
checked: false,
|
|
3362
3382
|
ok: null,
|
|
3363
3383
|
expectedWorkflow,
|
|
3384
|
+
expectedJobs,
|
|
3364
3385
|
headSha: null,
|
|
3365
3386
|
run: null,
|
|
3366
3387
|
diagnostics: []
|
|
@@ -3388,6 +3409,7 @@ function inspectConsumerCi(consumer, options = {}) {
|
|
|
3388
3409
|
checked: true,
|
|
3389
3410
|
ok: false,
|
|
3390
3411
|
expectedWorkflow,
|
|
3412
|
+
expectedJobs,
|
|
3391
3413
|
headSha,
|
|
3392
3414
|
run: null,
|
|
3393
3415
|
diagnostics
|
|
@@ -3424,6 +3446,7 @@ function inspectConsumerCi(consumer, options = {}) {
|
|
|
3424
3446
|
checked: true,
|
|
3425
3447
|
ok: false,
|
|
3426
3448
|
expectedWorkflow,
|
|
3449
|
+
expectedJobs,
|
|
3427
3450
|
headSha,
|
|
3428
3451
|
run: null,
|
|
3429
3452
|
diagnostics
|
|
@@ -3454,6 +3477,7 @@ function inspectConsumerCi(consumer, options = {}) {
|
|
|
3454
3477
|
checked: true,
|
|
3455
3478
|
ok: false,
|
|
3456
3479
|
expectedWorkflow,
|
|
3480
|
+
expectedJobs,
|
|
3457
3481
|
headSha,
|
|
3458
3482
|
run: null,
|
|
3459
3483
|
diagnostics
|
|
@@ -3477,17 +3501,105 @@ function inspectConsumerCi(consumer, options = {}) {
|
|
|
3477
3501
|
suggestedFix: "Wait for or fix the consumer verification workflow, then rerun release status."
|
|
3478
3502
|
});
|
|
3479
3503
|
}
|
|
3504
|
+
if (expectedJobs.length > 0 && run.databaseId) {
|
|
3505
|
+
const jobResult = inspectConsumerWorkflowJobs(consumer, run.databaseId, expectedJobs, options);
|
|
3506
|
+
if (jobResult.jobs) {
|
|
3507
|
+
run.jobs = jobResult.jobs;
|
|
3508
|
+
}
|
|
3509
|
+
diagnostics.push(...jobResult.diagnostics);
|
|
3510
|
+
} else if (expectedJobs.length > 0) {
|
|
3511
|
+
diagnostics.push({
|
|
3512
|
+
code: "release_consumer_ci_jobs_unavailable",
|
|
3513
|
+
severity: options.strict ? "error" : "warning",
|
|
3514
|
+
message: `${consumer.name} ${expectedWorkflow} run did not include a database id, so expected jobs could not be inspected.`,
|
|
3515
|
+
path: run.url || `attebury/${consumer.name}`,
|
|
3516
|
+
suggestedFix: "Rerun release status after GitHub exposes the workflow run id."
|
|
3517
|
+
});
|
|
3518
|
+
}
|
|
3519
|
+
const errorCount = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
3480
3520
|
return {
|
|
3481
3521
|
checked: true,
|
|
3482
|
-
ok:
|
|
3522
|
+
ok: errorCount === 0 &&
|
|
3483
3523
|
(!options.strict || (run.status === "completed" && run.conclusion === "success" && (!headSha || !run.headSha || run.headSha === headSha))),
|
|
3484
3524
|
expectedWorkflow,
|
|
3525
|
+
expectedJobs,
|
|
3485
3526
|
headSha,
|
|
3486
3527
|
run,
|
|
3487
3528
|
diagnostics
|
|
3488
3529
|
};
|
|
3489
3530
|
}
|
|
3490
3531
|
|
|
3532
|
+
/**
|
|
3533
|
+
* @param {{ name: string, root?: string|null }} consumer
|
|
3534
|
+
* @param {number|string} runId
|
|
3535
|
+
* @param {string[]} expectedJobs
|
|
3536
|
+
* @param {{ strict?: boolean }} [options]
|
|
3537
|
+
* @returns {{ jobs: Array<Record<string, any>>|null, diagnostics: Array<Record<string, any>> }}
|
|
3538
|
+
*/
|
|
3539
|
+
function inspectConsumerWorkflowJobs(consumer, runId, expectedJobs, options = {}) {
|
|
3540
|
+
const diagnostics = [];
|
|
3541
|
+
const result = childProcess.spawnSync("gh", [
|
|
3542
|
+
"run",
|
|
3543
|
+
"view",
|
|
3544
|
+
String(runId),
|
|
3545
|
+
"--repo",
|
|
3546
|
+
`attebury/${consumer.name}`,
|
|
3547
|
+
"--json",
|
|
3548
|
+
"jobs"
|
|
3549
|
+
], {
|
|
3550
|
+
cwd: consumer.root || process.cwd(),
|
|
3551
|
+
encoding: "utf8",
|
|
3552
|
+
env: { ...process.env, PATH: process.env.PATH || "" }
|
|
3553
|
+
});
|
|
3554
|
+
if (result.status !== 0) {
|
|
3555
|
+
diagnostics.push(commandDiagnostic({
|
|
3556
|
+
code: "release_consumer_ci_jobs_unavailable",
|
|
3557
|
+
severity: options.strict ? "error" : "warning",
|
|
3558
|
+
message: `Could not inspect expected jobs for ${consumer.name}.`,
|
|
3559
|
+
path: `attebury/${consumer.name}`,
|
|
3560
|
+
suggestedFix: "Check GitHub CLI auth/network access, then rerun release status.",
|
|
3561
|
+
result
|
|
3562
|
+
}));
|
|
3563
|
+
return { jobs: null, diagnostics };
|
|
3564
|
+
}
|
|
3565
|
+
let payload = {};
|
|
3566
|
+
try {
|
|
3567
|
+
payload = JSON.parse(String(result.stdout || "{}"));
|
|
3568
|
+
} catch (error) {
|
|
3569
|
+
diagnostics.push({
|
|
3570
|
+
code: "release_consumer_ci_jobs_unreadable",
|
|
3571
|
+
severity: options.strict ? "error" : "warning",
|
|
3572
|
+
message: `Could not parse ${consumer.name} workflow job status: ${messageFromError(error)}`,
|
|
3573
|
+
path: `attebury/${consumer.name}`,
|
|
3574
|
+
suggestedFix: "Rerun release status after GitHub CLI output is valid JSON."
|
|
3575
|
+
});
|
|
3576
|
+
}
|
|
3577
|
+
const jobs = Array.isArray(payload.jobs) ? payload.jobs : [];
|
|
3578
|
+
for (const expectedJob of expectedJobs) {
|
|
3579
|
+
const job = jobs.find((candidate) => candidate?.name === expectedJob);
|
|
3580
|
+
if (!job) {
|
|
3581
|
+
diagnostics.push({
|
|
3582
|
+
code: "release_consumer_ci_job_missing",
|
|
3583
|
+
severity: options.strict ? "error" : "warning",
|
|
3584
|
+
message: `${consumer.name} workflow is missing expected job '${expectedJob}'.`,
|
|
3585
|
+
path: `attebury/${consumer.name}`,
|
|
3586
|
+
suggestedFix: "Update the consumer workflow or the release-status expected job list, then rerun release status."
|
|
3587
|
+
});
|
|
3588
|
+
continue;
|
|
3589
|
+
}
|
|
3590
|
+
if (job.status !== "completed" || job.conclusion !== "success") {
|
|
3591
|
+
diagnostics.push({
|
|
3592
|
+
code: "release_consumer_ci_job_not_successful",
|
|
3593
|
+
severity: options.strict ? "error" : "warning",
|
|
3594
|
+
message: `${consumer.name} job '${expectedJob}' is ${job.status || "unknown"}/${job.conclusion || "unknown"}.`,
|
|
3595
|
+
path: job.url || `attebury/${consumer.name}`,
|
|
3596
|
+
suggestedFix: "Wait for or fix the expected workflow job, then rerun release status."
|
|
3597
|
+
});
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
return { jobs, diagnostics };
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3491
3603
|
/**
|
|
3492
3604
|
* @param {string} cwd
|
|
3493
3605
|
* @returns {Array<{ name: string, root: string|null, path: string, version: string|null, found: boolean }>}
|
|
@@ -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
|
}
|