@fabricorg/fabric-gen 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/README.md +5 -0
- package/dist/{chunk-CUNWMAA2.js → chunk-4QFNFERM.js} +169 -7
- package/dist/chunk-4QFNFERM.js.map +1 -0
- package/dist/cli.cjs +165 -5
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +169 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +5 -1
- package/package.json +13 -12
- package/dist/chunk-CUNWMAA2.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,88 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e9a2f5e: Add `fabric-gen promote-experience`, which turns a validated release into a signed one from a
|
|
8
|
+
declared configuration: the assembly it was validated against, the release, and the usage contracts
|
|
9
|
+
routes are compiled from.
|
|
10
|
+
|
|
11
|
+
The signing key is a `--key` argument rather than a configuration field, and a configuration that
|
|
12
|
+
carries one is refused, because a configuration file is the kind of thing that ends up committed.
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- a598fb9: Close the remaining review findings.
|
|
17
|
+
|
|
18
|
+
**One capability-reference grammar.** `sdui-release` accepted any name after the namespace while
|
|
19
|
+
`sdui-composer` rejected `/`, so a view named `orders/reports/daily` produced a release that validated
|
|
20
|
+
and then died at promotion with nothing wrong upstream to point at. Both now share
|
|
21
|
+
`parseCapabilityRef`, tightened to lowercase path segments matching what the platform permits for a
|
|
22
|
+
view name. Two views in one contract that reduce to the same reference are reported as
|
|
23
|
+
`ambiguous_capability_reference` at validation rather than surfacing as an ambiguous route later.
|
|
24
|
+
|
|
25
|
+
**A bounded replay window.** A client-supplied `capturedAt` had no lower bound. Deferring it to the
|
|
26
|
+
Host looked safe and was not: the Host checks staleness only when the capture-authority adapter
|
|
27
|
+
supplies an `expiresAt`, and skips the execution-time expiry check entirely for actions governed at
|
|
28
|
+
capture. A gateway that accepts replay must now declare `maxOfflineReplayAgeMs`, and the instant must
|
|
29
|
+
be RFC 3339 with an explicit offset.
|
|
30
|
+
|
|
31
|
+
**One token-set grammar.** `document.tokenSet` accepted names and versions that `supportedTokenSets`
|
|
32
|
+
rejected, while validation treats the two fields as interchangeable.
|
|
33
|
+
|
|
34
|
+
**Port requirements narrow as they are checked.** A requirement for a standard _and_ a version could
|
|
35
|
+
be satisfied by two different adapters covering one constraint each. `requirement.standard.version`
|
|
36
|
+
was declared and never read.
|
|
37
|
+
|
|
38
|
+
- 0d1f6f6: Close what a third independent review found, including two defects introduced by the previous fix.
|
|
39
|
+
|
|
40
|
+
**Offline replay counted refusals as successes.** `PlatformHost` reports a refused capture by
|
|
41
|
+
_returning_ `reconciliation_required` with the reason, not by throwing, so the queue took its success
|
|
42
|
+
branch, deleted the capture and reported it applied. The reconciliation path was unreachable. Replay
|
|
43
|
+
now reads the returned outcome. The already-applied branch is removed entirely: a repeated key
|
|
44
|
+
returns the original invocation without complaint, and an idempotency _conflict_ means the key was
|
|
45
|
+
reused for a materially different command, so that capture was never applied and is kept rather than
|
|
46
|
+
discarded.
|
|
47
|
+
|
|
48
|
+
**`deriveAuthorizationGrants` defaulted open and reached the signed artifact.** Requirements are
|
|
49
|
+
optional metadata, so a capability declaring none granted everything to an actor holding nothing.
|
|
50
|
+
Grants are also carried onto a release unexamined and promotion signs a route for each, so a surplus
|
|
51
|
+
grant became a signed route to a capability the screen never renders. Undeclared requirements now
|
|
52
|
+
withhold by default, and `document` scopes the result to references the screen actually binds.
|
|
53
|
+
|
|
54
|
+
Several conformance checks could not fail: a search suite passed an adapter returning nothing, a
|
|
55
|
+
payments suite passed a double-charger that omitted a provider reference, an identity suite certified
|
|
56
|
+
timestamps the scope check then rejected on every request, an MFE probe could be refused for an
|
|
57
|
+
unrelated reason, and a channel check accepted any exception as a refusal. Each now proves what it
|
|
58
|
+
claims. Promotion configuration rejects anything reading like a key rather than two spellings,
|
|
59
|
+
rejects unknown fields, and constrains the application name that decides the default output path.
|
|
60
|
+
|
|
61
|
+
- Updated dependencies [8ce5e06]
|
|
62
|
+
- Updated dependencies [a598fb9]
|
|
63
|
+
- Updated dependencies [8f75ac9]
|
|
64
|
+
- Updated dependencies [9a5b18a]
|
|
65
|
+
- Updated dependencies [9b2e55b]
|
|
66
|
+
- Updated dependencies [0d1f6f6]
|
|
67
|
+
- Updated dependencies [274d948]
|
|
68
|
+
- @fabricorg/sdui-release@0.5.0
|
|
69
|
+
- @fabricorg/sdui-composer@0.2.0
|
|
70
|
+
- @fabricorg/assembly@0.4.0
|
|
71
|
+
- @fabricorg/gen-conformance@1.0.0
|
|
72
|
+
- @fabricorg/gen-graphql@4.0.0
|
|
73
|
+
|
|
74
|
+
## 0.5.0
|
|
75
|
+
|
|
76
|
+
### Minor Changes
|
|
77
|
+
|
|
78
|
+
- Add `fabric-gen init experience` for wiring the supported experience tier into an
|
|
79
|
+
existing application without generating a competing shell.
|
|
80
|
+
|
|
81
|
+
### Patch Changes
|
|
82
|
+
|
|
83
|
+
- Updated dependencies
|
|
84
|
+
- @fabricorg/sdui-release@0.4.0
|
|
85
|
+
|
|
3
86
|
## 0.4.1
|
|
4
87
|
|
|
5
88
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -7,9 +7,14 @@ Create a compiler-valid capability without copying a repository template:
|
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
9
|
fabric-gen init capability case-review
|
|
10
|
+
fabric-gen init experience portfolio --directory apps/saas/modules/portfolio
|
|
10
11
|
fabric-gen init capability case-review --directory packages/case-review
|
|
11
12
|
```
|
|
12
13
|
|
|
14
|
+
`init experience` adds runtime, gateway, component-pack, and application-check
|
|
15
|
+
integration files to an existing Supastarter-derived application. It never creates or
|
|
16
|
+
replaces the application shell.
|
|
17
|
+
|
|
13
18
|
Initialization refuses to overwrite either `README.md` or
|
|
14
19
|
`fabric.manifest.json`.
|
|
15
20
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// index.ts
|
|
2
2
|
import { access, mkdir, readFile, readdir, rm, writeFile } from "fs/promises";
|
|
3
|
+
import { createPrivateKey } from "crypto";
|
|
3
4
|
import path from "path";
|
|
4
5
|
import { compileManifestBundle, runGenerator } from "@fabricorg/compiler";
|
|
5
6
|
import { capabilityGenerator } from "@fabricorg/gen-capability";
|
|
@@ -9,6 +10,7 @@ import { typesGenerator } from "@fabricorg/gen-types";
|
|
|
9
10
|
import { prismaGenerator } from "@fabricorg/gen-prisma";
|
|
10
11
|
import { graphqlGenerator } from "@fabricorg/gen-graphql";
|
|
11
12
|
import { sduiGenerator } from "@fabricorg/gen-sdui";
|
|
13
|
+
import { createEs256ReleaseSigner, promoteExperienceRelease } from "@fabricorg/sdui-composer";
|
|
12
14
|
import { conformanceGenerator } from "@fabricorg/gen-conformance";
|
|
13
15
|
import {
|
|
14
16
|
assertAssemblyLockfile,
|
|
@@ -31,7 +33,9 @@ var compareCanonicalStrings = (left, right) => left < right ? -1 : left > right
|
|
|
31
33
|
var usage = [
|
|
32
34
|
"Usage:",
|
|
33
35
|
" fabric-gen init capability <namespace> [--directory <path>]",
|
|
36
|
+
" fabric-gen init experience <application> [--directory <path>]",
|
|
34
37
|
" fabric-gen check-application [--config <path>]",
|
|
38
|
+
" fabric-gen promote-experience --config <path> --key <pem> [--out <path>]",
|
|
35
39
|
" fabric-gen --input <manifest.json> --out <directory> [--targets <list>] [--check]"
|
|
36
40
|
].join("\n");
|
|
37
41
|
var FabricGenUsageError = class extends Error {
|
|
@@ -49,13 +53,37 @@ function flagValue(args, flag) {
|
|
|
49
53
|
}
|
|
50
54
|
async function runFabricGenCommand(args) {
|
|
51
55
|
if (args[0] === "init") {
|
|
52
|
-
if (
|
|
56
|
+
if (!args[2] || args[2].startsWith("--")) {
|
|
53
57
|
throw new FabricGenUsageError(usage);
|
|
54
58
|
}
|
|
55
|
-
const
|
|
56
|
-
const directory = flagValue(args, "--directory") ??
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
const name = args[2];
|
|
60
|
+
const directory = flagValue(args, "--directory") ?? name;
|
|
61
|
+
if (args[1] === "capability") {
|
|
62
|
+
const result2 = await initializeCapability({ directory, namespace: name });
|
|
63
|
+
return `Initialized capability "${name}" with ${result2.created.length} files.`;
|
|
64
|
+
}
|
|
65
|
+
if (args[1] === "experience") {
|
|
66
|
+
const result2 = await initializeExperience({ directory, application: name });
|
|
67
|
+
return `Initialized experience integration "${name}" with ${result2.created.length} files.`;
|
|
68
|
+
}
|
|
69
|
+
throw new FabricGenUsageError(usage);
|
|
70
|
+
}
|
|
71
|
+
if (args[0] === "promote-experience") {
|
|
72
|
+
const configPath = flagValue(args, "--config");
|
|
73
|
+
const privateKeyPath = flagValue(args, "--key");
|
|
74
|
+
if (!configPath) throw new FabricGenUsageError("promote-experience requires --config.");
|
|
75
|
+
if (!privateKeyPath) throw new FabricGenUsageError("promote-experience requires --key.");
|
|
76
|
+
const outputPath = flagValue(args, "--out");
|
|
77
|
+
const result2 = await promoteExperience({
|
|
78
|
+
configPath,
|
|
79
|
+
privateKeyPath,
|
|
80
|
+
...outputPath ? { outputPath } : {}
|
|
81
|
+
});
|
|
82
|
+
return [
|
|
83
|
+
`Promoted ${result2.application} release ${result2.releaseDigest.slice(0, 12)} against assembly ${result2.assemblyDigest.slice(0, 12)}.`,
|
|
84
|
+
`Routes: ${result2.routes.views} views, ${result2.routes.intents} intents.`,
|
|
85
|
+
`Wrote ${result2.outputPath}`
|
|
86
|
+
].join("\n");
|
|
59
87
|
}
|
|
60
88
|
if (args[0] === "check-application") {
|
|
61
89
|
const configPath = flagValue(args, "--config") ?? "fabric.application.json";
|
|
@@ -73,6 +101,75 @@ async function runFabricGenCommand(args) {
|
|
|
73
101
|
});
|
|
74
102
|
return `${args.includes("--check") ? "Checked" : "Generated"} ${result.files.length} files.`;
|
|
75
103
|
}
|
|
104
|
+
async function initializeExperience(options) {
|
|
105
|
+
if (!/^[a-z][a-z0-9-]*$/.test(options.application)) {
|
|
106
|
+
throw new Error("Application name must use lowercase letters, numbers, and hyphens.");
|
|
107
|
+
}
|
|
108
|
+
const functionName = options.application.split("-").map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`).join("");
|
|
109
|
+
const files = [
|
|
110
|
+
[
|
|
111
|
+
path.join(options.directory, "fabric.experience.ts"),
|
|
112
|
+
[
|
|
113
|
+
'import { createExperienceServerRuntime } from "@fabricorg/experience-runtime/server";',
|
|
114
|
+
'import { createExperienceGateway } from "@fabricorg/experience-gateway";',
|
|
115
|
+
"",
|
|
116
|
+
`export const create${functionName}ExperienceRuntime = (`,
|
|
117
|
+
" options: Parameters<typeof createExperienceServerRuntime>[0],",
|
|
118
|
+
") => createExperienceServerRuntime(options);",
|
|
119
|
+
"",
|
|
120
|
+
`export const create${functionName}ExperienceGateway = (`,
|
|
121
|
+
" options: Parameters<typeof createExperienceGateway>[0],",
|
|
122
|
+
") => createExperienceGateway(options);",
|
|
123
|
+
""
|
|
124
|
+
].join("\n")
|
|
125
|
+
],
|
|
126
|
+
[
|
|
127
|
+
path.join(options.directory, "fabric.component-pack.ts"),
|
|
128
|
+
[
|
|
129
|
+
'import { defineComponentPack } from "@fabricorg/component-pack";',
|
|
130
|
+
"",
|
|
131
|
+
`export const define${functionName}ComponentPack = (artifactDigest: string) =>`,
|
|
132
|
+
" defineComponentPack({",
|
|
133
|
+
` name: "${options.application}",`,
|
|
134
|
+
` namespace: "${options.application}",`,
|
|
135
|
+
' version: "0.1.0",',
|
|
136
|
+
" artifactDigest,",
|
|
137
|
+
' tokenContract: { name: "fabric-family", version: "1.0.0" },',
|
|
138
|
+
" components: {",
|
|
139
|
+
' Text: { module: "./components/text.js", roles: ["content"], slots: [] },',
|
|
140
|
+
" },",
|
|
141
|
+
" });",
|
|
142
|
+
""
|
|
143
|
+
].join("\n")
|
|
144
|
+
],
|
|
145
|
+
[
|
|
146
|
+
path.join(options.directory, "fabric.application.json"),
|
|
147
|
+
`${JSON.stringify({
|
|
148
|
+
formatVersion: 1,
|
|
149
|
+
application: options.application,
|
|
150
|
+
assembly: "./fabric.assembly.lock.json",
|
|
151
|
+
runtimeCapabilities: "./fabric.runtime.json",
|
|
152
|
+
releases: []
|
|
153
|
+
}, null, 2)}
|
|
154
|
+
`
|
|
155
|
+
]
|
|
156
|
+
];
|
|
157
|
+
await mkdir(options.directory, { recursive: true });
|
|
158
|
+
const created = [];
|
|
159
|
+
try {
|
|
160
|
+
for (const [file, content] of files) {
|
|
161
|
+
await writeFile(file, content, { encoding: "utf8", flag: "wx" });
|
|
162
|
+
created.push(file);
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
await Promise.all(created.map((file) => rm(file, { force: true })));
|
|
166
|
+
if (error.code === "EEXIST") {
|
|
167
|
+
throw new Error("Refusing to overwrite an existing experience integration file.");
|
|
168
|
+
}
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
return { created };
|
|
172
|
+
}
|
|
76
173
|
async function readJson(file) {
|
|
77
174
|
return JSON.parse(await readFile(file, "utf8"));
|
|
78
175
|
}
|
|
@@ -238,14 +335,79 @@ async function listFiles(directory, prefix = "") {
|
|
|
238
335
|
const nested = await Promise.all(entries.map((entry) => entry.isDirectory() ? listFiles(path.join(directory, entry.name), `${prefix}${entry.name}/`) : [`${prefix}${entry.name}`]));
|
|
239
336
|
return nested.flat().sort();
|
|
240
337
|
}
|
|
338
|
+
function assertExperiencePromotionConfig(value) {
|
|
339
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
340
|
+
throw new Error("Experience promotion configuration must be a JSON object.");
|
|
341
|
+
}
|
|
342
|
+
const config = value;
|
|
343
|
+
if (config.formatVersion !== 1) throw new Error("Experience promotion configuration formatVersion must be 1.");
|
|
344
|
+
for (const field of ["application", "issuer", "keyId", "assembly", "release"]) {
|
|
345
|
+
if (typeof config[field] !== "string" || !config[field]) {
|
|
346
|
+
throw new Error(`Experience promotion configuration ${field} must be a non-empty string.`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (!/^[a-z][a-z0-9-]*$/.test(config.application)) {
|
|
350
|
+
throw new Error("Experience promotion configuration application must be lowercase letters, numbers, and hyphens.");
|
|
351
|
+
}
|
|
352
|
+
if (!Array.isArray(config.contracts) || !config.contracts.every((entry) => typeof entry === "string" && entry)) {
|
|
353
|
+
throw new Error("Experience promotion configuration contracts must be an array of file paths.");
|
|
354
|
+
}
|
|
355
|
+
const known = /* @__PURE__ */ new Set(["formatVersion", "application", "issuer", "keyId", "assembly", "release", "contracts"]);
|
|
356
|
+
for (const field of Object.keys(config)) {
|
|
357
|
+
if (/key|secret|credential|token|pem|password/i.test(field) && field !== "keyId") {
|
|
358
|
+
throw new Error(`Experience promotion configuration must not carry "${field}"; pass the signing key with --key.`);
|
|
359
|
+
}
|
|
360
|
+
if (!known.has(field)) {
|
|
361
|
+
throw new Error(`Experience promotion configuration has an unknown field "${field}".`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
async function promoteExperience(options) {
|
|
366
|
+
const configValue = await readJson(options.configPath);
|
|
367
|
+
assertExperiencePromotionConfig(configValue);
|
|
368
|
+
const base = path.dirname(options.configPath);
|
|
369
|
+
const resolve = (file) => path.resolve(base, file);
|
|
370
|
+
const assembly = await readJson(resolve(configValue.assembly));
|
|
371
|
+
assertAssemblyLockfile(assembly);
|
|
372
|
+
const release = await readJson(resolve(configValue.release));
|
|
373
|
+
assertSduiReleaseIntegrity(release);
|
|
374
|
+
const contracts = await Promise.all(
|
|
375
|
+
configValue.contracts.map(async (file) => await readJson(resolve(file)))
|
|
376
|
+
);
|
|
377
|
+
const privateKey = createPrivateKey(await readFile(options.privateKeyPath, "utf8"));
|
|
378
|
+
const promoted = await promoteExperienceRelease({
|
|
379
|
+
application: configValue.application,
|
|
380
|
+
release,
|
|
381
|
+
contracts,
|
|
382
|
+
assembly,
|
|
383
|
+
issuer: configValue.issuer,
|
|
384
|
+
signer: createEs256ReleaseSigner({ keyId: configValue.keyId, privateKey })
|
|
385
|
+
});
|
|
386
|
+
const outputPath = options.outputPath ? path.resolve(options.outputPath) : resolve(`${configValue.application}.experience-release.jws`);
|
|
387
|
+
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
388
|
+
await writeFile(outputPath, `${promoted.compactJws}
|
|
389
|
+
`, "utf8");
|
|
390
|
+
return {
|
|
391
|
+
application: configValue.application,
|
|
392
|
+
releaseDigest: release.releaseDigest,
|
|
393
|
+
assemblyDigest: assembly.assemblyDigest,
|
|
394
|
+
routes: {
|
|
395
|
+
views: promoted.release.routes.views.length,
|
|
396
|
+
intents: promoted.release.routes.intents.length
|
|
397
|
+
},
|
|
398
|
+
outputPath
|
|
399
|
+
};
|
|
400
|
+
}
|
|
241
401
|
|
|
242
402
|
export {
|
|
243
403
|
GENERATOR_TARGETS,
|
|
244
404
|
FabricGenUsageError,
|
|
245
405
|
fabricGenCliExitCode,
|
|
246
406
|
runFabricGenCommand,
|
|
407
|
+
initializeExperience,
|
|
247
408
|
checkApplication,
|
|
248
409
|
initializeCapability,
|
|
249
|
-
generateFromManifest
|
|
410
|
+
generateFromManifest,
|
|
411
|
+
promoteExperience
|
|
250
412
|
};
|
|
251
|
-
//# sourceMappingURL=chunk-
|
|
413
|
+
//# sourceMappingURL=chunk-4QFNFERM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../index.ts"],"sourcesContent":["import { access, mkdir, readFile, readdir, rm, writeFile } from \"node:fs/promises\";\nimport { createPrivateKey } from \"node:crypto\";\nimport path from \"node:path\";\nimport { compileManifestBundle, runGenerator, type GeneratedFile, type GeneratorTarget } from \"@fabricorg/compiler\";\nimport { capabilityGenerator } from \"@fabricorg/gen-capability\";\nimport { docsGenerator } from \"@fabricorg/gen-docs\";\nimport { eventsGenerator } from \"@fabricorg/gen-events\";\nimport { typesGenerator } from \"@fabricorg/gen-types\";\nimport { prismaGenerator } from \"@fabricorg/gen-prisma\";\nimport { graphqlGenerator } from \"@fabricorg/gen-graphql\";\nimport { sduiGenerator } from \"@fabricorg/gen-sdui\";\nimport type { UsageContractDocument } from \"@fabricorg/gen-capability\";\nimport { createEs256ReleaseSigner, promoteExperienceRelease } from \"@fabricorg/sdui-composer\";\nimport { conformanceGenerator } from \"@fabricorg/gen-conformance\";\nimport {\n\tassertAssemblyLockfile,\n\tassertAssemblyRuntimeCompatible,\n\ttype AssemblyLockfile,\n\ttype AvailableCapability,\n} from \"@fabricorg/assembly\";\nimport {\n\tassertSduiReleaseIntegrity,\n\ttype SduiRelease,\n} from \"@fabricorg/sdui-release\";\n\nexport const GENERATOR_TARGETS: Record<string, GeneratorTarget> = {\n\ttypes: typesGenerator,\n\tevents: eventsGenerator,\n\tcapability: capabilityGenerator,\n\tdocs: docsGenerator,\n\tprisma: prismaGenerator,\n\tgraphql: graphqlGenerator,\n\tsdui: sduiGenerator,\n\tconformance: conformanceGenerator,\n};\nconst compareCanonicalStrings = (left: string, right: string): number => left < right ? -1 : left > right ? 1 : 0;\n\nexport interface GenerateOptions { inputPath: string; outputDirectory: string; targets: string[]; check?: boolean; }\nexport interface GenerateResult { changed: string[]; stale: string[]; files: GeneratedFile[]; }\nexport interface InitializeCapabilityOptions { directory: string; namespace: string; }\nexport interface InitializeCapabilityResult { created: string[]; }\nexport interface InitializeExperienceOptions { directory: string; application: string; }\nexport interface InitializeExperienceResult { created: string[]; }\nexport interface CheckApplicationOptions { configPath: string; }\nexport interface PromoteExperienceOptions {\n\tconfigPath: string;\n\t/** PEM-encoded ES256 private key. Never read from the configuration file. */\n\tprivateKeyPath: string;\n\toutputPath?: string;\n}\nexport interface PromoteExperienceResult {\n\tapplication: string;\n\treleaseDigest: string;\n\tassemblyDigest: string;\n\troutes: { views: number; intents: number };\n\toutputPath: string;\n}\nexport interface CheckApplicationResult {\n\tapplication: string;\n\tcapabilities: number;\n\treleases: number;\n\tassemblyDigest: string;\n}\n\nconst usage = [\n\t\"Usage:\",\n\t\" fabric-gen init capability <namespace> [--directory <path>]\",\n\t\" fabric-gen init experience <application> [--directory <path>]\",\n\t\" fabric-gen check-application [--config <path>]\",\n\t\" fabric-gen promote-experience --config <path> --key <pem> [--out <path>]\",\n\t\" fabric-gen --input <manifest.json> --out <directory> [--targets <list>] [--check]\",\n].join(\"\\n\");\n\nexport class FabricGenUsageError extends Error {\n\toverride readonly name = \"FabricGenUsageError\";\n}\n\nexport function fabricGenCliExitCode(error: unknown): number {\n\treturn error instanceof FabricGenUsageError ? 2 : 1;\n}\n\nfunction flagValue(args: readonly string[], flag: string): string | undefined {\n\tconst index = args.indexOf(flag);\n\tif (index < 0) return undefined;\n\tconst value = args[index + 1];\n\tif (!value || value.startsWith(\"--\")) throw new FabricGenUsageError(usage);\n\treturn value;\n}\n\n/** Execute one CLI command and return its single human-readable result line. */\nexport async function runFabricGenCommand(args: readonly string[]): Promise<string> {\n\tif (args[0] === \"init\") {\n\t\tif (!args[2] || args[2].startsWith(\"--\")) {\n\t\t\tthrow new FabricGenUsageError(usage);\n\t\t}\n\t\tconst name = args[2];\n\t\tconst directory = flagValue(args, \"--directory\") ?? name;\n\t\tif (args[1] === \"capability\") {\n\t\t\tconst result = await initializeCapability({ directory, namespace: name });\n\t\t\treturn `Initialized capability \"${name}\" with ${result.created.length} files.`;\n\t\t}\n\t\tif (args[1] === \"experience\") {\n\t\t\tconst result = await initializeExperience({ directory, application: name });\n\t\t\treturn `Initialized experience integration \"${name}\" with ${result.created.length} files.`;\n\t\t}\n\t\tthrow new FabricGenUsageError(usage);\n\t}\n\tif (args[0] === \"promote-experience\") {\n\t\tconst configPath = flagValue(args, \"--config\");\n\t\tconst privateKeyPath = flagValue(args, \"--key\");\n\t\tif (!configPath) throw new FabricGenUsageError(\"promote-experience requires --config.\");\n\t\tif (!privateKeyPath) throw new FabricGenUsageError(\"promote-experience requires --key.\");\n\t\tconst outputPath = flagValue(args, \"--out\");\n\t\tconst result = await promoteExperience({\n\t\t\tconfigPath,\n\t\t\tprivateKeyPath,\n\t\t\t...(outputPath ? { outputPath } : {}),\n\t\t});\n\t\treturn [\n\t\t\t`Promoted ${result.application} release ${result.releaseDigest.slice(0, 12)} against assembly ${result.assemblyDigest.slice(0, 12)}.`,\n\t\t\t`Routes: ${result.routes.views} views, ${result.routes.intents} intents.`,\n\t\t\t`Wrote ${result.outputPath}`,\n\t\t].join(\"\\n\");\n\t}\n\n\tif (args[0] === \"check-application\") {\n\t\tconst configPath = flagValue(args, \"--config\") ?? \"fabric.application.json\";\n\t\tconst result = await checkApplication({ configPath });\n\t\treturn `Checked \"${result.application}\": ${result.capabilities} capabilities, ${result.releases} releases.`;\n\t}\n\tconst inputPath = flagValue(args, \"--input\");\n\tconst outputDirectory = flagValue(args, \"--out\");\n\tif (!inputPath || !outputDirectory) throw new FabricGenUsageError(usage);\n\tconst result = await generateFromManifest({\n\t\tinputPath,\n\t\toutputDirectory,\n\t\ttargets: (flagValue(args, \"--targets\") ?? \"types,events,capability,docs\")\n\t\t\t.split(\",\")\n\t\t\t.filter(Boolean),\n\t\tcheck: args.includes(\"--check\"),\n\t});\n\treturn `${args.includes(\"--check\") ? \"Checked\" : \"Generated\"} ${result.files.length} files.`;\n}\n\n/**\n * Add experience integration files to an existing application. It deliberately\n * does not create package.json, routes, authentication, billing, or navigation:\n * those remain owned by the Supastarter-derived application shell.\n */\nexport async function initializeExperience(\n\toptions: InitializeExperienceOptions,\n): Promise<InitializeExperienceResult> {\n\tif (!/^[a-z][a-z0-9-]*$/.test(options.application)) {\n\t\tthrow new Error(\"Application name must use lowercase letters, numbers, and hyphens.\");\n\t}\n\tconst functionName = options.application\n\t\t.split(\"-\")\n\t\t.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)\n\t\t.join(\"\");\n\tconst files = [\n\t\t[\n\t\t\tpath.join(options.directory, \"fabric.experience.ts\"),\n\t\t\t[\n\t\t\t\t'import { createExperienceServerRuntime } from \"@fabricorg/experience-runtime/server\";',\n\t\t\t\t'import { createExperienceGateway } from \"@fabricorg/experience-gateway\";',\n\t\t\t\t\"\",\n\t\t\t\t`export const create${functionName}ExperienceRuntime = (`,\n\t\t\t\t\"\\toptions: Parameters<typeof createExperienceServerRuntime>[0],\",\n\t\t\t\t\") => createExperienceServerRuntime(options);\",\n\t\t\t\t\"\",\n\t\t\t\t`export const create${functionName}ExperienceGateway = (`,\n\t\t\t\t\"\\toptions: Parameters<typeof createExperienceGateway>[0],\",\n\t\t\t\t\") => createExperienceGateway(options);\",\n\t\t\t\t\"\",\n\t\t\t].join(\"\\n\"),\n\t\t],\n\t\t[\n\t\t\tpath.join(options.directory, \"fabric.component-pack.ts\"),\n\t\t\t[\n\t\t\t\t'import { defineComponentPack } from \"@fabricorg/component-pack\";',\n\t\t\t\t\"\",\n\t\t\t\t`export const define${functionName}ComponentPack = (artifactDigest: string) =>`,\n\t\t\t\t\"\\tdefineComponentPack({\",\n\t\t\t\t`\\t\\tname: \"${options.application}\",`,\n\t\t\t\t`\\t\\tnamespace: \"${options.application}\",`,\n\t\t\t\t'\\t\\tversion: \"0.1.0\",',\n\t\t\t\t\"\\t\\tartifactDigest,\",\n\t\t\t\t'\\t\\ttokenContract: { name: \"fabric-family\", version: \"1.0.0\" },',\n\t\t\t\t\"\\t\\tcomponents: {\",\n\t\t\t\t'\\t\\t\\tText: { module: \"./components/text.js\", roles: [\"content\"], slots: [] },',\n\t\t\t\t\"\\t\\t},\",\n\t\t\t\t\"\\t});\",\n\t\t\t\t\"\",\n\t\t\t].join(\"\\n\"),\n\t\t],\n\t\t[\n\t\t\tpath.join(options.directory, \"fabric.application.json\"),\n\t\t\t`${JSON.stringify({\n\t\t\t\tformatVersion: 1,\n\t\t\t\tapplication: options.application,\n\t\t\t\tassembly: \"./fabric.assembly.lock.json\",\n\t\t\t\truntimeCapabilities: \"./fabric.runtime.json\",\n\t\t\t\treleases: [],\n\t\t\t}, null, 2)}\\n`,\n\t\t],\n\t] as const;\n\tawait mkdir(options.directory, { recursive: true });\n\tconst created: string[] = [];\n\ttry {\n\t\tfor (const [file, content] of files) {\n\t\t\tawait writeFile(file, content, { encoding: \"utf8\", flag: \"wx\" });\n\t\t\tcreated.push(file);\n\t\t}\n\t} catch (error) {\n\t\tawait Promise.all(created.map((file) => rm(file, { force: true })));\n\t\tif ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n\t\t\tthrow new Error(\"Refusing to overwrite an existing experience integration file.\");\n\t\t}\n\t\tthrow error;\n\t}\n\treturn { created };\n}\n\ninterface ApplicationCheckConfig {\n\tformatVersion: 1;\n\tassembly: string;\n\truntimeCapabilities: string;\n\treleases: string[];\n}\n\nasync function readJson(file: string): Promise<unknown> {\n\treturn JSON.parse(await readFile(file, \"utf8\"));\n}\n\nfunction assertApplicationCheckConfig(value: unknown): asserts value is ApplicationCheckConfig {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\tthrow new Error(\"Application check configuration must be a JSON object.\");\n\t}\n\tconst config = value as Record<string, unknown>;\n\tif (config.formatVersion !== 1) throw new Error(\"Application check configuration formatVersion must be 1.\");\n\tif (typeof config.assembly !== \"string\" || !config.assembly) {\n\t\tthrow new Error(\"Application check configuration assembly must be a file path.\");\n\t}\n\tif (typeof config.runtimeCapabilities !== \"string\" || !config.runtimeCapabilities) {\n\t\tthrow new Error(\"Application check configuration runtimeCapabilities must be a file path.\");\n\t}\n\tif (!Array.isArray(config.releases) || !config.releases.every((entry) => typeof entry === \"string\" && entry)) {\n\t\tthrow new Error(\"Application check configuration releases must be an array of file paths.\");\n\t}\n}\n\n/**\n * Verify an application's immutable composition inputs before deployment.\n * Every runtime capability must exactly match the approved lockfile, and every\n * promoted experience release must be intact and bound to that assembly.\n */\nexport async function checkApplication(\n\toptions: CheckApplicationOptions,\n): Promise<CheckApplicationResult> {\n\tconst configValue = await readJson(options.configPath);\n\tassertApplicationCheckConfig(configValue);\n\tconst base = path.dirname(options.configPath);\n\tconst resolve = (file: string): string => path.resolve(base, file);\n\tconst assembly = await readJson(resolve(configValue.assembly)) as AssemblyLockfile;\n\tassertAssemblyLockfile(assembly);\n\tconst runtimeCapabilities = await readJson(\n\t\tresolve(configValue.runtimeCapabilities),\n\t) as AvailableCapability[];\n\tif (!Array.isArray(runtimeCapabilities)) {\n\t\tthrow new Error(\"Runtime capabilities must be a JSON array.\");\n\t}\n\tassertAssemblyRuntimeCompatible(assembly, runtimeCapabilities);\n\tfor (const releasePath of configValue.releases) {\n\t\tconst release = await readJson(resolve(releasePath)) as SduiRelease;\n\t\ttry {\n\t\t\tassertSduiReleaseIntegrity(release);\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Experience release \"${releasePath}\" is invalid: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t\tif (release.assemblyDigest !== assembly.assemblyDigest) {\n\t\t\tthrow new Error(\n\t\t\t\t`Experience release \"${release.document.name}@${release.document.version}\" belongs to assembly \"${release.assemblyDigest}\", not \"${assembly.assemblyDigest}\".`,\n\t\t\t);\n\t\t}\n\t}\n\treturn {\n\t\tapplication: assembly.application,\n\t\tcapabilities: assembly.capabilities.length,\n\t\treleases: configValue.releases.length,\n\t\tassemblyDigest: assembly.assemblyDigest,\n\t};\n}\n\n/**\n * Create the smallest useful capability source tree. The manifest is accepted\n * by the compiler immediately, while leaving domain vocabulary to the adopter.\n */\nexport async function initializeCapability(\n\toptions: InitializeCapabilityOptions,\n): Promise<InitializeCapabilityResult> {\n\tif (!/^[a-z][a-z0-9-]*$/.test(options.namespace)) {\n\t\tthrow new Error(\"Capability namespace must use lowercase letters, numbers, and hyphens.\");\n\t}\n\tconst displayName = options.namespace\n\t\t.split(\"-\")\n\t\t.map((part, index) => index === 0\n\t\t\t? `${part.charAt(0).toUpperCase()}${part.slice(1)}`\n\t\t\t: part)\n\t\t.join(\" \");\n\tconst manifestPath = path.join(options.directory, \"fabric.manifest.json\");\n\tconst readmePath = path.join(options.directory, \"README.md\");\n\tfor (const file of [readmePath, manifestPath]) {\n\t\ttry {\n\t\t\tawait access(file);\n\t\t\tthrow new Error(`Refusing to overwrite existing file \"${file}\".`);\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error && error.message.startsWith(\"Refusing to overwrite\")) throw error;\n\t\t\tif ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t\t}\n\t}\n\tconst manifest = {\n\t\tmanifestVersion: 2,\n\t\tnamespace: options.namespace,\n\t\tobjectTypes: [],\n\t\tsubjectTypes: [],\n\t\tidPrefixes: [],\n\t\tdependencies: [],\n\t\tpolicies: [],\n\t\teventTypes: [],\n\t\tactions: [],\n\t\tviews: [],\n\t\tstateMachines: [],\n\t\tcapability: {\n\t\t\tdisplayName,\n\t\t\tdescription: `Governed ${displayName.toLowerCase()} capability.`,\n\t\t\tstability: \"experimental\",\n\t\t},\n\t};\n\tconst readme = [\n\t\t`# ${displayName}`,\n\t\t\"\",\n\t\t\"Define domain objects, governed actions, events, and projections in `fabric.manifest.json`.\",\n\t\t\"\",\n\t\t\"```bash\",\n\t\t\"fabric-gen --input fabric.manifest.json --out generated\",\n\t\t\"fabric-gen --input fabric.manifest.json --out generated --check\",\n\t\t\"```\",\n\t\t\"\",\n\t].join(\"\\n\");\n\tawait mkdir(options.directory, { recursive: true });\n\tconst created: string[] = [];\n\ttry {\n\t\tfor (const [file, content] of [\n\t\t\t[readmePath, readme],\n\t\t\t[manifestPath, `${JSON.stringify(manifest, null, 2)}\\n`],\n\t\t] as const) {\n\t\t\tawait writeFile(file, content, { encoding: \"utf8\", flag: \"wx\" });\n\t\t\tcreated.push(file);\n\t\t}\n\t} catch (error) {\n\t\tawait Promise.all(created.map((file) => rm(file, { force: true })));\n\t\tif ((error as NodeJS.ErrnoException).code === \"EEXIST\") {\n\t\t\tthrow new Error(\"Refusing to overwrite a file created during capability initialization.\");\n\t\t}\n\t\tthrow error;\n\t}\n\treturn { created };\n}\n\nexport async function generateFromManifest(options: GenerateOptions): Promise<GenerateResult> {\n\tconst input = JSON.parse(await readFile(options.inputPath, \"utf8\"));\n\tif (!input || typeof input !== \"object\" || Array.isArray(input)) throw new Error(\"Manifest input must be a JSON object.\");\n\tconst bundle = \"modules\" in input ? input : { manifestVersion: input.manifestVersion, modules: [input] };\n\tconst ir = compileManifestBundle(bundle);\n\tconst files = (await Promise.all(options.targets.map(async (name) => {\n\t\tconst target = GENERATOR_TARGETS[name];\n\t\tif (!target) throw new Error(`Unknown generator target \"${name}\".`);\n\t\treturn (await runGenerator(target, ir)).map((file) => ({ ...file, path: `${name}/${file.path}` }));\n\t}))).flat().sort((left, right) => compareCanonicalStrings(left.path, right.path));\n\tconst expected = new Set(files.map((file) => file.path));\n\tconst existing = await listFiles(options.outputDirectory);\n\tconst selectedPrefixes = options.targets.map((target) => `${target}/`);\n\tconst stale = existing.filter((file) => selectedPrefixes.some((prefix) => file.startsWith(prefix)) && !expected.has(file));\n\tif (!options.check) await Promise.all(stale.map((file) => rm(path.join(options.outputDirectory, file), { force: true })));\n\tconst changed: string[] = [];\n\tfor (const file of files) {\n\t\tconst target = path.join(options.outputDirectory, file.path);\n\t\tlet current: string | undefined;\n\t\ttry { current = await readFile(target, \"utf8\"); } catch { current = undefined; }\n\t\tif (current !== file.content) {\n\t\t\tchanged.push(file.path);\n\t\t\tif (!options.check) { await mkdir(path.dirname(target), { recursive: true }); await writeFile(target, file.content, \"utf8\"); }\n\t\t}\n\t}\n\tif (options.check && (changed.length || stale.length)) throw new Error(`Generated artifacts drifted: ${[...changed, ...stale.map((file) => `stale:${file}`)].join(\", \")}`);\n\treturn { changed, stale, files };\n}\n\nasync function listFiles(directory: string, prefix = \"\"): Promise<string[]> {\n\tlet entries;\n\ttry { entries = await readdir(directory, { withFileTypes: true }); } catch { return []; }\n\tconst nested = await Promise.all(entries.map((entry) => entry.isDirectory() ? listFiles(path.join(directory, entry.name), `${prefix}${entry.name}/`) : [`${prefix}${entry.name}`]));\n\treturn nested.flat().sort();\n}\n\ninterface ExperiencePromotionConfig {\n\tformatVersion: 1;\n\tapplication: string;\n\tissuer: string;\n\tkeyId: string;\n\tassembly: string;\n\trelease: string;\n\tcontracts: string[];\n}\n\nfunction assertExperiencePromotionConfig(value: unknown): asserts value is ExperiencePromotionConfig {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\tthrow new Error(\"Experience promotion configuration must be a JSON object.\");\n\t}\n\tconst config = value as Record<string, unknown>;\n\tif (config.formatVersion !== 1) throw new Error(\"Experience promotion configuration formatVersion must be 1.\");\n\tfor (const field of [\"application\", \"issuer\", \"keyId\", \"assembly\", \"release\"] as const) {\n\t\tif (typeof config[field] !== \"string\" || !config[field]) {\n\t\t\tthrow new Error(`Experience promotion configuration ${field} must be a non-empty string.`);\n\t\t}\n\t}\n\t// The application name is interpolated into the default output path and\n\t// signed into the release body, so \"../../x\" would escape the configuration\n\t// directory and \"/etc/x\" would ignore it entirely.\n\tif (!/^[a-z][a-z0-9-]*$/.test(config.application as string)) {\n\t\tthrow new Error(\"Experience promotion configuration application must be lowercase letters, numbers, and hyphens.\");\n\t}\n\tif (!Array.isArray(config.contracts) || !config.contracts.every((entry) => typeof entry === \"string\" && entry)) {\n\t\tthrow new Error(\"Experience promotion configuration contracts must be an array of file paths.\");\n\t}\n\t// A signing key in a checked-in configuration file is a key in version\n\t// control. Enumerating two spellings only catches two spellings, so reject\n\t// anything that reads like one, and reject unknown fields so a future\n\t// spelling cannot arrive unnoticed.\n\tconst known = new Set([\"formatVersion\", \"application\", \"issuer\", \"keyId\", \"assembly\", \"release\", \"contracts\"]);\n\tfor (const field of Object.keys(config)) {\n\t\tif (/key|secret|credential|token|pem|password/i.test(field) && field !== \"keyId\") {\n\t\t\tthrow new Error(`Experience promotion configuration must not carry \"${field}\"; pass the signing key with --key.`);\n\t\t}\n\t\tif (!known.has(field)) {\n\t\t\tthrow new Error(`Experience promotion configuration has an unknown field \"${field}\".`);\n\t\t}\n\t}\n}\n\n/**\n * Turn a validated release into a signed one.\n *\n * Promotion is where a granted `capability://` reference becomes a concrete\n * action id and version, so it is also where that mapping has to be bound to\n * the assembly that approved it. The signing key is a command-line argument\n * rather than a configuration field, because a configuration file is the kind\n * of thing that ends up committed.\n */\nexport async function promoteExperience(\n\toptions: PromoteExperienceOptions,\n): Promise<PromoteExperienceResult> {\n\tconst configValue = await readJson(options.configPath);\n\tassertExperiencePromotionConfig(configValue);\n\tconst base = path.dirname(options.configPath);\n\tconst resolve = (file: string): string => path.resolve(base, file);\n\n\tconst assembly = await readJson(resolve(configValue.assembly)) as AssemblyLockfile;\n\tassertAssemblyLockfile(assembly);\n\tconst release = await readJson(resolve(configValue.release)) as SduiRelease;\n\tassertSduiReleaseIntegrity(release);\n\tconst contracts = await Promise.all(\n\t\tconfigValue.contracts.map(async (file) => await readJson(resolve(file)) as UsageContractDocument),\n\t);\n\n\tconst privateKey = createPrivateKey(await readFile(options.privateKeyPath, \"utf8\"));\n\tconst promoted = await promoteExperienceRelease({\n\t\tapplication: configValue.application,\n\t\trelease,\n\t\tcontracts,\n\t\tassembly,\n\t\tissuer: configValue.issuer,\n\t\tsigner: createEs256ReleaseSigner({ keyId: configValue.keyId, privateKey }),\n\t});\n\n\tconst outputPath = options.outputPath\n\t\t? path.resolve(options.outputPath)\n\t\t: resolve(`${configValue.application}.experience-release.jws`);\n\tawait mkdir(path.dirname(outputPath), { recursive: true });\n\tawait writeFile(outputPath, `${promoted.compactJws}\\n`, \"utf8\");\n\n\treturn {\n\t\tapplication: configValue.application,\n\t\treleaseDigest: release.releaseDigest,\n\t\tassemblyDigest: assembly.assemblyDigest,\n\t\troutes: {\n\t\t\tviews: promoted.release.routes.views.length,\n\t\t\tintents: promoted.release.routes.intents.length,\n\t\t},\n\t\toutputPath,\n\t};\n}\n"],"mappings":"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","names":["result"]}
|
package/dist/cli.cjs
CHANGED
|
@@ -25,6 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
|
|
26
26
|
// index.ts
|
|
27
27
|
var import_promises = require("fs/promises");
|
|
28
|
+
var import_node_crypto = require("crypto");
|
|
28
29
|
var import_node_path = __toESM(require("path"), 1);
|
|
29
30
|
var import_compiler = require("@fabricorg/compiler");
|
|
30
31
|
var import_gen_capability = require("@fabricorg/gen-capability");
|
|
@@ -34,6 +35,7 @@ var import_gen_types = require("@fabricorg/gen-types");
|
|
|
34
35
|
var import_gen_prisma = require("@fabricorg/gen-prisma");
|
|
35
36
|
var import_gen_graphql = require("@fabricorg/gen-graphql");
|
|
36
37
|
var import_gen_sdui = require("@fabricorg/gen-sdui");
|
|
38
|
+
var import_sdui_composer = require("@fabricorg/sdui-composer");
|
|
37
39
|
var import_gen_conformance = require("@fabricorg/gen-conformance");
|
|
38
40
|
var import_assembly = require("@fabricorg/assembly");
|
|
39
41
|
var import_sdui_release = require("@fabricorg/sdui-release");
|
|
@@ -51,7 +53,9 @@ var compareCanonicalStrings = (left, right) => left < right ? -1 : left > right
|
|
|
51
53
|
var usage = [
|
|
52
54
|
"Usage:",
|
|
53
55
|
" fabric-gen init capability <namespace> [--directory <path>]",
|
|
56
|
+
" fabric-gen init experience <application> [--directory <path>]",
|
|
54
57
|
" fabric-gen check-application [--config <path>]",
|
|
58
|
+
" fabric-gen promote-experience --config <path> --key <pem> [--out <path>]",
|
|
55
59
|
" fabric-gen --input <manifest.json> --out <directory> [--targets <list>] [--check]"
|
|
56
60
|
].join("\n");
|
|
57
61
|
var FabricGenUsageError = class extends Error {
|
|
@@ -69,13 +73,37 @@ function flagValue(args2, flag) {
|
|
|
69
73
|
}
|
|
70
74
|
async function runFabricGenCommand(args2) {
|
|
71
75
|
if (args2[0] === "init") {
|
|
72
|
-
if (
|
|
76
|
+
if (!args2[2] || args2[2].startsWith("--")) {
|
|
73
77
|
throw new FabricGenUsageError(usage);
|
|
74
78
|
}
|
|
75
|
-
const
|
|
76
|
-
const directory = flagValue(args2, "--directory") ??
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
const name = args2[2];
|
|
80
|
+
const directory = flagValue(args2, "--directory") ?? name;
|
|
81
|
+
if (args2[1] === "capability") {
|
|
82
|
+
const result2 = await initializeCapability({ directory, namespace: name });
|
|
83
|
+
return `Initialized capability "${name}" with ${result2.created.length} files.`;
|
|
84
|
+
}
|
|
85
|
+
if (args2[1] === "experience") {
|
|
86
|
+
const result2 = await initializeExperience({ directory, application: name });
|
|
87
|
+
return `Initialized experience integration "${name}" with ${result2.created.length} files.`;
|
|
88
|
+
}
|
|
89
|
+
throw new FabricGenUsageError(usage);
|
|
90
|
+
}
|
|
91
|
+
if (args2[0] === "promote-experience") {
|
|
92
|
+
const configPath = flagValue(args2, "--config");
|
|
93
|
+
const privateKeyPath = flagValue(args2, "--key");
|
|
94
|
+
if (!configPath) throw new FabricGenUsageError("promote-experience requires --config.");
|
|
95
|
+
if (!privateKeyPath) throw new FabricGenUsageError("promote-experience requires --key.");
|
|
96
|
+
const outputPath = flagValue(args2, "--out");
|
|
97
|
+
const result2 = await promoteExperience({
|
|
98
|
+
configPath,
|
|
99
|
+
privateKeyPath,
|
|
100
|
+
...outputPath ? { outputPath } : {}
|
|
101
|
+
});
|
|
102
|
+
return [
|
|
103
|
+
`Promoted ${result2.application} release ${result2.releaseDigest.slice(0, 12)} against assembly ${result2.assemblyDigest.slice(0, 12)}.`,
|
|
104
|
+
`Routes: ${result2.routes.views} views, ${result2.routes.intents} intents.`,
|
|
105
|
+
`Wrote ${result2.outputPath}`
|
|
106
|
+
].join("\n");
|
|
79
107
|
}
|
|
80
108
|
if (args2[0] === "check-application") {
|
|
81
109
|
const configPath = flagValue(args2, "--config") ?? "fabric.application.json";
|
|
@@ -93,6 +121,75 @@ async function runFabricGenCommand(args2) {
|
|
|
93
121
|
});
|
|
94
122
|
return `${args2.includes("--check") ? "Checked" : "Generated"} ${result.files.length} files.`;
|
|
95
123
|
}
|
|
124
|
+
async function initializeExperience(options) {
|
|
125
|
+
if (!/^[a-z][a-z0-9-]*$/.test(options.application)) {
|
|
126
|
+
throw new Error("Application name must use lowercase letters, numbers, and hyphens.");
|
|
127
|
+
}
|
|
128
|
+
const functionName = options.application.split("-").map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`).join("");
|
|
129
|
+
const files = [
|
|
130
|
+
[
|
|
131
|
+
import_node_path.default.join(options.directory, "fabric.experience.ts"),
|
|
132
|
+
[
|
|
133
|
+
'import { createExperienceServerRuntime } from "@fabricorg/experience-runtime/server";',
|
|
134
|
+
'import { createExperienceGateway } from "@fabricorg/experience-gateway";',
|
|
135
|
+
"",
|
|
136
|
+
`export const create${functionName}ExperienceRuntime = (`,
|
|
137
|
+
" options: Parameters<typeof createExperienceServerRuntime>[0],",
|
|
138
|
+
") => createExperienceServerRuntime(options);",
|
|
139
|
+
"",
|
|
140
|
+
`export const create${functionName}ExperienceGateway = (`,
|
|
141
|
+
" options: Parameters<typeof createExperienceGateway>[0],",
|
|
142
|
+
") => createExperienceGateway(options);",
|
|
143
|
+
""
|
|
144
|
+
].join("\n")
|
|
145
|
+
],
|
|
146
|
+
[
|
|
147
|
+
import_node_path.default.join(options.directory, "fabric.component-pack.ts"),
|
|
148
|
+
[
|
|
149
|
+
'import { defineComponentPack } from "@fabricorg/component-pack";',
|
|
150
|
+
"",
|
|
151
|
+
`export const define${functionName}ComponentPack = (artifactDigest: string) =>`,
|
|
152
|
+
" defineComponentPack({",
|
|
153
|
+
` name: "${options.application}",`,
|
|
154
|
+
` namespace: "${options.application}",`,
|
|
155
|
+
' version: "0.1.0",',
|
|
156
|
+
" artifactDigest,",
|
|
157
|
+
' tokenContract: { name: "fabric-family", version: "1.0.0" },',
|
|
158
|
+
" components: {",
|
|
159
|
+
' Text: { module: "./components/text.js", roles: ["content"], slots: [] },',
|
|
160
|
+
" },",
|
|
161
|
+
" });",
|
|
162
|
+
""
|
|
163
|
+
].join("\n")
|
|
164
|
+
],
|
|
165
|
+
[
|
|
166
|
+
import_node_path.default.join(options.directory, "fabric.application.json"),
|
|
167
|
+
`${JSON.stringify({
|
|
168
|
+
formatVersion: 1,
|
|
169
|
+
application: options.application,
|
|
170
|
+
assembly: "./fabric.assembly.lock.json",
|
|
171
|
+
runtimeCapabilities: "./fabric.runtime.json",
|
|
172
|
+
releases: []
|
|
173
|
+
}, null, 2)}
|
|
174
|
+
`
|
|
175
|
+
]
|
|
176
|
+
];
|
|
177
|
+
await (0, import_promises.mkdir)(options.directory, { recursive: true });
|
|
178
|
+
const created = [];
|
|
179
|
+
try {
|
|
180
|
+
for (const [file, content] of files) {
|
|
181
|
+
await (0, import_promises.writeFile)(file, content, { encoding: "utf8", flag: "wx" });
|
|
182
|
+
created.push(file);
|
|
183
|
+
}
|
|
184
|
+
} catch (error) {
|
|
185
|
+
await Promise.all(created.map((file) => (0, import_promises.rm)(file, { force: true })));
|
|
186
|
+
if (error.code === "EEXIST") {
|
|
187
|
+
throw new Error("Refusing to overwrite an existing experience integration file.");
|
|
188
|
+
}
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
return { created };
|
|
192
|
+
}
|
|
96
193
|
async function readJson(file) {
|
|
97
194
|
return JSON.parse(await (0, import_promises.readFile)(file, "utf8"));
|
|
98
195
|
}
|
|
@@ -258,6 +355,69 @@ async function listFiles(directory, prefix = "") {
|
|
|
258
355
|
const nested = await Promise.all(entries.map((entry) => entry.isDirectory() ? listFiles(import_node_path.default.join(directory, entry.name), `${prefix}${entry.name}/`) : [`${prefix}${entry.name}`]));
|
|
259
356
|
return nested.flat().sort();
|
|
260
357
|
}
|
|
358
|
+
function assertExperiencePromotionConfig(value) {
|
|
359
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
360
|
+
throw new Error("Experience promotion configuration must be a JSON object.");
|
|
361
|
+
}
|
|
362
|
+
const config = value;
|
|
363
|
+
if (config.formatVersion !== 1) throw new Error("Experience promotion configuration formatVersion must be 1.");
|
|
364
|
+
for (const field of ["application", "issuer", "keyId", "assembly", "release"]) {
|
|
365
|
+
if (typeof config[field] !== "string" || !config[field]) {
|
|
366
|
+
throw new Error(`Experience promotion configuration ${field} must be a non-empty string.`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (!/^[a-z][a-z0-9-]*$/.test(config.application)) {
|
|
370
|
+
throw new Error("Experience promotion configuration application must be lowercase letters, numbers, and hyphens.");
|
|
371
|
+
}
|
|
372
|
+
if (!Array.isArray(config.contracts) || !config.contracts.every((entry) => typeof entry === "string" && entry)) {
|
|
373
|
+
throw new Error("Experience promotion configuration contracts must be an array of file paths.");
|
|
374
|
+
}
|
|
375
|
+
const known = /* @__PURE__ */ new Set(["formatVersion", "application", "issuer", "keyId", "assembly", "release", "contracts"]);
|
|
376
|
+
for (const field of Object.keys(config)) {
|
|
377
|
+
if (/key|secret|credential|token|pem|password/i.test(field) && field !== "keyId") {
|
|
378
|
+
throw new Error(`Experience promotion configuration must not carry "${field}"; pass the signing key with --key.`);
|
|
379
|
+
}
|
|
380
|
+
if (!known.has(field)) {
|
|
381
|
+
throw new Error(`Experience promotion configuration has an unknown field "${field}".`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
async function promoteExperience(options) {
|
|
386
|
+
const configValue = await readJson(options.configPath);
|
|
387
|
+
assertExperiencePromotionConfig(configValue);
|
|
388
|
+
const base = import_node_path.default.dirname(options.configPath);
|
|
389
|
+
const resolve = (file) => import_node_path.default.resolve(base, file);
|
|
390
|
+
const assembly = await readJson(resolve(configValue.assembly));
|
|
391
|
+
(0, import_assembly.assertAssemblyLockfile)(assembly);
|
|
392
|
+
const release = await readJson(resolve(configValue.release));
|
|
393
|
+
(0, import_sdui_release.assertSduiReleaseIntegrity)(release);
|
|
394
|
+
const contracts = await Promise.all(
|
|
395
|
+
configValue.contracts.map(async (file) => await readJson(resolve(file)))
|
|
396
|
+
);
|
|
397
|
+
const privateKey = (0, import_node_crypto.createPrivateKey)(await (0, import_promises.readFile)(options.privateKeyPath, "utf8"));
|
|
398
|
+
const promoted = await (0, import_sdui_composer.promoteExperienceRelease)({
|
|
399
|
+
application: configValue.application,
|
|
400
|
+
release,
|
|
401
|
+
contracts,
|
|
402
|
+
assembly,
|
|
403
|
+
issuer: configValue.issuer,
|
|
404
|
+
signer: (0, import_sdui_composer.createEs256ReleaseSigner)({ keyId: configValue.keyId, privateKey })
|
|
405
|
+
});
|
|
406
|
+
const outputPath = options.outputPath ? import_node_path.default.resolve(options.outputPath) : resolve(`${configValue.application}.experience-release.jws`);
|
|
407
|
+
await (0, import_promises.mkdir)(import_node_path.default.dirname(outputPath), { recursive: true });
|
|
408
|
+
await (0, import_promises.writeFile)(outputPath, `${promoted.compactJws}
|
|
409
|
+
`, "utf8");
|
|
410
|
+
return {
|
|
411
|
+
application: configValue.application,
|
|
412
|
+
releaseDigest: release.releaseDigest,
|
|
413
|
+
assemblyDigest: assembly.assemblyDigest,
|
|
414
|
+
routes: {
|
|
415
|
+
views: promoted.release.routes.views.length,
|
|
416
|
+
intents: promoted.release.routes.intents.length
|
|
417
|
+
},
|
|
418
|
+
outputPath
|
|
419
|
+
};
|
|
420
|
+
}
|
|
261
421
|
|
|
262
422
|
// cli.ts
|
|
263
423
|
var args = process.argv.slice(2);
|