@canonical/pragma-cli 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -0
- package/dist/pragma +0 -0
- package/package.json +19 -15
- package/pragma.conf.ts +664 -0
- package/src/bin.ts +15 -6
- package/src/capabilities/block.parity.test.ts +250 -0
- package/src/capabilities/capabilities/capabilities.render.ts +3 -2
- package/src/capabilities/capabilities/capabilities.test.ts +10 -1
- package/src/capabilities/capabilities/capabilities.verb.ts +32 -14
- package/src/capabilities/capabilities/catalog.ts +20 -8
- package/src/capabilities/capabilities/hints.ts +21 -36
- package/src/capabilities/capabilities/types.ts +2 -0
- package/src/capabilities/colophon/collectColophon.ts +34 -19
- package/src/capabilities/colophon/colophon.render.test.ts +31 -4
- package/src/capabilities/colophon/colophon.render.ts +13 -4
- package/src/capabilities/colophon/colophon.test.ts +3 -3
- package/src/capabilities/colophon/colophon.verb.ts +11 -4
- package/src/capabilities/colophon/markdownTerminal.ts +33 -14
- package/src/capabilities/colophon/types.ts +16 -7
- package/src/capabilities/config/collectConfigShow.test.ts +57 -0
- package/src/capabilities/config/collectConfigShow.ts +41 -4
- package/src/capabilities/config/fields.ts +10 -108
- package/src/capabilities/config/set.test.ts +6 -6
- package/src/capabilities/config/set.verb.ts +12 -8
- package/src/capabilities/config/show.render.test.ts +137 -0
- package/src/capabilities/config/show.render.ts +57 -16
- package/src/capabilities/config/show.verb.ts +9 -8
- package/src/capabilities/config/types.ts +8 -0
- package/src/capabilities/create/byteEquality.subprocess.test.ts +1 -1
- package/src/capabilities/create/byteEquality.test.ts +82 -101
- package/src/capabilities/create/compiledCreate.subprocess.test.ts +53 -16
- package/src/capabilities/create/constants.ts +75 -0
- package/src/capabilities/create/create.mcp.test.ts +2 -2
- package/src/capabilities/create/create.test.ts +192 -16
- package/src/capabilities/create/create.verb.ts +208 -79
- package/src/capabilities/create/index.ts +2 -10
- package/src/capabilities/create/pickGenerator.ts +34 -28
- package/src/capabilities/create/templates.embedded.generated.ts +4 -29
- package/src/capabilities/create/types.ts +10 -6
- package/src/capabilities/{standard/parity.test.ts → distribution.parity.test.ts} +24 -14
- package/src/capabilities/distribution.test.ts +120 -0
- package/src/capabilities/distribution.ts +59 -0
- package/src/capabilities/doctor/checks/checkConfigFile.ts +6 -5
- package/src/capabilities/doctor/checks/checkKeStore.ts +2 -2
- package/src/capabilities/doctor/checks/checkMcpCommands.ts +6 -0
- package/src/capabilities/doctor/checks/checkMcpConfigured.ts +15 -8
- package/src/capabilities/doctor/checks/checkPackageRefs.ts +79 -45
- package/src/capabilities/doctor/checks/checkPragmaVersion.ts +3 -2
- package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +61 -11
- package/src/capabilities/doctor/checks/checkShellCompletions.ts +63 -23
- package/src/capabilities/doctor/checks/checkSkillsSymlinked.ts +3 -2
- package/src/capabilities/doctor/checks/deriveBand.test.ts +42 -0
- package/src/capabilities/doctor/checks/deriveBand.ts +28 -0
- package/src/capabilities/doctor/doctor.render.test.ts +170 -0
- package/src/capabilities/doctor/doctor.render.ts +117 -34
- package/src/capabilities/doctor/doctor.test.ts +68 -9
- package/src/capabilities/doctor/doctor.verb.ts +6 -2
- package/src/capabilities/doctor/runChecks.ts +33 -9
- package/src/capabilities/doctor/types.ts +16 -1
- package/src/capabilities/emptyRecovery.test.ts +32 -15
- package/src/capabilities/graph/inspect.verb.ts +3 -2
- package/src/capabilities/graph/query.verb.ts +3 -2
- package/src/capabilities/graph/runQuery.ts +8 -5
- package/src/capabilities/index.ts +22 -18
- package/src/capabilities/info/collectInfo.ts +10 -6
- package/src/capabilities/info/info.render.ts +3 -2
- package/src/capabilities/info/info.verb.ts +3 -2
- package/src/capabilities/lazy.test.ts +93 -2
- package/src/capabilities/ontology/index.ts +10 -3
- package/src/capabilities/ontology/ontology.test.ts +157 -5
- package/src/capabilities/ontology/verbs.ts +203 -104
- package/src/capabilities/prompt/prompt.render.test.ts +94 -0
- package/src/capabilities/prompt/prompt.render.ts +23 -6
- package/src/capabilities/prompt/prompt.test.ts +68 -1
- package/src/capabilities/prompt/verbs.ts +24 -11
- package/src/capabilities/reference.test.ts +32 -0
- package/src/capabilities/resources/provider.ts +58 -25
- package/src/capabilities/resources/resources.test.ts +176 -21
- package/src/capabilities/scopeTypeSync.test.ts +37 -0
- package/src/capabilities/setup/index.ts +7 -1
- package/src/capabilities/setup/operations/setupCompletions.ts +49 -16
- package/src/capabilities/setup/operations/setupGenerator.ts +166 -63
- package/src/capabilities/setup/operations/setupLsp.ts +56 -12
- package/src/capabilities/setup/operations/setupMcp.ts +216 -39
- package/src/capabilities/setup/operations/setupSkills.ts +10 -4
- package/src/capabilities/setup/setup.render.test.ts +109 -0
- package/src/capabilities/setup/setup.render.ts +61 -14
- package/src/capabilities/setup/setup.test.ts +352 -1
- package/src/capabilities/setup/setup.verb.ts +68 -9
- package/src/capabilities/setup/shell.ts +29 -3
- package/src/capabilities/setup/types.ts +59 -2
- package/src/capabilities/shared/bands.ts +16 -0
- package/src/capabilities/skill/discover.ts +11 -5
- package/src/capabilities/skill/render.ts +4 -5
- package/src/capabilities/skill/verbs.ts +7 -7
- package/src/capabilities/sources/collectStatus.ts +69 -49
- package/src/capabilities/sources/index.ts +1 -1
- package/src/capabilities/sources/installSkills.ts +2 -2
- package/src/capabilities/sources/runUpdate.ts +146 -98
- package/src/capabilities/sources/sources.test.ts +264 -84
- package/src/capabilities/sources/status.render.test.ts +94 -0
- package/src/capabilities/sources/status.render.ts +55 -33
- package/src/capabilities/sources/status.verb.ts +15 -8
- package/src/capabilities/sources/types.ts +18 -25
- package/src/capabilities/sources/update.render.ts +9 -2
- package/src/capabilities/sources/update.verb.ts +12 -23
- package/src/capabilities/surface.test.ts +65 -17
- package/src/capabilities/tier.parity.test.ts +144 -0
- package/src/capabilities/upgrade/upgrade.verb.ts +7 -3
- package/src/constants.test.ts +9 -7
- package/src/constants.ts +49 -23
- package/src/identity.test.ts +333 -0
- package/src/index.ts +2 -2
- package/src/kernel/completion/__snapshots__/emitScripts.test.ts.snap +18 -16
- package/src/kernel/completion/complete.test.ts +3 -5
- package/src/kernel/completion/emitScripts.test.ts +1 -1
- package/src/kernel/completion/emitScripts.ts +4 -2
- package/src/kernel/completion/entitySource.test.ts +185 -41
- package/src/kernel/completion/entitySource.ts +131 -113
- package/src/kernel/completion/model.test.ts +3 -3
- package/src/kernel/completion/model.ts +11 -1
- package/src/kernel/completion/parse.test.ts +2 -4
- package/src/kernel/completion/safety.test.ts +82 -36
- package/src/kernel/completion/shellDrive.test.ts +592 -0
- package/src/kernel/completion/templates/fish.ts +28 -6
- package/src/kernel/completion/types.ts +3 -3
- package/src/kernel/config/__snapshots__/firstRun.test.ts.snap +1 -1
- package/src/kernel/config/defaults.test.ts +85 -0
- package/src/kernel/config/defaults.ts +12 -23
- package/src/kernel/config/evaluateProjectConfig.ts +2 -3
- package/src/kernel/config/findProjectConfig.ts +4 -3
- package/src/kernel/config/firstRun.ts +12 -5
- package/src/kernel/config/globalConfig.ts +2 -2
- package/src/kernel/config/index.ts +4 -5
- package/src/kernel/config/paths.ts +17 -17
- package/src/kernel/config/readConfig.test.ts +242 -6
- package/src/kernel/config/readConfig.ts +8 -8
- package/src/kernel/config/schema.test.ts +50 -0
- package/src/kernel/config/schema.ts +97 -10
- package/src/kernel/config/types.ts +73 -19
- package/src/kernel/copy.test.ts +453 -0
- package/src/kernel/error/PragmaError.test.ts +5 -5
- package/src/kernel/error/constants.ts +3 -2
- package/src/kernel/error/index.ts +1 -1
- package/src/kernel/error/recovery.test.ts +22 -45
- package/src/kernel/error/recovery.ts +22 -27
- package/src/kernel/error/renderError.ts +1 -1
- package/src/kernel/error/types.ts +5 -4
- package/src/kernel/packs/collect.test.ts +195 -11
- package/src/kernel/packs/collect.ts +213 -58
- package/src/kernel/packs/compile.test.ts +163 -7
- package/src/kernel/packs/compile.ts +15 -12
- package/src/kernel/packs/disclosure.test.ts +5 -2
- package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +11 -10
- package/src/kernel/packs/graphqlSource.test.ts +6 -3
- package/src/kernel/packs/iri.ts +5 -7
- package/src/kernel/packs/renderPack.ts +5 -3
- package/src/kernel/packs/resolveEntity.ts +21 -17
- package/src/kernel/packs/runBodies.ts +15 -14
- package/src/kernel/packs/schema.ts +96 -10
- package/src/kernel/packs/sparql/runSelect.test.ts +104 -10
- package/src/kernel/packs/sparql/runSelect.ts +73 -34
- package/src/kernel/packs/toolDescription.test.ts +10 -2
- package/src/kernel/packs/types.ts +39 -4
- package/src/kernel/packs/uniqueness.ts +4 -4
- package/src/kernel/project/cli/buildProgram.test.ts +60 -11
- package/src/kernel/project/cli/buildProgram.ts +11 -0
- package/src/kernel/project/cli/dispatch.test.ts +67 -0
- package/src/kernel/project/cli/dispatch.ts +34 -12
- package/src/kernel/project/cli/globalFlags.test.ts +12 -4
- package/src/kernel/project/cli/globalFlags.ts +14 -14
- package/src/kernel/project/cli/helpFormat.ts +1 -1
- package/src/kernel/project/cli/rootHelp.ts +109 -107
- package/src/kernel/project/mcp/buildServer.ts +11 -6
- package/src/kernel/project/mcp/instructions.ts +12 -7
- package/src/kernel/project/mcp/mcpError.ts +52 -0
- package/src/kernel/project/mcp/parity.test.ts +39 -1
- package/src/kernel/project/mcp/prompts/prompts.test.ts +128 -5
- package/src/kernel/project/mcp/prompts/provider.ts +51 -7
- package/src/kernel/project/mcp/prompts/source.ts +68 -67
- package/src/kernel/project/mcp/registerVerb.ts +25 -6
- package/src/kernel/project/mcp/serve.ts +11 -3
- package/src/kernel/project/mcp/toolDescriptions.test.ts +6 -2
- package/src/kernel/render/disclosure.ts +4 -2
- package/src/kernel/render/formatters.ts +5 -4
- package/src/kernel/render/prefixes.test.ts +36 -0
- package/src/kernel/render/prefixes.ts +40 -13
- package/src/kernel/render/renderers.test.ts +35 -1
- package/src/kernel/render/renderers.ts +25 -7
- package/src/kernel/render/style.test.ts +31 -0
- package/src/kernel/render/style.ts +74 -0
- package/src/kernel/runtime/graphpack/build.ts +57 -3
- package/src/kernel/runtime/graphpack/buildIndex.test.ts +149 -0
- package/src/kernel/runtime/graphpack/buildIndex.ts +173 -50
- package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +5 -8
- package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +3 -4
- package/src/kernel/runtime/graphpack/embedded/pack.stories.generated.ts +3 -0
- package/src/kernel/runtime/graphpack/embedded.ts +37 -11
- package/src/kernel/runtime/graphpack/graphpack.test.ts +274 -6
- package/src/kernel/runtime/graphpack/hash.ts +3 -2
- package/src/kernel/runtime/graphpack/index.ts +1 -1
- package/src/kernel/runtime/graphpack/manifest.ts +114 -12
- package/src/kernel/runtime/graphpack/read.ts +32 -22
- package/src/kernel/runtime/graphpack/schemas.ts +74 -0
- package/src/kernel/runtime/graphpack/stories.ts +84 -0
- package/src/kernel/runtime/graphpack/types.ts +72 -45
- package/src/kernel/runtime/loadSession.ts +4 -12
- package/src/kernel/runtime/paths.ts +64 -15
- package/src/kernel/runtime/probe.test.ts +8 -4
- package/src/kernel/runtime/readEntity.ts +6 -5
- package/src/kernel/runtime/refs/gitOps.ts +1 -19
- package/src/kernel/runtime/refs/parseRef.ts +7 -7
- package/src/kernel/runtime/refs/resolve.ts +141 -65
- package/src/kernel/runtime/resolveSources.test.ts +63 -24
- package/src/kernel/runtime/resolveSources.ts +36 -25
- package/src/kernel/runtime/storeReadiness.ts +62 -0
- package/src/kernel/runtime/storeless.test.ts +1 -1
- package/src/kernel/runtime/types.ts +1 -1
- package/src/kernel/runtime/wasmEmbed.test.ts +25 -20
- package/src/kernel/spec/emitReference.test.ts +196 -0
- package/src/kernel/spec/emitReference.ts +624 -0
- package/src/kernel/spec/emitSurface.ts +13 -7
- package/src/kernel/spec/index.ts +2 -0
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/kernel/spec/surfaceConformance.ts +8 -0
- package/src/kernel/spec/types.ts +29 -9
- package/src/kernel/spec/validate.ts +2 -1
- package/src/kernel/vocabulary.test.ts +164 -0
- package/src/kernel/vocabulary.ts +146 -0
- package/src/testing/PARITY_GAPS.ts +9 -3
- package/src/testing/behavioral/README.md +3 -3
- package/src/testing/behavioral/autoLlm.e2e.test.ts +12 -2
- package/src/testing/behavioral/completion.test.ts +21 -8
- package/src/testing/behavioral/disclosure.test.ts +12 -0
- package/src/testing/behavioral/docExamples.test.ts +283 -0
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +4 -3
- package/src/testing/behavioral/errorMatrix.storeless.test.ts +3 -2
- package/src/testing/behavioral/exitCodes.e2e.test.ts +6 -5
- package/src/testing/behavioral/harness.foundation.test.ts +14 -5
- package/src/testing/behavioral/journeys.cli.test.ts +116 -73
- package/src/testing/behavioral/journeys.defaultPack.test.ts +477 -0
- package/src/testing/behavioral/journeys.firstInstall.test.ts +167 -0
- package/src/testing/behavioral/journeys.interactiveTty.e2e.test.ts +66 -0
- package/src/testing/behavioral/journeys.livePack.e2e.test.ts +129 -0
- package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +179 -0
- package/src/testing/behavioral/journeys.packageStories.test.ts +188 -0
- package/src/testing/behavioral/liveReadSurface.ts +4 -3
- package/src/testing/behavioral/mcpSurface.test.ts +10 -5
- package/src/testing/behavioral/staleCommands.test.ts +86 -0
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +9 -9
- package/src/testing/eval/cases/assembly.ts +16 -9
- package/src/testing/eval/cases/effectful.ts +31 -16
- package/src/testing/eval/cases/readNouns.ts +23 -18
- package/src/testing/eval/cases/stable.ts +5 -1
- package/src/testing/eval/eval.test.ts +7 -1
- package/src/testing/fixtures/fixtureCapability.ts +201 -1
- package/src/testing/fixtures/graph/canonical.ts +6 -3
- package/src/testing/fixtures/graph/defaultPack.ts +179 -0
- package/src/testing/fixtures/graph/owlExport.ts +97 -0
- package/src/testing/helpers/extractSnippets.ts +29 -0
- package/src/testing/helpers/fixtureGraph.ts +33 -16
- package/src/testing/helpers/golden.ts +1 -1
- package/src/testing/helpers/packRuntime.ts +2 -3
- package/src/testing/helpers/projectCli.ts +2 -1
- package/src/testing/helpers/projectMcp.ts +6 -0
- package/src/testing/perf/budgets.test.ts +4 -0
- package/src/testing/perf/budgets.ts +22 -7
- package/src/testing/perf/globalSetup.ts +121 -6
- package/src/types.ts +2 -2
- package/src/capabilities/block/blockList.verb.ts +0 -150
- package/src/capabilities/block/index.ts +0 -25
- package/src/capabilities/block/pack.ts +0 -155
- package/src/capabilities/block/parity.test.ts +0 -180
- package/src/capabilities/block/tierChain.ts +0 -78
- package/src/capabilities/colophon/pragmaColophon.ts +0 -66
- package/src/capabilities/config/field.test.ts +0 -188
- package/src/capabilities/modifier/index.ts +0 -15
- package/src/capabilities/modifier/pack.ts +0 -64
- package/src/capabilities/standard/index.ts +0 -18
- package/src/capabilities/standard/pack.ts +0 -128
- package/src/capabilities/tier/index.ts +0 -19
- package/src/capabilities/tier/lookup.render.ts +0 -34
- package/src/capabilities/tier/lookup.test.ts +0 -68
- package/src/capabilities/tier/lookup.verb.ts +0 -44
- package/src/capabilities/tier/pack.ts +0 -28
- package/src/capabilities/tier/runLookup.ts +0 -59
- package/src/capabilities/token/addConfig.render.ts +0 -27
- package/src/capabilities/token/addConfig.test.ts +0 -62
- package/src/capabilities/token/addConfig.verb.ts +0 -40
- package/src/capabilities/token/index.ts +0 -19
- package/src/capabilities/token/pack.ts +0 -58
- package/src/capabilities/token/runAddConfig.ts +0 -95
- package/src/kernel/completion/bashDrive.test.ts +0 -73
- package/src/kernel/runtime/graphpack/embedded/sample.ttl +0 -39
- package/src/kernel/runtime/lock.ts +0 -90
|
@@ -24,29 +24,20 @@ import { mkdtempSync, readdirSync, readFileSync } from "node:fs";
|
|
|
24
24
|
import { tmpdir } from "node:os";
|
|
25
25
|
import { dirname, join, resolve } from "node:path";
|
|
26
26
|
import { fileURLToPath } from "node:url";
|
|
27
|
-
import {
|
|
27
|
+
import { describe, expect, it } from "vitest";
|
|
28
28
|
|
|
29
29
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
30
30
|
const repoRoot = resolve(here, "../../../../../..");
|
|
31
|
-
const cliNextDir = join(repoRoot, "packages/cli/
|
|
31
|
+
const cliNextDir = join(repoRoot, "packages/cli/pragma");
|
|
32
32
|
const pragmaBin = join(cliNextDir, "src/bin.ts");
|
|
33
33
|
const compiledBin = join(cliNextDir, "dist/pragma");
|
|
34
34
|
const freshCwd = (): string => mkdtempSync(join(tmpdir(), "pragma-compiled-"));
|
|
35
35
|
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
beforeAll
|
|
40
|
-
|
|
41
|
-
cwd: cliNextDir,
|
|
42
|
-
stdio: "pipe",
|
|
43
|
-
});
|
|
44
|
-
if (result.status !== 0) {
|
|
45
|
-
throw new Error(
|
|
46
|
-
`failed to build dist/pragma:\n${result.stderr?.toString() ?? ""}`,
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
}, 180_000);
|
|
36
|
+
// The binary is provisioned by `testing/perf/globalSetup.ts`, which rebuilds it
|
|
37
|
+
// whenever it is missing or older than `src/**`, `scripts/**`, `pragma.conf.ts`
|
|
38
|
+
// or `package.json` — so these tests exercise the current bundle + embedded
|
|
39
|
+
// manifest without a second `beforeAll` writing `dist/pragma` in place while
|
|
40
|
+
// another worker's test is spawning it.
|
|
50
41
|
|
|
51
42
|
/** Read a directory tree into a sorted map of relative path → contents. */
|
|
52
43
|
function snapshot(dir: string): Map<string, string> {
|
|
@@ -108,6 +99,52 @@ describe("compiled pragma create component (PROTECTED)", () => {
|
|
|
108
99
|
}
|
|
109
100
|
});
|
|
110
101
|
|
|
102
|
+
/**
|
|
103
|
+
* PROTECTED — the compiled-binary `create` gate.
|
|
104
|
+
*
|
|
105
|
+
* `create component` runs from the binary (the describe above proves it byte for
|
|
106
|
+
* byte) because summon-component routes every template read through
|
|
107
|
+
* `loadTemplateSync` and passes `content:` into `template()`. `create package` /
|
|
108
|
+
* `create application` do NOT: they call `template({ source })`, summon-core
|
|
109
|
+
* falls through to `readFile(options.source)`, and the run dies with `ENOENT …
|
|
110
|
+
* /$bunfs/templates/package.json.ejs` AFTER `mkdir` has already run — a
|
|
111
|
+
* half-made package left on the user's disk. Measured against a real
|
|
112
|
+
* `dist/pragma` with the gate lifted, for both nouns.
|
|
113
|
+
*
|
|
114
|
+
* A `--dry-run` does NOT test this: it exits 0 without reading a template and
|
|
115
|
+
* merely PRINTS `Read file: /$bunfs/templates/package.json.ejs` as a planned
|
|
116
|
+
* effect. That false positive is why the gate has to be pinned by a real run.
|
|
117
|
+
*
|
|
118
|
+
* The `readdirSync(dir)` assertion is the load-bearing one: it fails with the
|
|
119
|
+
* real symptom if the gate is ever lifted without fixing the generators.
|
|
120
|
+
*/
|
|
121
|
+
describe("compiled pragma create gate (PROTECTED)", () => {
|
|
122
|
+
// A LITERAL noun list, deliberately not derived from `create`'s own
|
|
123
|
+
// declaration: changing the gate's input must turn this red rather than
|
|
124
|
+
// silently drop the case.
|
|
125
|
+
for (const kind of ["package", "application"] as const) {
|
|
126
|
+
it(`refuses \`create ${kind}\` and leaves the cwd untouched`, () => {
|
|
127
|
+
const dir = freshCwd();
|
|
128
|
+
const result = spawnSync(compiledBin, ["create", kind, "--yes"], {
|
|
129
|
+
cwd: dir,
|
|
130
|
+
stdio: "pipe",
|
|
131
|
+
encoding: "utf-8",
|
|
132
|
+
});
|
|
133
|
+
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`;
|
|
134
|
+
// 1, not merely non-zero: UNSUPPORTED is a runtime refusal, which
|
|
135
|
+
// `kernel/error/constants.ts` maps to the generic runtime exit.
|
|
136
|
+
expect(result.status).toBe(1);
|
|
137
|
+
expect(output).toContain("not available in the compiled pragma binary");
|
|
138
|
+
// The clean refusal, not the crash it exists to prevent.
|
|
139
|
+
expect(output).not.toMatch(/ENOENT/);
|
|
140
|
+
expect(output).not.toMatch(/Internal error/);
|
|
141
|
+
// And nothing half-made on disk: the generator's `mkdir` effects run
|
|
142
|
+
// BEFORE its first template read, so a lifted gate leaves a stub tree.
|
|
143
|
+
expect(readdirSync(dir)).toEqual([]);
|
|
144
|
+
}, 30_000);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
111
148
|
/**
|
|
112
149
|
* PROTECTED — the compiled-binary READ guard (U1-orig).
|
|
113
150
|
*
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The generator bindings the `create` surface exposes — the one list the
|
|
3
|
+
* surface's mechanical copies derive from: the `CreateKind` union (`types.ts`),
|
|
4
|
+
* the `--framework` enum and the compiled-binary gate (`create.verb.ts`), the
|
|
5
|
+
* generator lookup (`pickGenerator.ts`), and the template roots the bundler
|
|
6
|
+
* embeds (`scripts/build.ts`). Prose is NOT derived: the verb summaries below
|
|
7
|
+
* `createVerbs` and `capabilities/hints.ts` still name the frameworks by hand.
|
|
8
|
+
*
|
|
9
|
+
* This table is the SINGLE authoring point for which generator packages the
|
|
10
|
+
* distribution ships: it binds each `create` noun to the generator-map key it
|
|
11
|
+
* runs, and — for the one generator whose templates the binary carries — to
|
|
12
|
+
* the package `scripts/build.ts` harvests them from. (A parallel `generators`
|
|
13
|
+
* CONFIG field once declared the same packages; it was validated, layered and
|
|
14
|
+
* read by nothing, and was removed under the L-OPEN-1 ruling — the config
|
|
15
|
+
* validator now rejects it loudly.) The binding is hand-written because
|
|
16
|
+
* neither half can be discovered:
|
|
17
|
+
* - surfacing a noun also needs a hand-written prompt mirror, path param and
|
|
18
|
+
* examples in `create.verb.ts`, so the surface is a deliberate SUBSET —
|
|
19
|
+
* `@canonical/summon-application` ships `application/react`, `domain`, `route`
|
|
20
|
+
* and `wrapper`, and `create` exposes one of them;
|
|
21
|
+
* - `bun build --compile` bundles only statically analysable import specifiers,
|
|
22
|
+
* so a shipped binary can never `import(name)` a declared package (measured:
|
|
23
|
+
* `Cannot find module '@canonical/summon-component' from '/$bunfs/root/…'`).
|
|
24
|
+
* `pickGenerator` must import all three statically.
|
|
25
|
+
*
|
|
26
|
+
* `create.test.ts` pins what is checkable: every binding resolves to the
|
|
27
|
+
* generator it names, and the embedded manifest carries only the templates of
|
|
28
|
+
* the binding that reads through it.
|
|
29
|
+
*
|
|
30
|
+
* Deliberately free of any import: `create.verb.ts` reads this on the `--help` /
|
|
31
|
+
* `__complete` fast path (`capabilities/lazy.test.ts`).
|
|
32
|
+
*/
|
|
33
|
+
export const CREATE_GENERATORS = {
|
|
34
|
+
component: {
|
|
35
|
+
/**
|
|
36
|
+
* The declaring package: `scripts/build.ts` harvests its `.ejs` for the
|
|
37
|
+
* binary.
|
|
38
|
+
*/
|
|
39
|
+
name: "@canonical/summon-component",
|
|
40
|
+
/** `--framework <f>` runs `component/<f>`; the FIRST is the enum default. */
|
|
41
|
+
frameworks: ["react", "svelte", "lit"],
|
|
42
|
+
/**
|
|
43
|
+
* Runs from the compiled binary: this generator routes every template read
|
|
44
|
+
* through `loadTemplateSync` and passes `content:` into `template()`, so the
|
|
45
|
+
* embedded manifest serves it. That — not the templates' file extensions —
|
|
46
|
+
* is the property that decides it, and it is a fact about the GENERATOR's
|
|
47
|
+
* source, not about what this build chooses to embed.
|
|
48
|
+
*/
|
|
49
|
+
readsEmbeddedTemplates: true,
|
|
50
|
+
},
|
|
51
|
+
package: {
|
|
52
|
+
/** The generator-map key `create package` runs. */
|
|
53
|
+
key: "package",
|
|
54
|
+
/**
|
|
55
|
+
* NOT runnable from the compiled binary: this generator calls
|
|
56
|
+
* `template({ source })` with no `content:`, so summon-core falls through to
|
|
57
|
+
* `readFile(options.source)`. Measured against a real `dist/pragma` with the
|
|
58
|
+
* gate lifted: `ENOENT: no such file or directory, open
|
|
59
|
+
* '/$bunfs/templates/package.json.ejs'`, after `mkdir` had already created
|
|
60
|
+
* `my-lib/` and `my-lib/src/` — a half-made package left on disk. A
|
|
61
|
+
* `--dry-run` exits 0 without reading a template, so it does NOT test this.
|
|
62
|
+
*/
|
|
63
|
+
readsEmbeddedTemplates: false,
|
|
64
|
+
},
|
|
65
|
+
application: {
|
|
66
|
+
/** The generator-map key `create application` runs. */
|
|
67
|
+
key: "application/react",
|
|
68
|
+
/**
|
|
69
|
+
* NOT runnable from the compiled binary — same cause as `package` (measured:
|
|
70
|
+
* `ENOENT … '/$bunfs/root/templates/package.json.ejs'`, after the app
|
|
71
|
+
* directory was created). It also `copy()`s non-`.ejs` assets.
|
|
72
|
+
*/
|
|
73
|
+
readsEmbeddedTemplates: false,
|
|
74
|
+
},
|
|
75
|
+
} as const;
|
|
@@ -14,7 +14,7 @@ import { bootRuntime } from "../../kernel/runtime/boot.js";
|
|
|
14
14
|
import type { GlobalFlags } from "../../kernel/runtime/types.js";
|
|
15
15
|
import type { VerbSpec } from "../../kernel/spec/types.js";
|
|
16
16
|
import { projectMcp } from "../../testing/helpers/projectMcp.js";
|
|
17
|
-
import {
|
|
17
|
+
import { createVerbs } from "./create.verb.js";
|
|
18
18
|
import { createModule } from "./index.js";
|
|
19
19
|
|
|
20
20
|
const FLAGS: GlobalFlags = {
|
|
@@ -117,7 +117,7 @@ describe("create over MCP (PROTECTED)", () => {
|
|
|
117
117
|
const mcpPlan = (mcpResult.data as { plan: string[] }).plan;
|
|
118
118
|
|
|
119
119
|
const cliOutcome = await executeVerb(
|
|
120
|
-
|
|
120
|
+
createVerbs.component as VerbSpec,
|
|
121
121
|
{
|
|
122
122
|
framework: "react",
|
|
123
123
|
componentPath: "src/components/Button",
|
|
@@ -25,13 +25,14 @@ import {
|
|
|
25
25
|
import { bootRuntime } from "../../kernel/runtime/boot.js";
|
|
26
26
|
import type { GlobalFlags } from "../../kernel/runtime/types.js";
|
|
27
27
|
import type { ParamSpec } from "../../kernel/spec/types.js";
|
|
28
|
+
import { CREATE_GENERATORS } from "./constants.js";
|
|
28
29
|
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
createPackageVerb,
|
|
30
|
+
createVerbs,
|
|
31
|
+
INCLUDE_FLAG_ALIASES,
|
|
32
32
|
isModuleNotFound,
|
|
33
33
|
} from "./create.verb.js";
|
|
34
34
|
import { generatorToParams } from "./generatorToVerbSpec.js";
|
|
35
|
+
import { createModule } from "./index.js";
|
|
35
36
|
|
|
36
37
|
const FLAGS: GlobalFlags = {
|
|
37
38
|
llm: false,
|
|
@@ -52,7 +53,7 @@ async function runIn(
|
|
|
52
53
|
process.chdir(dir);
|
|
53
54
|
try {
|
|
54
55
|
return await executeVerb(
|
|
55
|
-
|
|
56
|
+
createVerbs.component,
|
|
56
57
|
params,
|
|
57
58
|
mutation,
|
|
58
59
|
bootRuntime(FLAGS, dir),
|
|
@@ -85,7 +86,11 @@ describe("create — real generation + stamp", () => {
|
|
|
85
86
|
expect(readFileSync(main, "utf-8").split("\n")[0]).toBe(
|
|
86
87
|
"// Generated by @canonical/summon-component:react v0.1.0",
|
|
87
88
|
);
|
|
88
|
-
|
|
89
|
+
// Explicit timeout, like the package's other real-run gates: this drives a
|
|
90
|
+
// full generator run (~2.4s idle), and under a contended CI matrix the 5s
|
|
91
|
+
// vitest default fails it on load, not on a defect — both build-matrix
|
|
92
|
+
// legs timed out at 5000ms on a run whose import phase alone took 88s.
|
|
93
|
+
}, 60_000);
|
|
89
94
|
|
|
90
95
|
it("--dry-run writes nothing and previews effects (no Prompt lines)", async () => {
|
|
91
96
|
const dir = freshCwd();
|
|
@@ -100,6 +105,45 @@ describe("create — real generation + stamp", () => {
|
|
|
100
105
|
expect(walk(dir)).toEqual([]); // nothing written
|
|
101
106
|
});
|
|
102
107
|
|
|
108
|
+
it("--dry-run reports the bytes the real run writes (post-stamp)", async () => {
|
|
109
|
+
// The mock previewer never called `onEffectStart`, so summon's stamping
|
|
110
|
+
// transform did not run and every planned byte count under-reported the
|
|
111
|
+
// written file by the stamp's length. The honest previewer runs it.
|
|
112
|
+
const params = {
|
|
113
|
+
framework: "react",
|
|
114
|
+
componentPath: "src/components/Widget",
|
|
115
|
+
withStyles: true,
|
|
116
|
+
withStories: false,
|
|
117
|
+
withSsrTests: false,
|
|
118
|
+
};
|
|
119
|
+
const previewDir = freshCwd();
|
|
120
|
+
const preview = await runIn(previewDir, params, {
|
|
121
|
+
dryRun: true,
|
|
122
|
+
undo: false,
|
|
123
|
+
yes: false,
|
|
124
|
+
});
|
|
125
|
+
expect(preview.exitCode).toBe(0);
|
|
126
|
+
expect(walk(previewDir)).toEqual([]);
|
|
127
|
+
|
|
128
|
+
const planned = new Map<string, number>();
|
|
129
|
+
for (const line of (preview.stdout ?? "").split("\n")) {
|
|
130
|
+
const match = /^ {2}- Write file: (.+) \((\d+) bytes\)$/.exec(line);
|
|
131
|
+
if (match) planned.set(match[1] as string, Number(match[2]));
|
|
132
|
+
}
|
|
133
|
+
expect(planned.size).toBeGreaterThan(0);
|
|
134
|
+
|
|
135
|
+
const realDir = freshCwd();
|
|
136
|
+
expect((await runIn(realDir, params)).exitCode).toBe(0);
|
|
137
|
+
for (const [relative, bytes] of planned) {
|
|
138
|
+
expect({
|
|
139
|
+
file: relative,
|
|
140
|
+
bytes: readFileSync(join(realDir, relative), "utf-8").length,
|
|
141
|
+
}).toEqual({ file: relative, bytes });
|
|
142
|
+
}
|
|
143
|
+
// Same set of files, not just the same counts for the ones checked.
|
|
144
|
+
expect(walk(realDir).sort()).toEqual([...planned.keys()].sort());
|
|
145
|
+
});
|
|
146
|
+
|
|
103
147
|
it("--undo reverses a run", async () => {
|
|
104
148
|
const dir = freshCwd();
|
|
105
149
|
await runIn(dir, {
|
|
@@ -122,7 +166,9 @@ describe("create — real generation + stamp", () => {
|
|
|
122
166
|
);
|
|
123
167
|
expect(outcome.exitCode).toBe(0);
|
|
124
168
|
expect(outcome.stdout).toMatch(/Undid \d+ step/);
|
|
125
|
-
|
|
169
|
+
// TWO real runs (generate, then undo) — the same load-sensitivity as the
|
|
170
|
+
// stamp test above, doubled; same explicit ceiling.
|
|
171
|
+
}, 60_000);
|
|
126
172
|
|
|
127
173
|
it("rejects an unknown framework with INVALID_INPUT", async () => {
|
|
128
174
|
const dir = freshCwd();
|
|
@@ -130,7 +176,7 @@ describe("create — real generation + stamp", () => {
|
|
|
130
176
|
// framework param to exercise pickGenerator's own guard too.
|
|
131
177
|
const runtime = bootRuntime(FLAGS, dir);
|
|
132
178
|
await expect(
|
|
133
|
-
|
|
179
|
+
createVerbs.component.run({ framework: "vue" }, {
|
|
134
180
|
...runtime,
|
|
135
181
|
interaction: { isTTY: false, transport: "cli", yes: true },
|
|
136
182
|
} as never),
|
|
@@ -216,9 +262,19 @@ describe("lazy-React discipline (PROTECTED)", () => {
|
|
|
216
262
|
"@canonical/summon-component",
|
|
217
263
|
"@canonical/summon-package",
|
|
218
264
|
"@canonical/summon-application",
|
|
265
|
+
// The generators pull summon-core, and summon-core is what costs the
|
|
266
|
+
// fast paths (+43 MB RSS once it loads). Dropping the `type` from one of
|
|
267
|
+
// the four `import type … from "@canonical/summon-core"` lines in this
|
|
268
|
+
// directory is a one-word regression nothing else catches.
|
|
269
|
+
"@canonical/summon-core",
|
|
219
270
|
];
|
|
220
271
|
for (const file of graph) {
|
|
221
|
-
|
|
272
|
+
// Type-only imports are erased before runtime, so they load nothing —
|
|
273
|
+
// strip them before asking what the file actually pulls in.
|
|
274
|
+
const src = readFileSync(file, "utf-8").replace(
|
|
275
|
+
/\bimport\s+type\b[^;]*;/g,
|
|
276
|
+
"",
|
|
277
|
+
);
|
|
222
278
|
for (const pkg of forbidden) {
|
|
223
279
|
const esc = pkg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
224
280
|
expect(
|
|
@@ -284,7 +340,7 @@ describe("generator→grammar adapter parity (PROTECTED)", () => {
|
|
|
284
340
|
(generators as Record<string, { prompts: unknown[] }>)["component/react"]
|
|
285
341
|
.prompts as never,
|
|
286
342
|
);
|
|
287
|
-
const verb =
|
|
343
|
+
const verb = createVerbs.component.params;
|
|
288
344
|
|
|
289
345
|
// The verb SYNTHESIZES a `framework` enum (the 3 framework generators
|
|
290
346
|
// collapse to one verb): react|svelte|lit, default react — no generator
|
|
@@ -327,7 +383,7 @@ describe("generator→grammar adapter parity (PROTECTED)", () => {
|
|
|
327
383
|
.prompts as never,
|
|
328
384
|
);
|
|
329
385
|
// The package verb's grammar IS generatorToParams(PACKAGE_MIRROR).
|
|
330
|
-
expect(behavioural(
|
|
386
|
+
expect(behavioural(createVerbs.package.params)).toEqual(
|
|
331
387
|
behavioural(applyRunInstallOverride(real)),
|
|
332
388
|
);
|
|
333
389
|
|
|
@@ -335,7 +391,7 @@ describe("generator→grammar adapter parity (PROTECTED)", () => {
|
|
|
335
391
|
const realRunInstall = real.find((p) => p.name === "runInstall") as
|
|
336
392
|
| Record<string, unknown>
|
|
337
393
|
| undefined;
|
|
338
|
-
const mirrorRunInstall =
|
|
394
|
+
const mirrorRunInstall = createVerbs.package.params.find(
|
|
339
395
|
(p) => p.name === "runInstall",
|
|
340
396
|
) as Record<string, unknown> | undefined;
|
|
341
397
|
expect(realRunInstall?.default).toBe(true);
|
|
@@ -350,6 +406,18 @@ describe("generator→grammar adapter parity (PROTECTED)", () => {
|
|
|
350
406
|
});
|
|
351
407
|
});
|
|
352
408
|
|
|
409
|
+
/** Map a mirror's CLI `--with-X` include-flag names back to the generator
|
|
410
|
+
* prompt names (AV-228 B8) so the parity comparison still checks kinds and
|
|
411
|
+
* defaults against the real generator despite the deliberate flag rename. */
|
|
412
|
+
const toGeneratorNames = (
|
|
413
|
+
kind: "application",
|
|
414
|
+
params: readonly ParamSpec[],
|
|
415
|
+
): ParamSpec[] =>
|
|
416
|
+
params.map((p) => {
|
|
417
|
+
const generatorName = INCLUDE_FLAG_ALIASES[kind][p.name];
|
|
418
|
+
return generatorName ? ({ ...p, name: generatorName } as ParamSpec) : p;
|
|
419
|
+
});
|
|
420
|
+
|
|
353
421
|
it("application: mirror matches the application/react generator (defaults)", async () => {
|
|
354
422
|
const { generators } = await import("@canonical/summon-application");
|
|
355
423
|
const real = generatorToParams(
|
|
@@ -357,14 +425,30 @@ describe("generator→grammar adapter parity (PROTECTED)", () => {
|
|
|
357
425
|
"application/react"
|
|
358
426
|
].prompts as never,
|
|
359
427
|
);
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
428
|
+
// Bridge the B8 include-flag rename before comparing: the CLI grammar
|
|
429
|
+
// exposes `withSsr`/`withRouter`/`withForms`, which map back to the
|
|
430
|
+
// generator's `ssr`/`router`/`forms`.
|
|
431
|
+
expect(
|
|
432
|
+
behavioural(
|
|
433
|
+
toGeneratorNames("application", createVerbs.application.params),
|
|
434
|
+
),
|
|
435
|
+
).toEqual(behavioural(applyRunInstallOverride(real)));
|
|
436
|
+
|
|
437
|
+
// Guard the B8 rename itself: ALL application include-flags ARE on the
|
|
438
|
+
// `--with-X` convention (params `withSsr`/`withRouter`/`withForms`/
|
|
439
|
+
// `withRelay`), not the bare `ssr`/`router`/`forms`/`relay` booleans.
|
|
440
|
+
const names = createVerbs.application.params.map((p) => p.name);
|
|
441
|
+
expect(names).toContain("withSsr");
|
|
442
|
+
expect(names).toContain("withRouter");
|
|
443
|
+
expect(names).toContain("withForms");
|
|
444
|
+
expect(names).toContain("withRelay");
|
|
445
|
+
expect(names).not.toContain("ssr");
|
|
446
|
+
expect(names).not.toContain("relay");
|
|
363
447
|
|
|
364
448
|
const realRunInstall = real.find((p) => p.name === "runInstall") as
|
|
365
449
|
| Record<string, unknown>
|
|
366
450
|
| undefined;
|
|
367
|
-
const mirrorRunInstall =
|
|
451
|
+
const mirrorRunInstall = createVerbs.application.params.find(
|
|
368
452
|
(p) => p.name === "runInstall",
|
|
369
453
|
) as Record<string, unknown> | undefined;
|
|
370
454
|
expect(realRunInstall?.default).toBe(true);
|
|
@@ -372,7 +456,7 @@ describe("generator→grammar adapter parity (PROTECTED)", () => {
|
|
|
372
456
|
|
|
373
457
|
// appPath is the positional entry point (its default carries across).
|
|
374
458
|
expect(
|
|
375
|
-
|
|
459
|
+
createVerbs.application.params.find((p) => p.name === "appPath"),
|
|
376
460
|
).toMatchObject({ kind: "string", positional: true, default: "my-app" });
|
|
377
461
|
});
|
|
378
462
|
});
|
|
@@ -401,3 +485,95 @@ describe("compiled-binary create gate (M4)", () => {
|
|
|
401
485
|
expect(isModuleNotFound("nope")).toBe(false);
|
|
402
486
|
});
|
|
403
487
|
});
|
|
488
|
+
|
|
489
|
+
describe("declared generator bindings (PROTECTED)", () => {
|
|
490
|
+
// CREATE_GENERATORS is the one place the create surface's generator facts are
|
|
491
|
+
// written down. What is checkable against something outside itself — the live
|
|
492
|
+
// generator maps and the embedded manifest — is checked here.
|
|
493
|
+
|
|
494
|
+
it("every binding resolves to the generator it names", async () => {
|
|
495
|
+
const { pickGenerator } = await import("./pickGenerator.js");
|
|
496
|
+
// `meta.name` is the generator's OWN identity, so a swapped `key` or a
|
|
497
|
+
// framework the map does not carry turns this red — which `toBeDefined()`
|
|
498
|
+
// on a non-nullable return could not.
|
|
499
|
+
for (const framework of CREATE_GENERATORS.component.frameworks) {
|
|
500
|
+
expect(pickGenerator("component", { framework }).meta.name).toBe(
|
|
501
|
+
`component/${framework}`,
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
expect(pickGenerator("package", {}).meta.name).toBe("package");
|
|
505
|
+
expect(pickGenerator("application", {}).meta.name).toBe(
|
|
506
|
+
"application/react",
|
|
507
|
+
);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it("exactly the two source-run-only verbs publish the availability caveat", () => {
|
|
511
|
+
// A LITERAL list, for the same reason `compiledCreate.subprocess.test.ts`
|
|
512
|
+
// uses one: the caveat is DERIVED from `readsEmbeddedTemplates`, so an
|
|
513
|
+
// assertion derived from the same bit would agree with itself no matter
|
|
514
|
+
// which way the bit was flipped. These two are the nouns that test proves
|
|
515
|
+
// refuse on a real compiled binary, so this is what the reference is held
|
|
516
|
+
// to — and flipping the bit turns this red, along with the reference
|
|
517
|
+
// drift-guard, in the same run.
|
|
518
|
+
const caveated = createModule.verbs
|
|
519
|
+
.filter((verb) => verb.summary.includes("Source-run only."))
|
|
520
|
+
.map((verb) => verb.path.at(1));
|
|
521
|
+
expect(caveated).toEqual(["package", "application"]);
|
|
522
|
+
for (const verb of createModule.verbs) {
|
|
523
|
+
const kind = verb.path.at(1);
|
|
524
|
+
const gated = caveated.includes(kind);
|
|
525
|
+
// The `doc` is what `tools.md` and the MCP tool description render, so
|
|
526
|
+
// the explanation has to travel with the marker, and it quotes the
|
|
527
|
+
// command it is about — a copy-paste between bindings is then visible.
|
|
528
|
+
expect(verb.doc?.includes("UNSUPPORTED") ?? false, kind).toBe(gated);
|
|
529
|
+
if (gated) {
|
|
530
|
+
expect(verb.doc).toContain(`create ${kind}`);
|
|
531
|
+
// And it still says what the verb MAKES. `doc ?? summary` means a doc
|
|
532
|
+
// REPLACES the summary on the MCP surface, so a caveat alone left an
|
|
533
|
+
// agent no statement anywhere of what `create_package` scaffolds.
|
|
534
|
+
expect(verb.doc).toContain(
|
|
535
|
+
verb.summary.replace(" Source-run only.", ""),
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it("the --framework enum offers the declared frameworks and defaults to one of them", () => {
|
|
542
|
+
const framework = createVerbs.component.params.find(
|
|
543
|
+
(p) => p.name === "framework",
|
|
544
|
+
);
|
|
545
|
+
// A `values: []` enum with a `default` would leave every `--framework`
|
|
546
|
+
// rejected while `--help` still advertises a default — the hazard this names.
|
|
547
|
+
// (That every value resolves is the test above.)
|
|
548
|
+
expect(framework?.kind).toBe("enum");
|
|
549
|
+
const values = (framework as { values: readonly string[] }).values;
|
|
550
|
+
expect(values.length).toBeGreaterThan(0);
|
|
551
|
+
expect(values).toContain(framework?.default);
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
it("embeds only the templates the component loader can key", async () => {
|
|
555
|
+
const { TEMPLATES } = await import("./templates.embedded.generated.js");
|
|
556
|
+
const keys = Object.keys(TEMPLATES);
|
|
557
|
+
// summon-component's `qualifiedKey()` prefixes EVERY lookup with
|
|
558
|
+
// `component/`, so any other entry ships dead weight the binary can never
|
|
559
|
+
// read — and a binding embedded without a manifest-reading generator would
|
|
560
|
+
// put one here. Non-empty first: `.every()` over an empty list is vacuous.
|
|
561
|
+
expect(keys.length).toBeGreaterThan(0);
|
|
562
|
+
expect(keys.filter((k) => !k.startsWith("component/"))).toEqual([]);
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it("create surfaces exactly the three declared nouns", () => {
|
|
566
|
+
// A LITERAL surface pin, deliberately not derived from CREATE_GENERATORS:
|
|
567
|
+
// surfacing a noun also needs a hand-written prompt mirror, path param and
|
|
568
|
+
// examples in create.verb.ts, so the surface is a deliberate SUBSET of what
|
|
569
|
+
// the declared packages ship (`@canonical/summon-application` also ships
|
|
570
|
+
// `domain`, `route` and `wrapper`). Pinning OUR three nouns rather than any
|
|
571
|
+
// third-party package's generator list keeps an upstream release from
|
|
572
|
+
// turning this red.
|
|
573
|
+
expect(createModule.verbs.map((v) => v.path[1])).toEqual([
|
|
574
|
+
"component",
|
|
575
|
+
"package",
|
|
576
|
+
"application",
|
|
577
|
+
]);
|
|
578
|
+
});
|
|
579
|
+
});
|