@canonical/pragma-cli 0.36.0 → 0.38.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 +3 -2
- package/bundled-skills/adoption-a1-styles/SKILL.md +24 -0
- package/bundled-skills/adoption-a2-components/SKILL.md +22 -6
- package/bundled-skills/anatomy-author/ANATOMY_DSL_SPEC.md +1397 -327
- package/bundled-skills/anatomy-author/SKILL.md +840 -137
- package/bundled-skills/design-auditor/SKILL.md +133 -9
- package/bundled-skills/implement-component/SKILL.md +192 -23
- package/bundled-skills/specify-component/SKILL.md +124 -12
- package/bundled-skills/specify-pattern/SKILL.md +47 -8
- package/dist/package.json +23 -20
- package/dist/pragma.conf.js +963 -40
- package/dist/pragma.conf.js.map +1 -1
- package/dist/src/capabilities/capabilities/catalog.js +14 -5
- package/dist/src/capabilities/capabilities/catalog.js.map +1 -1
- package/dist/src/capabilities/capabilities/hints.js +33 -5
- package/dist/src/capabilities/capabilities/hints.js.map +1 -1
- package/dist/src/capabilities/config/show.render.js +30 -2
- package/dist/src/capabilities/config/show.render.js.map +1 -1
- package/dist/src/capabilities/create/create.verb.js +2 -1
- package/dist/src/capabilities/create/create.verb.js.map +1 -1
- package/dist/src/capabilities/create/createSurface.generated.js +23 -1
- package/dist/src/capabilities/create/createSurface.generated.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/checkNodeVersion.js +11 -6
- package/dist/src/capabilities/doctor/checks/checkNodeVersion.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/harnessInventory.js +1 -1
- package/dist/src/capabilities/doctor/checks/harnessInventory.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/isSupportedNodeVersion.js +38 -0
- package/dist/src/capabilities/doctor/checks/isSupportedNodeVersion.js.map +1 -0
- package/dist/src/capabilities/doctor/checks/targetHealth.js +104 -14
- package/dist/src/capabilities/doctor/checks/targetHealth.js.map +1 -1
- package/dist/src/capabilities/graph/connect.render.js +101 -0
- package/dist/src/capabilities/graph/connect.render.js.map +1 -0
- package/dist/src/capabilities/graph/connect.types.js +21 -0
- package/dist/src/capabilities/graph/connect.types.js.map +1 -0
- package/dist/src/capabilities/graph/connect.verb.js +147 -0
- package/dist/src/capabilities/graph/connect.verb.js.map +1 -0
- package/dist/src/capabilities/graph/index.js +5 -3
- package/dist/src/capabilities/graph/index.js.map +1 -1
- package/dist/src/capabilities/graph/queryText.js +206 -0
- package/dist/src/capabilities/graph/queryText.js.map +1 -0
- package/dist/src/capabilities/graph/relationIndex.js +302 -0
- package/dist/src/capabilities/graph/relationIndex.js.map +1 -0
- package/dist/src/capabilities/graph/runConnect.js +456 -0
- package/dist/src/capabilities/graph/runConnect.js.map +1 -0
- package/dist/src/capabilities/graph/runQuery.js +21 -4
- package/dist/src/capabilities/graph/runQuery.js.map +1 -1
- package/dist/src/capabilities/setup/buildPlan.js +6 -2
- package/dist/src/capabilities/setup/buildPlan.js.map +1 -1
- package/dist/src/capabilities/setup/operations/index.js +7 -2
- package/dist/src/capabilities/setup/operations/index.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupCompletions.js +6 -2
- package/dist/src/capabilities/setup/operations/setupCompletions.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupConfig.js +4 -2
- package/dist/src/capabilities/setup/operations/setupConfig.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupGenerator.js +72 -14
- package/dist/src/capabilities/setup/operations/setupGenerator.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupLsp.js +372 -62
- package/dist/src/capabilities/setup/operations/setupLsp.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupMcp.js +150 -5
- package/dist/src/capabilities/setup/operations/setupMcp.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupSkills.js +4 -2
- package/dist/src/capabilities/setup/operations/setupSkills.js.map +1 -1
- package/dist/src/capabilities/setup/operations/writability.js +114 -0
- package/dist/src/capabilities/setup/operations/writability.js.map +1 -0
- package/dist/src/capabilities/setup/plan.js.map +1 -1
- package/dist/src/capabilities/setup/setup.verb.js +80 -31
- package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
- package/dist/src/capabilities/setup/targets.js +124 -42
- package/dist/src/capabilities/setup/targets.js.map +1 -1
- package/dist/src/capabilities/shared/index.js +1 -1
- package/dist/src/capabilities/shared/index.js.map +1 -1
- package/dist/src/constants.js +7 -1
- package/dist/src/constants.js.map +1 -1
- package/dist/src/kernel/error/renderError.js +3 -2
- package/dist/src/kernel/error/renderError.js.map +1 -1
- package/dist/src/kernel/error/validOptions.js +41 -0
- package/dist/src/kernel/error/validOptions.js.map +1 -0
- package/dist/src/kernel/packs/compile.js +90 -6
- package/dist/src/kernel/packs/compile.js.map +1 -1
- package/dist/src/kernel/packs/cursor.js +135 -0
- package/dist/src/kernel/packs/cursor.js.map +1 -0
- package/dist/src/kernel/packs/graphql/buildLookupDocument.js +9 -2
- package/dist/src/kernel/packs/graphql/buildLookupDocument.js.map +1 -1
- package/dist/src/kernel/packs/paging.js +108 -0
- package/dist/src/kernel/packs/paging.js.map +1 -0
- package/dist/src/kernel/packs/renderPack.js +154 -5
- package/dist/src/kernel/packs/renderPack.js.map +1 -1
- package/dist/src/kernel/packs/resolveEntity.js +118 -19
- package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
- package/dist/src/kernel/packs/runBodies.js +156 -18
- package/dist/src/kernel/packs/runBodies.js.map +1 -1
- package/dist/src/kernel/packs/schema.js +160 -12
- package/dist/src/kernel/packs/schema.js.map +1 -1
- package/dist/src/kernel/packs/sparql/authorQuery.js +227 -0
- package/dist/src/kernel/packs/sparql/authorQuery.js.map +1 -0
- package/dist/src/kernel/packs/sparql/buildListQuery.js +251 -0
- package/dist/src/kernel/packs/sparql/buildListQuery.js.map +1 -0
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js +125 -22
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
- package/dist/src/kernel/packs/sparql/filterValues.js +145 -0
- package/dist/src/kernel/packs/sparql/filterValues.js.map +1 -0
- package/dist/src/kernel/packs/sparql/index.js +24 -14
- package/dist/src/kernel/packs/sparql/index.js.map +1 -1
- package/dist/src/kernel/packs/sparql/runSelect.js +10 -4
- package/dist/src/kernel/packs/sparql/runSelect.js.map +1 -1
- package/dist/src/kernel/packs/sparql/searchTerm.js +25 -0
- package/dist/src/kernel/packs/sparql/searchTerm.js.map +1 -0
- package/dist/src/kernel/packs/storyRules.js +151 -0
- package/dist/src/kernel/packs/storyRules.js.map +1 -0
- package/dist/src/kernel/packs/tierScope.js +310 -0
- package/dist/src/kernel/packs/tierScope.js.map +1 -0
- package/dist/src/kernel/packs/types.js +80 -0
- package/dist/src/kernel/packs/types.js.map +1 -1
- package/dist/src/kernel/project/cli/buildProgram.js +10 -1
- package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
- package/dist/src/kernel/project/cli/dispatch.js +104 -3
- package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
- package/dist/src/kernel/project/cli/rootHelp.js +8 -5
- package/dist/src/kernel/project/cli/rootHelp.js.map +1 -1
- package/dist/src/kernel/project/cli/suggestNames.js +31 -7
- package/dist/src/kernel/project/cli/suggestNames.js.map +1 -1
- package/dist/src/kernel/project/mcp/registerVerb.js +5 -1
- package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
- package/dist/src/kernel/render/renderers.js +34 -2
- package/dist/src/kernel/render/renderers.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/embedded/pack.generated.js +2 -3
- package/dist/src/kernel/runtime/graphpack/embedded/pack.generated.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/embedded/pack.index.generated.js +1 -1
- package/dist/src/kernel/runtime/graphpack/embedded/pack.index.generated.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.js +4 -0
- package/dist/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.js.map +1 -0
- package/dist/src/kernel/runtime/graphpack/embedded.js +13 -6
- package/dist/src/kernel/runtime/graphpack/embedded.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/read.js +17 -10
- package/dist/src/kernel/runtime/graphpack/read.js.map +1 -1
- package/dist/src/kernel/runtime/loadSession.js +3 -1
- package/dist/src/kernel/runtime/loadSession.js.map +1 -1
- package/dist/src/kernel/runtime/probe.js +1 -1
- package/dist/src/kernel/runtime/probe.js.map +1 -1
- package/dist/src/kernel/runtime/readEntity.js +19 -4
- package/dist/src/kernel/runtime/readEntity.js.map +1 -1
- package/dist/src/kernel/runtime/refs/parseRef.js +17 -1
- package/dist/src/kernel/runtime/refs/parseRef.js.map +1 -1
- package/dist/src/kernel/runtime/refs/resolve.js +38 -5
- package/dist/src/kernel/runtime/refs/resolve.js.map +1 -1
- package/dist/src/kernel/spec/emitReference.js +1 -1
- package/dist/src/kernel/spec/emitReference.js.map +1 -1
- package/package.json +23 -20
- package/pragma.conf.ts +983 -40
- package/src/capabilities/block.parity.test.ts +207 -27
- package/src/capabilities/block.shipped.exec.test.ts +156 -14
- package/src/capabilities/block.tierRank.exec.test.ts +46 -9
- package/src/capabilities/capabilities/catalog.ts +14 -5
- package/src/capabilities/capabilities/hints.ts +40 -5
- package/src/capabilities/config/collectConfigShow.test.ts +1 -0
- package/src/capabilities/config/set.test.ts +24 -25
- package/src/capabilities/config/show.render.test.ts +9 -4
- package/src/capabilities/config/show.render.ts +30 -2
- package/src/capabilities/create/byteEquality.test.ts +28 -1
- package/src/capabilities/create/create.test.ts +1 -0
- package/src/capabilities/create/create.verb.ts +2 -1
- package/src/capabilities/create/createGrammar.test.ts +4 -1
- package/src/capabilities/create/createSurface.generated.ts +23 -1
- package/src/capabilities/create/crossCli.subprocess.test.ts +10 -3
- package/src/capabilities/distribution.parity.test.ts +12 -11
- package/src/capabilities/distribution.test.ts +77 -7
- package/src/capabilities/doctor/checks/checkNodeVersion.test.ts +49 -0
- package/src/capabilities/doctor/checks/checkNodeVersion.ts +11 -7
- package/src/capabilities/doctor/checks/harnessInventory.test.ts +13 -6
- package/src/capabilities/doctor/checks/harnessInventory.ts +8 -4
- package/src/capabilities/doctor/checks/isSupportedNodeVersion.test.ts +111 -0
- package/src/capabilities/doctor/checks/isSupportedNodeVersion.ts +34 -0
- package/src/capabilities/doctor/checks/mcpCommand.test.ts +7 -1
- package/src/capabilities/doctor/checks/targetHealth.ts +114 -13
- package/src/capabilities/doctor/doctor.test.ts +327 -36
- package/src/capabilities/emptyRecovery.test.ts +84 -28
- package/src/capabilities/graph/connect.render.ts +141 -0
- package/src/capabilities/graph/connect.shipped.exec.test.ts +370 -0
- package/src/capabilities/graph/connect.test.ts +361 -0
- package/src/capabilities/graph/connect.types.ts +136 -0
- package/src/capabilities/graph/connect.verb.ts +165 -0
- package/src/capabilities/graph/index.ts +5 -3
- package/src/capabilities/graph/query.shipped.exec.test.ts +66 -0
- package/src/capabilities/graph/query.test.ts +17 -0
- package/src/capabilities/graph/queryText.test.ts +121 -0
- package/src/capabilities/graph/queryText.ts +240 -0
- package/src/capabilities/graph/relationIndex.ts +372 -0
- package/src/capabilities/graph/runConnect.ts +577 -0
- package/src/capabilities/graph/runQuery.ts +21 -6
- package/src/capabilities/lazy.test.ts +32 -2
- package/src/capabilities/listBudget.shipped.exec.test.ts +239 -0
- package/src/capabilities/listLookup.shipped.exec.test.ts +387 -0
- package/src/capabilities/listQuery.shipped.exec.test.ts +400 -0
- package/src/capabilities/resources/resources.test.ts +38 -2
- package/src/capabilities/setup/buildPlan.ts +7 -1
- package/src/capabilities/setup/operations/index.ts +14 -0
- package/src/capabilities/setup/operations/setupCompletions.ts +9 -2
- package/src/capabilities/setup/operations/setupConfig.ts +7 -2
- package/src/capabilities/setup/operations/setupGenerator.ts +93 -15
- package/src/capabilities/setup/operations/setupLsp.ts +544 -85
- package/src/capabilities/setup/operations/setupMcp.ts +212 -4
- package/src/capabilities/setup/operations/setupSkills.ts +7 -2
- package/src/capabilities/setup/operations/writability.test.ts +151 -0
- package/src/capabilities/setup/operations/writability.ts +135 -0
- package/src/capabilities/setup/plan.ts +11 -0
- package/src/capabilities/setup/setup.test.ts +992 -16
- package/src/capabilities/setup/setup.verb.ts +84 -36
- package/src/capabilities/setup/targets.ts +152 -48
- package/src/capabilities/setup/types.ts +41 -5
- package/src/capabilities/shared/index.ts +1 -0
- package/src/capabilities/sources/sources.test.ts +55 -0
- package/src/capabilities/surface.test.ts +59 -11
- package/src/capabilities/tier.parity.test.ts +2 -2
- package/src/capabilities/token.parity.test.ts +784 -0
- package/src/capabilities/tokenGraph.cli.test.ts +279 -0
- package/src/capabilities/tokenGraph.shipped.exec.test.ts +653 -0
- package/src/capabilities/variable.parity.test.ts +448 -0
- package/src/constants.ts +8 -0
- package/src/kernel/completion/shellDrive.test.ts +32 -30
- package/src/kernel/config/defaults.test.ts +21 -2
- package/src/kernel/config/readConfig.test.ts +1 -1
- package/src/kernel/error/renderError.ts +3 -2
- package/src/kernel/error/validOptions.test.ts +57 -0
- package/src/kernel/error/validOptions.ts +45 -0
- package/src/kernel/packs/collect.test.ts +2 -0
- package/src/kernel/packs/compile.test.ts +188 -15
- package/src/kernel/packs/compile.ts +109 -14
- package/src/kernel/packs/cursor.ts +135 -0
- package/src/kernel/packs/filteredEmpty.test.ts +213 -0
- package/src/kernel/packs/graphql/buildLookupDocument.ts +16 -9
- package/src/kernel/packs/graphqlSource.test.ts +152 -0
- package/src/kernel/packs/listNotice.test.ts +156 -0
- package/src/kernel/packs/paging.ts +110 -0
- package/src/kernel/packs/renderPack.ts +186 -13
- package/src/kernel/packs/resolveEntity.test.ts +62 -3
- package/src/kernel/packs/resolveEntity.ts +186 -26
- package/src/kernel/packs/runBodies.ts +196 -26
- package/src/kernel/packs/schema.ts +180 -13
- package/src/kernel/packs/sparql/authorQuery.test.ts +179 -0
- package/src/kernel/packs/sparql/authorQuery.ts +257 -0
- package/src/kernel/packs/sparql/buildListQuery.test.ts +344 -0
- package/src/kernel/packs/sparql/buildListQuery.ts +344 -0
- package/src/kernel/packs/sparql/buildLookupQuery.test.ts +138 -0
- package/src/kernel/packs/sparql/buildLookupQuery.ts +140 -20
- package/src/kernel/packs/sparql/filterValues.test.ts +86 -0
- package/src/kernel/packs/sparql/filterValues.ts +182 -0
- package/src/kernel/packs/sparql/index.ts +30 -14
- package/src/kernel/packs/sparql/runSelect.test.ts +63 -0
- package/src/kernel/packs/sparql/runSelect.ts +42 -2
- package/src/kernel/packs/sparql/searchTerm.ts +29 -0
- package/src/kernel/packs/storyRules.ts +180 -0
- package/src/kernel/packs/tierScope.test.ts +487 -0
- package/src/kernel/packs/tierScope.ts +409 -0
- package/src/kernel/packs/toolDescription.test.ts +13 -10
- package/src/kernel/packs/types.ts +311 -9
- package/src/kernel/project/cli/buildProgram.ts +17 -1
- package/src/kernel/project/cli/dispatch.test.ts +87 -0
- package/src/kernel/project/cli/dispatch.ts +114 -4
- package/src/kernel/project/cli/rootHelp.ts +8 -5
- package/src/kernel/project/cli/suggest.test.ts +35 -0
- package/src/kernel/project/cli/suggestNames.ts +34 -7
- package/src/kernel/project/mcp/registerVerb.ts +5 -1
- package/src/kernel/render/contracts.ts +2 -0
- package/src/kernel/render/prefixes.test.ts +2 -0
- package/src/kernel/render/renderers.test.ts +43 -0
- package/src/kernel/render/renderers.ts +34 -1
- package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +2 -3
- package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +1 -1
- package/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.ts +3 -0
- package/src/kernel/runtime/graphpack/embedded.ts +14 -6
- package/src/kernel/runtime/graphpack/graphpack.test.ts +31 -5
- package/src/kernel/runtime/graphpack/read.ts +17 -13
- package/src/kernel/runtime/loadSession.ts +3 -1
- package/src/kernel/runtime/probe.ts +1 -1
- package/src/kernel/runtime/readEntity.ts +19 -4
- package/src/kernel/runtime/refs/parseRef.test.ts +49 -1
- package/src/kernel/runtime/refs/parseRef.ts +25 -1
- package/src/kernel/runtime/refs/resolve.ts +39 -5
- package/src/kernel/runtime/types.ts +30 -0
- package/src/kernel/spec/emitReference.ts +1 -1
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/kernel/spec/types.ts +17 -0
- package/src/testing/PARITY_GAPS.ts +4 -4
- package/src/testing/behavioral/agentSession.mcp.test.ts +43 -39
- package/src/testing/behavioral/completion.test.ts +7 -6
- package/src/testing/behavioral/disclosure.test.ts +39 -37
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +82 -64
- package/src/testing/behavioral/errorMatrix.storeless.test.ts +11 -10
- package/src/testing/behavioral/journeys.cli.test.ts +71 -32
- package/src/testing/behavioral/journeys.defaultPack.test.ts +6 -4
- package/src/testing/behavioral/journeys.firstInstall.test.ts +70 -5
- package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +4 -3
- package/src/testing/behavioral/journeys.packageStories.test.ts +2 -1
- package/src/testing/behavioral/mcpSurface.test.ts +28 -25
- package/src/testing/behavioral/parity.test.ts +25 -23
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +20 -0
- package/src/testing/eval/cases/effectful.ts +7 -1
- package/src/testing/eval/cases/readNouns.ts +100 -11
- package/src/testing/fixtures/blockGraph.ts +89 -5
- package/src/testing/fixtures/connectGraph.ts +159 -0
- package/src/testing/fixtures/graph/canonical.ts +128 -32
- package/src/testing/helpers/fsProbe.ts +88 -0
- package/src/testing/helpers/runCli.test.ts +44 -0
- package/src/testing/helpers/runCli.ts +27 -3
- package/src/testing/perf/budgets.test.ts +3 -1
- package/src/testing/perf/budgets.ts +138 -54
- package/dist/src/kernel/packs/sparql/applyFilters.js +0 -167
- package/dist/src/kernel/packs/sparql/applyFilters.js.map +0 -1
- package/dist/src/kernel/packs/sparql/applySearch.js +0 -25
- package/dist/src/kernel/packs/sparql/applySearch.js.map +0 -1
- package/src/kernel/packs/sparql/applyFilters.ts +0 -203
- package/src/kernel/packs/sparql/applySearch.ts +0 -33
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Variable-noun semantic parity (PROTECTED) — the `variable` story
|
|
3
|
+
* `pragma.conf.ts` declares, over a platform-stratum fixture graph.
|
|
4
|
+
*
|
|
5
|
+
* Three properties of the story are only falsifiable against an authored graph,
|
|
6
|
+
* and the fixture exists for them:
|
|
7
|
+
*
|
|
8
|
+
* - the PUBLISHED NAME is the CSS name without its leading dashes. That is not
|
|
9
|
+
* cosmetic: a `--`-prefixed positional cannot be typed at all, so a noun
|
|
10
|
+
* publishing `--color-text` would be addressable by nothing. The fixture
|
|
11
|
+
* carries a variable whose name would collide with a SYMBOL name if the
|
|
12
|
+
* dashes were kept differently, and one standing for no symbol at all.
|
|
13
|
+
* - the AT-RULE STACK is an rdf:List, so its path needs the list walk. One
|
|
14
|
+
* condition here nests two levels, which is the only shape that tells the
|
|
15
|
+
* walk apart from reading the list's head — and the corpus has exactly one
|
|
16
|
+
* such condition, so a shipped-only test would pass on the head alone.
|
|
17
|
+
* - the CHAIN is transitively closed over every declaration of every hop. The
|
|
18
|
+
* fixture is a three-hop chain through a variable that stands for nothing, so
|
|
19
|
+
* the walk has to pass THROUGH a symbol-less variable to reach the symbol at
|
|
20
|
+
* the end. That is the case a one-hop join silently gets wrong.
|
|
21
|
+
*
|
|
22
|
+
* Counts are pinned because this file AUTHORS the graph. The invariants the
|
|
23
|
+
* shipped corpus decides — that the dash-stripping is injective and collides
|
|
24
|
+
* with no symbol name — are asserted over the real corpus in
|
|
25
|
+
* `tokenGraph.shipped.exec.test.ts`.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
29
|
+
import { compilePack } from "../kernel/packs/compile.js";
|
|
30
|
+
import type { LookupOutput } from "../kernel/packs/resolveEntity.js";
|
|
31
|
+
import { distributionSource, type PackPage } from "../kernel/packs/types.js";
|
|
32
|
+
import { verbKey } from "../kernel/packs/uniqueness.js";
|
|
33
|
+
import { DEFAULT_PREFIX_MAP } from "../kernel/render/prefixes.js";
|
|
34
|
+
import type { PragmaRuntime } from "../kernel/runtime/types.js";
|
|
35
|
+
import type { VerbSpec } from "../kernel/spec/types.js";
|
|
36
|
+
import { buildFixtureRuntime } from "../testing/helpers/packRuntime.js";
|
|
37
|
+
import { declaredStories } from "./distribution.js";
|
|
38
|
+
|
|
39
|
+
const variablePack = declaredStories.get("variable");
|
|
40
|
+
if (!variablePack) {
|
|
41
|
+
throw new Error('pragma.conf.ts declares no story for "variable"');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const DT = "https://dt.canonical.com/";
|
|
45
|
+
const DT_WEB = "https://dt.canonical.com/platform/web/";
|
|
46
|
+
const PREFIXES = {
|
|
47
|
+
dt: DT,
|
|
48
|
+
"dt-web": DT_WEB,
|
|
49
|
+
rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
|
|
50
|
+
rdfs: "http://www.w3.org/2000/01/rdf-schema#",
|
|
51
|
+
owl: "http://www.w3.org/2002/07/owl#",
|
|
52
|
+
xsd: "http://www.w3.org/2001/XMLSchema#",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Four variables under one platform, and the chain that joins them:
|
|
57
|
+
*
|
|
58
|
+
* --modifier-color-text (stands for NOTHING; selected at criticality.success,
|
|
59
|
+
* and nested two at-rules deep)
|
|
60
|
+
* references
|
|
61
|
+
* --color-text-success (stands for nothing either — the middle of the walk)
|
|
62
|
+
* references
|
|
63
|
+
* --color-text (stands for dt:color.text; declared under :root,
|
|
64
|
+
* also at mode.dark)
|
|
65
|
+
*
|
|
66
|
+
* plus `--typography-weight-semi-bold`, a second spelling of a symbol another
|
|
67
|
+
* variable already stands for, which is why a lookup by name answers one row
|
|
68
|
+
* each rather than two.
|
|
69
|
+
*/
|
|
70
|
+
const TTL = `
|
|
71
|
+
@prefix dt: <${DT}> .
|
|
72
|
+
@prefix dt-web: <${DT_WEB}> .
|
|
73
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
74
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
75
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
76
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
77
|
+
|
|
78
|
+
dt:Variable a owl:Class .
|
|
79
|
+
dt:TokenSymbol a owl:Class .
|
|
80
|
+
dt:Declaration a owl:Class .
|
|
81
|
+
dt:Condition a owl:Class .
|
|
82
|
+
dt:Coordinate a owl:Class .
|
|
83
|
+
dt:Tier a owl:Class .
|
|
84
|
+
dt:Visibility a owl:Class .
|
|
85
|
+
dt:Derivation a owl:Class .
|
|
86
|
+
|
|
87
|
+
dt:ofSymbol a owl:ObjectProperty ; rdfs:range dt:TokenSymbol .
|
|
88
|
+
dt:tier a owl:ObjectProperty ; rdfs:range dt:Tier .
|
|
89
|
+
dt:visibility a owl:ObjectProperty ; rdfs:range dt:Visibility .
|
|
90
|
+
dt:declaredAt a owl:ObjectProperty ; rdfs:range dt:Declaration .
|
|
91
|
+
dt:under a owl:ObjectProperty ; rdfs:range dt:Condition .
|
|
92
|
+
dt:emits a owl:DatatypeProperty ; rdfs:range xsd:string .
|
|
93
|
+
dt:at a owl:DatatypeProperty ; rdfs:range xsd:string .
|
|
94
|
+
dt:alsoAt a owl:ObjectProperty ; rdfs:range dt:Coordinate .
|
|
95
|
+
dt:derives a owl:ObjectProperty ; rdfs:range dt:Derivation .
|
|
96
|
+
dt:references a owl:ObjectProperty ; rdfs:range rdf:List .
|
|
97
|
+
dt:selectsCoordinate a owl:ObjectProperty ; rdfs:range dt:Coordinate .
|
|
98
|
+
dt-web:selector a owl:DatatypeProperty ; rdfs:range xsd:string .
|
|
99
|
+
dt-web:inAtRule a owl:ObjectProperty ; rdfs:range rdf:List .
|
|
100
|
+
|
|
101
|
+
dt:tier.semantic a dt:Tier .
|
|
102
|
+
dt:tier.derived a dt:Tier .
|
|
103
|
+
dt:visibility.public a dt:Visibility .
|
|
104
|
+
dt:visibility.internal a dt:Visibility .
|
|
105
|
+
dt:coordinate.mode.dark a dt:Coordinate .
|
|
106
|
+
dt:coordinate.criticality.success a dt:Coordinate .
|
|
107
|
+
dt:derivation.channel-modifier a dt:Derivation .
|
|
108
|
+
|
|
109
|
+
dt:color.text a dt:TokenSymbol ; rdfs:label "color.text" .
|
|
110
|
+
dt:typography.weight.semiBold a dt:TokenSymbol ;
|
|
111
|
+
rdfs:label "typography.weight.semiBold" .
|
|
112
|
+
|
|
113
|
+
# — The conditions ————————————————————————————————————————————————————————
|
|
114
|
+
# One at-rule, the ordinary case.
|
|
115
|
+
<${DT}s4/web/cond/root> a dt:Condition ;
|
|
116
|
+
dt-web:selector ":root" ;
|
|
117
|
+
dt-web:inAtRule ( "@layer ds.tokens" ) .
|
|
118
|
+
# TWO at-rules, nested: this is what the list walk is for. Reading the head
|
|
119
|
+
# alone would lose the inner layer and nobody would notice on a corpus where
|
|
120
|
+
# one condition in thirty-one nests.
|
|
121
|
+
<${DT}s4/web/cond/success> a dt:Condition ;
|
|
122
|
+
dt-web:selector ".success" ;
|
|
123
|
+
dt-web:inAtRule ( "@media (min-width: 40em)" "@layer ds.modifiers" ) ;
|
|
124
|
+
dt:selectsCoordinate dt:coordinate.criticality.success .
|
|
125
|
+
|
|
126
|
+
# — The variables ——————————————————————————————————————————————————————————
|
|
127
|
+
<${DT}s4/web/--color-text> a dt:Variable ;
|
|
128
|
+
rdfs:label "color-text" ;
|
|
129
|
+
dt:ofSymbol dt:color.text ;
|
|
130
|
+
dt:tier dt:tier.semantic ;
|
|
131
|
+
dt:visibility dt:visibility.public ;
|
|
132
|
+
dt:declaredAt [
|
|
133
|
+
a dt:Declaration ;
|
|
134
|
+
dt:under <${DT}s4/web/cond/root> ;
|
|
135
|
+
dt:emits "light-dark(black, white)" ;
|
|
136
|
+
dt:at "tokens.css:12" ;
|
|
137
|
+
dt:alsoAt dt:coordinate.mode.dark
|
|
138
|
+
] .
|
|
139
|
+
|
|
140
|
+
# Stands for NOTHING — one of the population a symbol-keyed surface cannot
|
|
141
|
+
# reach, and the middle hop of the walk below.
|
|
142
|
+
<${DT}s4/web/--color-text-success> a dt:Variable ;
|
|
143
|
+
rdfs:label "color-text-success" ;
|
|
144
|
+
dt:tier dt:tier.semantic ;
|
|
145
|
+
dt:visibility dt:visibility.internal ;
|
|
146
|
+
dt:declaredAt [
|
|
147
|
+
a dt:Declaration ;
|
|
148
|
+
dt:under <${DT}s4/web/cond/root> ;
|
|
149
|
+
dt:emits "var(--color-text)" ;
|
|
150
|
+
dt:at "tokens.css:20" ;
|
|
151
|
+
dt:references ( <${DT}s4/web/--color-text> )
|
|
152
|
+
] .
|
|
153
|
+
|
|
154
|
+
# Also stands for nothing, and is where the walk starts.
|
|
155
|
+
<${DT}s4/web/--modifier-color-text> a dt:Variable ;
|
|
156
|
+
rdfs:label "modifier-color-text" ;
|
|
157
|
+
dt:tier dt:tier.derived ;
|
|
158
|
+
dt:visibility dt:visibility.internal ;
|
|
159
|
+
dt:declaredAt [
|
|
160
|
+
a dt:Declaration ;
|
|
161
|
+
dt:under <${DT}s4/web/cond/success> ;
|
|
162
|
+
dt:emits "var(--color-text-success)" ;
|
|
163
|
+
dt:at "modifiers.css:7" ;
|
|
164
|
+
dt:derives dt:derivation.channel-modifier ;
|
|
165
|
+
dt:references ( <${DT}s4/web/--color-text-success> )
|
|
166
|
+
] .
|
|
167
|
+
|
|
168
|
+
# The second spelling of a symbol another variable already stands for.
|
|
169
|
+
<${DT}s4/web/--typography-weight-semiBold> a dt:Variable ;
|
|
170
|
+
rdfs:label "typography-weight-semiBold" ;
|
|
171
|
+
dt:ofSymbol dt:typography.weight.semiBold ;
|
|
172
|
+
dt:tier dt:tier.semantic ;
|
|
173
|
+
dt:visibility dt:visibility.public .
|
|
174
|
+
<${DT}s4/web/--typography-weight-semi-bold> a dt:Variable ;
|
|
175
|
+
rdfs:label "typography-weight-semi-bold" ;
|
|
176
|
+
dt:ofSymbol dt:typography.weight.semiBold ;
|
|
177
|
+
dt:tier dt:tier.semantic ;
|
|
178
|
+
dt:visibility dt:visibility.public .
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
const verbs = () =>
|
|
182
|
+
compilePack(
|
|
183
|
+
variablePack,
|
|
184
|
+
distributionSource("pragma.conf.ts"),
|
|
185
|
+
DEFAULT_PREFIX_MAP,
|
|
186
|
+
);
|
|
187
|
+
const verb = (label: string) =>
|
|
188
|
+
verbs().find((v) => verbKey(v.path) === `variable ${label}`) as VerbSpec;
|
|
189
|
+
|
|
190
|
+
let rt: PragmaRuntime;
|
|
191
|
+
beforeAll(async () => {
|
|
192
|
+
({ rt } = await buildFixtureRuntime({ ttl: TTL, prefixes: PREFIXES }));
|
|
193
|
+
});
|
|
194
|
+
afterAll(async () => {
|
|
195
|
+
(await rt.store.get()).store.dispose();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const page = (label: string, params: Record<string, unknown> = {}) =>
|
|
199
|
+
verb(label).run(params, rt) as Promise<PackPage>;
|
|
200
|
+
|
|
201
|
+
async function rows(
|
|
202
|
+
label: string,
|
|
203
|
+
params: Record<string, unknown> = {},
|
|
204
|
+
): Promise<Record<string, string>[]> {
|
|
205
|
+
return [...(await page(label, params)).rows];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function lookupAll(...names: string[]): Promise<LookupOutput> {
|
|
209
|
+
return (await verb("lookup").run({ name: names }, rt)) as LookupOutput;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function lookup(name: string): Promise<Record<string, unknown>> {
|
|
213
|
+
const out = await lookupAll(name);
|
|
214
|
+
expect(out.errors).toEqual([]);
|
|
215
|
+
return out.results.at(0) as Record<string, unknown>;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function children(
|
|
219
|
+
entity: Record<string, unknown>,
|
|
220
|
+
name: string,
|
|
221
|
+
): Record<string, string>[] {
|
|
222
|
+
return (entity[name] ?? []) as Record<string, string>[];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
describe("variable list — the platform names, and what they stand for (PROTECTED)", () => {
|
|
226
|
+
it("publishes the CSS name WITHOUT its leading dashes", async () => {
|
|
227
|
+
// The whole reason the noun is addressable. Commander reads a leading `--`
|
|
228
|
+
// as an option, so a row publishing `--color-text` would name something no
|
|
229
|
+
// caller could pass as a positional.
|
|
230
|
+
const names = (await rows("list")).map((row) => row.name);
|
|
231
|
+
expect(names).toEqual([
|
|
232
|
+
"color-text",
|
|
233
|
+
"color-text-success",
|
|
234
|
+
"modifier-color-text",
|
|
235
|
+
"typography-weight-semi-bold",
|
|
236
|
+
"typography-weight-semiBold",
|
|
237
|
+
]);
|
|
238
|
+
expect(names.some((name) => name.startsWith("--"))).toBe(false);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("reads the platform out of the IRI base rather than an assertion", async () => {
|
|
242
|
+
// Nothing asserts the platform; the file identity in the IRI is the only
|
|
243
|
+
// statement of it, which is why it is a filter and not a noun.
|
|
244
|
+
expect(new Set((await rows("list")).map((row) => row.platform))).toEqual(
|
|
245
|
+
new Set(["web"]),
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("publishes a row for a variable standing for NO symbol", async () => {
|
|
250
|
+
// The measurement the second noun exists for: a symbol-keyed surface
|
|
251
|
+
// cannot address these at all.
|
|
252
|
+
const orphan = (await rows("list")).find(
|
|
253
|
+
(row) => row.name === "modifier-color-text",
|
|
254
|
+
);
|
|
255
|
+
expect(orphan).toBeDefined();
|
|
256
|
+
expect(orphan?.symbol).toBeUndefined();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("names tier and visibility by their own local names", async () => {
|
|
260
|
+
const byName = new Map((await rows("list")).map((r) => [r.name, r]));
|
|
261
|
+
expect(byName.get("color-text")).toMatchObject({
|
|
262
|
+
symbol: "color.text",
|
|
263
|
+
tier: "semantic",
|
|
264
|
+
visibility: "public",
|
|
265
|
+
});
|
|
266
|
+
expect(byName.get("modifier-color-text")).toMatchObject({
|
|
267
|
+
tier: "derived",
|
|
268
|
+
visibility: "internal",
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it("collects EVERY coordinate a variable reaches, from both sources", async () => {
|
|
273
|
+
const byName = new Map((await rows("list")).map((r) => [r.name, r]));
|
|
274
|
+
// From the declaration's own `dt:alsoAt`.
|
|
275
|
+
expect(byName.get("color-text")?.coordinate).toBe("mode.dark");
|
|
276
|
+
// From the CONDITION the declaration sits under.
|
|
277
|
+
expect(byName.get("modifier-color-text")?.coordinate).toBe(
|
|
278
|
+
"criticality.success",
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it("--coordinate set-matches, so a multi-coordinate cell answers for each", async () => {
|
|
283
|
+
expect(
|
|
284
|
+
(await rows("list", { coordinate: "mode.dark" })).map((r) => r.name),
|
|
285
|
+
).toEqual(["color-text"]);
|
|
286
|
+
expect(
|
|
287
|
+
(await rows("list", { coordinate: "criticality.success" })).map(
|
|
288
|
+
(r) => r.name,
|
|
289
|
+
),
|
|
290
|
+
).toEqual(["modifier-color-text"]);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("--symbol, --tier, --visibility and --platform each narrow", async () => {
|
|
294
|
+
expect(
|
|
295
|
+
(await rows("list", { symbol: "color.text" })).map((r) => r.name),
|
|
296
|
+
).toEqual(["color-text"]);
|
|
297
|
+
expect(
|
|
298
|
+
(await rows("list", { tier: "derived" })).map((r) => r.name),
|
|
299
|
+
).toEqual(["modifier-color-text"]);
|
|
300
|
+
expect(
|
|
301
|
+
(await rows("list", { visibility: "internal" })).map((r) => r.name),
|
|
302
|
+
).toEqual(["color-text-success", "modifier-color-text"]);
|
|
303
|
+
expect(await rows("list", { platform: "web" })).toHaveLength(5);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("--symbol answers BOTH spellings that stand for one symbol", async () => {
|
|
307
|
+
// A symbol can have more than one variable, which is the fact that makes
|
|
308
|
+
// this noun necessary rather than a flag on `token`.
|
|
309
|
+
expect(
|
|
310
|
+
(await rows("list", { symbol: "typography.weight.semiBold" })).map(
|
|
311
|
+
(r) => r.name,
|
|
312
|
+
),
|
|
313
|
+
).toEqual(["typography-weight-semi-bold", "typography-weight-semiBold"]);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it("refuses a tier the graph does not admit, naming the ones it does", async () => {
|
|
317
|
+
await expect(page("list", { tier: "cosmic" })).rejects.toMatchObject({
|
|
318
|
+
code: "INVALID_INPUT",
|
|
319
|
+
validOptions: ["derived", "semantic"],
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
describe("variable lookup — every place it is declared (PROTECTED)", () => {
|
|
325
|
+
it("resolves the dash-stripped name the list publishes", async () => {
|
|
326
|
+
expect(await lookup("color-text")).toMatchObject({
|
|
327
|
+
name: "color-text",
|
|
328
|
+
symbol: "color.text",
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it("each spelling answers ONE row, because each carries its own label", async () => {
|
|
333
|
+
// Both stand for the same symbol, and both are addressable — the surface
|
|
334
|
+
// shows both because the ontology holds both.
|
|
335
|
+
const out = await lookupAll(
|
|
336
|
+
"typography-weight-semiBold",
|
|
337
|
+
"typography-weight-semi-bold",
|
|
338
|
+
);
|
|
339
|
+
expect(out.errors).toEqual([]);
|
|
340
|
+
expect(out.results).toHaveLength(2);
|
|
341
|
+
for (const result of out.results) {
|
|
342
|
+
expect(result).toMatchObject({ symbol: "typography.weight.semiBold" });
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
it("shows the selector, the emitted value, the source and the coordinate", async () => {
|
|
347
|
+
const declarations = children(await lookup("color-text"), "declarations");
|
|
348
|
+
expect(declarations).toHaveLength(1);
|
|
349
|
+
expect(declarations[0]).toMatchObject({
|
|
350
|
+
selector: ":root",
|
|
351
|
+
emits: "light-dark(black, white)",
|
|
352
|
+
at: "tokens.css:12",
|
|
353
|
+
inAtRule: "@layer ds.tokens",
|
|
354
|
+
});
|
|
355
|
+
// `dt:at` is the SOURCE LOCATION and `dt:alsoAt` the coordinate. The two
|
|
356
|
+
// read alike and mean different things, so both are asserted.
|
|
357
|
+
expect(declarations[0]?.alsoAt).toContain("mode.dark");
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it("walks the at-rule LIST, so a nested stack contributes each level", async () => {
|
|
361
|
+
// The one shape a head-only read gets wrong. Two rows for one declaration
|
|
362
|
+
// here is the stack, read outermost-in.
|
|
363
|
+
const declarations = children(
|
|
364
|
+
await lookup("modifier-color-text"),
|
|
365
|
+
"declarations",
|
|
366
|
+
);
|
|
367
|
+
expect(declarations.map((d) => d.inAtRule).sort()).toEqual([
|
|
368
|
+
"@layer ds.modifiers",
|
|
369
|
+
"@media (min-width: 40em)",
|
|
370
|
+
]);
|
|
371
|
+
expect(new Set(declarations.map((d) => d.selector))).toEqual(
|
|
372
|
+
new Set([".success"]),
|
|
373
|
+
);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it("shows the derivation that computed a derived variable", async () => {
|
|
377
|
+
const declarations = children(
|
|
378
|
+
await lookup("modifier-color-text"),
|
|
379
|
+
"declarations",
|
|
380
|
+
);
|
|
381
|
+
expect(declarations[0]?.derives).toContain("channel-modifier");
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it("a miss carries did-you-mean suggestions", async () => {
|
|
385
|
+
let caught: unknown;
|
|
386
|
+
try {
|
|
387
|
+
await verb("lookup").run({ name: ["color-txt"] }, rt);
|
|
388
|
+
} catch (error) {
|
|
389
|
+
caught = error;
|
|
390
|
+
}
|
|
391
|
+
expect(caught).toMatchObject({ code: "ENTITY_NOT_FOUND" });
|
|
392
|
+
expect((caught as { suggestions: string[] }).suggestions).toContain(
|
|
393
|
+
"color-text",
|
|
394
|
+
);
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
describe("variable chain — the transitive walk (PROTECTED)", () => {
|
|
399
|
+
it("reaches a symbol THROUGH a variable that stands for nothing", async () => {
|
|
400
|
+
// Three hops, and the middle one stands for no symbol. A one-hop join
|
|
401
|
+
// would answer nothing here and be silently wrong rather than empty.
|
|
402
|
+
expect(await rows("chain", { variable: "modifier-color-text" })).toEqual([
|
|
403
|
+
{
|
|
404
|
+
variable: "modifier-color-text",
|
|
405
|
+
reaches: "color-text",
|
|
406
|
+
symbol: "color.text",
|
|
407
|
+
},
|
|
408
|
+
]);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it("answers from every starting variable that reaches a symbol", async () => {
|
|
412
|
+
expect(
|
|
413
|
+
(await rows("chain")).map((row) => [row.variable, row.symbol]),
|
|
414
|
+
).toEqual([
|
|
415
|
+
["color-text-success", "color.text"],
|
|
416
|
+
["modifier-color-text", "color.text"],
|
|
417
|
+
]);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it("--symbol answers the walks that REACH one symbol", async () => {
|
|
421
|
+
expect(await rows("chain", { symbol: "color.text" })).toHaveLength(2);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it("a variable that references nothing has no walk, calmly", async () => {
|
|
425
|
+
const answered = await page("chain", { variable: "color-text" });
|
|
426
|
+
expect(answered.rows).toEqual([]);
|
|
427
|
+
expect(answered.nextAfter).toBeUndefined();
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
describe("variable — the name literal is the whole grammar (PROTECTED)", () => {
|
|
432
|
+
it("every published name resolves through lookup, verbatim", async () => {
|
|
433
|
+
const names = (await rows("list")).map((row) => row.name);
|
|
434
|
+
const out = await lookupAll(...names);
|
|
435
|
+
expect(out.errors).toEqual([]);
|
|
436
|
+
expect(out.results.map((r) => (r as { name: string }).name).sort()).toEqual(
|
|
437
|
+
[...names].sort(),
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it("the dash-stripping is injective over this graph", async () => {
|
|
442
|
+
// Asserted here over an authored graph and over the WHOLE shipped corpus in
|
|
443
|
+
// `tokenGraph.shipped.exec.test.ts`: if two variables ever stripped to one
|
|
444
|
+
// name, a lookup would answer one of them and silently hide the other.
|
|
445
|
+
const names = (await rows("list")).map((row) => row.name);
|
|
446
|
+
expect(new Set(names).size).toBe(names.length);
|
|
447
|
+
});
|
|
448
|
+
});
|
package/src/constants.ts
CHANGED
|
@@ -41,6 +41,13 @@ const ISSUES_URL = identity.issuesUrl;
|
|
|
41
41
|
/** Semver version string read from package.json. */
|
|
42
42
|
const VERSION: string = pkg.version;
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* The Node.js range this distribution supports, read from `engines.node` in
|
|
46
|
+
* package.json so the manifest stays the single statement of the floor. The
|
|
47
|
+
* doctor check quotes it, and `isSupportedNodeVersion` is pinned to it by test.
|
|
48
|
+
*/
|
|
49
|
+
const SUPPORTED_NODE_RANGE: string = pkg.engines.node;
|
|
50
|
+
|
|
44
51
|
/**
|
|
45
52
|
* The project config filename the walker looks for and every surface that
|
|
46
53
|
* quotes it — diagnostics, the onboarding note, the generated reference, the
|
|
@@ -116,5 +123,6 @@ export {
|
|
|
116
123
|
PROGRAM_LOGO,
|
|
117
124
|
PROJECT_CONFIG_FILENAME,
|
|
118
125
|
RECOVERY_CLI_PREFIX,
|
|
126
|
+
SUPPORTED_NODE_RANGE,
|
|
119
127
|
VERSION,
|
|
120
128
|
};
|
|
@@ -351,12 +351,17 @@ const STRUCTURE = [
|
|
|
351
351
|
at: "pragma block lookup --<TAB>",
|
|
352
352
|
words: ["pragma", "block", "lookup", "--"],
|
|
353
353
|
cword: 3,
|
|
354
|
+
// The globals, plus the ONE flag this verb declares: `--tier`, which the
|
|
355
|
+
// block story earns by declaring a tier hierarchy. A verb's own flags are
|
|
356
|
+
// in the inlined table like the globals, so offering it still execs
|
|
357
|
+
// nothing.
|
|
354
358
|
offers: [
|
|
355
359
|
"--detail",
|
|
356
360
|
"--format",
|
|
357
361
|
"--help",
|
|
358
362
|
"--no-headers",
|
|
359
363
|
"--quiet",
|
|
364
|
+
"--tier",
|
|
360
365
|
"--verbose",
|
|
361
366
|
],
|
|
362
367
|
},
|
|
@@ -389,6 +394,7 @@ const NOUNS = [
|
|
|
389
394
|
"tier",
|
|
390
395
|
"token",
|
|
391
396
|
"upgrade",
|
|
397
|
+
"variable",
|
|
392
398
|
"version",
|
|
393
399
|
] as const;
|
|
394
400
|
|
|
@@ -401,17 +407,16 @@ describe("generated bash — the live grammar's script", () => {
|
|
|
401
407
|
expect(parses("bash", fixture.bash)).toBe(0);
|
|
402
408
|
});
|
|
403
409
|
|
|
404
|
-
it.each(STRUCTURE)(
|
|
405
|
-
|
|
406
|
-
cword,
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
});
|
|
410
|
+
it.each(STRUCTURE)(
|
|
411
|
+
"answers $at from the inlined table, execing nothing",
|
|
412
|
+
({ words, cword, offers }) => {
|
|
413
|
+
const { reply, calls } = driveBash(live.bash, words, cword);
|
|
414
|
+
expect(reply.sort()).toEqual([...offers].sort());
|
|
415
|
+
// The structural tier is the point of a generated script: a TAB here
|
|
416
|
+
// must never cost a process spawn, nor depend on a pack being built.
|
|
417
|
+
expect(calls).toEqual([]);
|
|
418
|
+
},
|
|
419
|
+
);
|
|
415
420
|
|
|
416
421
|
it("hands __complete the protocol argv and OFFERS what it answers", () => {
|
|
417
422
|
// The last mile, and the only assertion that covers it: everything else
|
|
@@ -480,17 +485,14 @@ describe.skipIf(!hasShell("zsh"))(
|
|
|
480
485
|
// test below, and the other five are asserted as the exact sets zsh really
|
|
481
486
|
// returns. An `arrayContaining` here would be blind to the regression this
|
|
482
487
|
// file exists to catch — a slot that offers EXTRA, wrong candidates.
|
|
483
|
-
it.each(
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
expect(reply.sort()).toEqual([...offers].sort());
|
|
492
|
-
expect(calls).toEqual([]);
|
|
493
|
-
});
|
|
488
|
+
it.each(STRUCTURE.filter((row) => row.at !== "pragma co"))(
|
|
489
|
+
"OFFERS exactly the candidates for $at, execing nothing",
|
|
490
|
+
({ words, cword, offers }) => {
|
|
491
|
+
const { reply, calls } = driveZsh(live.zsh, words, cword);
|
|
492
|
+
expect(reply.sort()).toEqual([...offers].sort());
|
|
493
|
+
expect(calls).toEqual([]);
|
|
494
|
+
},
|
|
495
|
+
);
|
|
494
496
|
|
|
495
497
|
it("offers the WHOLE noun table for a partial noun, leaving the filtering to zsh", () => {
|
|
496
498
|
const { reply, calls } = driveZsh(live.zsh, ["pragma", "co"], 1);
|
|
@@ -552,14 +554,14 @@ describe.skipIf(!hasShell("fish"))(
|
|
|
552
554
|
line: "pragma --format=",
|
|
553
555
|
offers: ["--format=json", "--format=llm", "--format=plain"],
|
|
554
556
|
},
|
|
555
|
-
])(
|
|
556
|
-
line,
|
|
557
|
-
offers
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
557
|
+
])(
|
|
558
|
+
"answers $line from the inlined rules, execing nothing",
|
|
559
|
+
({ line, offers }) => {
|
|
560
|
+
const { reply, calls } = driveFish(live.fish, line);
|
|
561
|
+
expect(reply.sort()).toEqual([...offers].sort());
|
|
562
|
+
expect(calls).toEqual([]);
|
|
563
|
+
},
|
|
564
|
+
);
|
|
563
565
|
|
|
564
566
|
it("hands __complete the SAME protocol argv bash does, and OFFERS the answer", () => {
|
|
565
567
|
const { reply, calls } = driveFish(
|
|
@@ -44,7 +44,12 @@ describe("defaults — the validated distribution config (pragma.conf.ts)", () =
|
|
|
44
44
|
expect(defaults.colophon?.summary?.length).toBeLessThan(400);
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
// Five packs, and ONE carries a subdirectory. That is load-bearing, not
|
|
48
|
+
// decoration: `@canonical/token-ontology` lives inside a monorepo, and a
|
|
49
|
+
// git source names a repository — without `:packages/token-ontology` the
|
|
50
|
+
// clone would succeed against a root that has no `definitions/` or `data/`
|
|
51
|
+
// and the pack would contribute nothing, silently.
|
|
52
|
+
it("ships the five canonical default packs (all git+https)", () => {
|
|
48
53
|
expect(
|
|
49
54
|
defaults.packs?.map((pack) =>
|
|
50
55
|
typeof pack === "string"
|
|
@@ -60,6 +65,11 @@ describe("defaults — the validated distribution config (pragma.conf.ts)", () =
|
|
|
60
65
|
name: "@canonical/anatomy-dsl",
|
|
61
66
|
source: "git+https://github.com/canonical/anatomy-dsl.git#main",
|
|
62
67
|
},
|
|
68
|
+
{
|
|
69
|
+
name: "@canonical/token-ontology",
|
|
70
|
+
source:
|
|
71
|
+
"git+https://github.com/canonical/design-tokens.git#main:packages/token-ontology",
|
|
72
|
+
},
|
|
63
73
|
{
|
|
64
74
|
name: "@canonical/code-standards",
|
|
65
75
|
source:
|
|
@@ -79,7 +89,11 @@ describe("defaults — the validated distribution config (pragma.conf.ts)", () =
|
|
|
79
89
|
const storyCounts = defaults.packs?.map((pack) =>
|
|
80
90
|
typeof pack === "string" ? 0 : (pack.stories?.length ?? 0),
|
|
81
91
|
);
|
|
82
|
-
|
|
92
|
+
// Six on the design-system pack: block, token, variable, modifier, tier
|
|
93
|
+
// and concept. `token` and `variable` read the token-ontology pack's
|
|
94
|
+
// strata and are declared on this one anyway, because `token consumers`
|
|
95
|
+
// spans both and no single pack's stories could carry the pair.
|
|
96
|
+
expect(storyCounts).toEqual([6, 0, 0, 1, 1]);
|
|
83
97
|
});
|
|
84
98
|
|
|
85
99
|
it("declares no removed field — the validator would refuse to load one", () => {
|
|
@@ -98,6 +112,11 @@ describe("defaults — the validated distribution config (pragma.conf.ts)", () =
|
|
|
98
112
|
// can expand — `cs:` is here for that reader.
|
|
99
113
|
expect(defaults.prefixes).toEqual({
|
|
100
114
|
ds: "https://ds.canonical.com/",
|
|
115
|
+
dt: "https://dt.canonical.com/",
|
|
116
|
+
// Nested under `dt:`, and bound separately because `compactUri` takes the
|
|
117
|
+
// longest match — with only the parent bound, every term of this
|
|
118
|
+
// vocabulary would render as `dt:w3c-tokens/…`.
|
|
119
|
+
"w3c-tokens": "https://dt.canonical.com/w3c-tokens/",
|
|
101
120
|
cs: "http://pragma.canonical.com/codestandards#",
|
|
102
121
|
});
|
|
103
122
|
});
|
|
@@ -59,7 +59,7 @@ describe("readConfig — layering + provenance", () => {
|
|
|
59
59
|
expect(config.tier).toBe("core");
|
|
60
60
|
expect(config.channel).toBe("experimental");
|
|
61
61
|
expect(config.detail).toBe("detailed");
|
|
62
|
-
expect(config.packs).toHaveLength(
|
|
62
|
+
expect(config.packs).toHaveLength(5); // from defaults
|
|
63
63
|
|
|
64
64
|
expect(origins).toMatchObject({
|
|
65
65
|
tier: "project",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import type { OutputFormat } from "../../constants.js";
|
|
11
11
|
import type { PragmaError } from "./PragmaError.js";
|
|
12
12
|
import { serializeError } from "./serialize.js";
|
|
13
|
+
import { formatValidOptions } from "./validOptions.js";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Render a {@link PragmaError} as human-readable plain text for the terminal.
|
|
@@ -35,7 +36,7 @@ function renderErrorPlain(error: PragmaError): string {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
if (error.validOptions && error.validOptions.length > 0) {
|
|
38
|
-
lines.push("",
|
|
39
|
+
lines.push("", formatValidOptions(error.validOptions));
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
if (error.recovery) {
|
|
@@ -73,7 +74,7 @@ function renderErrorLlm(error: PragmaError): string {
|
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
if (error.validOptions && error.validOptions.length > 0) {
|
|
76
|
-
lines.push(
|
|
77
|
+
lines.push(formatValidOptions(error.validOptions));
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
if (error.recovery) {
|