@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
|
@@ -14,17 +14,50 @@
|
|
|
14
14
|
* statically by `capabilities/distribution.ts` and never revalidated at dispatch
|
|
15
15
|
* (`collect.projectStoryTiers`'s default-origin carve-out); this runs for config-
|
|
16
16
|
* and package-declared stories, and in `distribution.test.ts`'s round-trip.
|
|
17
|
+
*
|
|
18
|
+
* That carve-out is exactly why the rest of the compilability rules are NOT
|
|
19
|
+
* here. A rule stated only in this file reaches a third-party author at
|
|
20
|
+
* declaration and the distribution's own stories not at all — they meet it as a
|
|
21
|
+
* first-call CONFIG_ERROR. Those rules live in {@link ./storyRules}, which
|
|
22
|
+
* `compileStoryModule` runs for every tier, and {@link refineListShape} reports
|
|
23
|
+
* them as zod issues so a config author still reads them here, in the same
|
|
24
|
+
* words and at the same paths.
|
|
17
25
|
*/
|
|
18
26
|
|
|
19
27
|
import { z } from "zod";
|
|
20
28
|
import { DETAIL_LEVELS, RECOVERY_CLI_PREFIX } from "../../constants.js";
|
|
21
29
|
import { PragmaError } from "../error/index.js";
|
|
22
|
-
import
|
|
30
|
+
import { listShapeIssues, tierScopeIssues } from "./storyRules.js";
|
|
31
|
+
import {
|
|
32
|
+
type PackDefinition,
|
|
33
|
+
type PackList,
|
|
34
|
+
type PackTierScope,
|
|
35
|
+
RESERVED_STORY_PARAMS,
|
|
36
|
+
} from "./types.js";
|
|
23
37
|
|
|
24
38
|
const NOUN_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
25
39
|
/** Message for {@link NOUN_PATTERN} — third-party authors never see the regex. */
|
|
26
40
|
const NOUN_MESSAGE = 'must be lowercase kebab-case, e.g. "design-token"';
|
|
27
|
-
|
|
41
|
+
/**
|
|
42
|
+
* A filter's parameter name: lowercase-initial, then letters and digits.
|
|
43
|
+
*
|
|
44
|
+
* camelCase is admitted because the PROJECTOR already relies on it —
|
|
45
|
+
* `emitSurface`'s `emitVerb` writes a flag as `--${kebabCase(param)}`, so a
|
|
46
|
+
* param named `channelOf` was always going to become `--channel-of`, and only
|
|
47
|
+
* this gate refused to let anyone declare it. A story wanting a two-word
|
|
48
|
+
* dimension had no spelling at all: `channel-of` fails the pattern too, and
|
|
49
|
+
* `channelof` projects to the unreadable `--channelof`.
|
|
50
|
+
*
|
|
51
|
+
* This is a WIDENING of what an author may declare, matched to what the
|
|
52
|
+
* projector already emits, and it closes a tier split rather than opening one:
|
|
53
|
+
* the distribution's own stories are compiled statically and never revalidated
|
|
54
|
+
* (see `storyRules.ts`), so `pragma.conf.ts` could already declare a param this
|
|
55
|
+
* schema would have refused from a package. The same rule now reaches both.
|
|
56
|
+
*
|
|
57
|
+
* Still lowercase-INITIAL: a leading capital would kebab to a leading dash and
|
|
58
|
+
* Commander would read the flag as empty.
|
|
59
|
+
*/
|
|
60
|
+
const FILTER_PARAM_PATTERN = /^[a-z][a-zA-Z0-9]*$/;
|
|
28
61
|
const FIELD_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
29
62
|
const GRAPHQL_NAME_PATTERN = /^[_A-Za-z][_0-9A-Za-z]*$/;
|
|
30
63
|
/**
|
|
@@ -38,14 +71,22 @@ const GRAPHQL_NAME_PATTERN = /^[_A-Za-z][_0-9A-Za-z]*$/;
|
|
|
38
71
|
const TERM_PATTERN =
|
|
39
72
|
/^(?:[A-Za-z][A-Za-z0-9+.-]*:\/\/[^<>"\s]+|\^?[A-Za-z][\w-]*:[^/<>"\s]+(?:\/\^?[A-Za-z][\w-]*:[^/<>"\s]+)*)$/;
|
|
40
73
|
|
|
41
|
-
/** Params a filter/verb may not claim
|
|
42
|
-
const RESERVED_PARAMS = new Set(
|
|
74
|
+
/** Params a filter/verb may not claim — the kernel's own, from the grammar. */
|
|
75
|
+
const RESERVED_PARAMS = new Set(RESERVED_STORY_PARAMS);
|
|
43
76
|
|
|
44
77
|
/**
|
|
45
78
|
* Whether an author-supplied query is a SELECT (optionally preceded by its own
|
|
46
79
|
* PREFIX lines). Every author query a story declares runs through `runSelect`,
|
|
47
80
|
* which reads `result.bindings` — so a non-SELECT is a shape mismatch caught
|
|
48
81
|
* here rather than downstream.
|
|
82
|
+
*
|
|
83
|
+
* Used for a `vocabulary.query` only, which is run exactly as the author wrote
|
|
84
|
+
* it. A `list.query` gets the real reader instead ({@link ./storyRules}, via
|
|
85
|
+
* `sparql/authorQuery.ts`), because this pattern is NARROWER than what a page
|
|
86
|
+
* can now serve: it admits `PREFIX` lines but not a leading comment, not
|
|
87
|
+
* `BASE`, and not a lower-case `prefix` — all of which the lift handles. A
|
|
88
|
+
* grammar that refuses the shape the kernel supports is the same defect as one
|
|
89
|
+
* that admits the shape it cannot, read from the other side.
|
|
49
90
|
*/
|
|
50
91
|
function isSelectQuery(query: string): boolean {
|
|
51
92
|
return /^\s*(?:PREFIX\s+[^\n]*\n\s*)*SELECT\s/i.test(query);
|
|
@@ -89,6 +130,12 @@ const filterSchema = z
|
|
|
89
130
|
.refine((f) => !f.vocabulary || isSelectQuery(f.vocabulary.query), {
|
|
90
131
|
message: '"vocabulary.query" must be a SPARQL SELECT query',
|
|
91
132
|
});
|
|
133
|
+
// A filter must also say WHAT it admits, one way or the other — it used to be
|
|
134
|
+
// able to say neither and fall back to the values the returned ROWS carried,
|
|
135
|
+
// which a page makes actively wrong. That rule is a compilability rule rather
|
|
136
|
+
// than a shape rule, so it is checked in `storyRules.ts` (which the
|
|
137
|
+
// distribution's own stories reach too) and reported from `refineListShape`
|
|
138
|
+
// below at the same `…filters.N` path it always used.
|
|
92
139
|
|
|
93
140
|
const searchSchema = z
|
|
94
141
|
.object({
|
|
@@ -146,6 +193,7 @@ const fieldSchema = z
|
|
|
146
193
|
|
|
147
194
|
const sectionSchema = fieldSchema.extend({
|
|
148
195
|
kind: z.enum(["field", "code"]).optional(),
|
|
196
|
+
note: z.string().optional(),
|
|
149
197
|
});
|
|
150
198
|
|
|
151
199
|
const nestedExpandSchema = z
|
|
@@ -174,6 +222,7 @@ const expandFieldSchema = z
|
|
|
174
222
|
property: term,
|
|
175
223
|
label: z.string().optional(),
|
|
176
224
|
graphqlField: graphqlName.optional(),
|
|
225
|
+
blankWhenSelf: z.literal(true).optional(),
|
|
177
226
|
})
|
|
178
227
|
.strict();
|
|
179
228
|
|
|
@@ -187,6 +236,12 @@ const expandSchema = z
|
|
|
187
236
|
select: z.array(z.union([nestedExpandSchema, expandFieldSchema])).min(1),
|
|
188
237
|
showWhenEmpty: z.boolean().optional(),
|
|
189
238
|
level: z.string().optional(),
|
|
239
|
+
note: z.string().optional(),
|
|
240
|
+
orderBy: z.array(fieldName).min(1).optional(),
|
|
241
|
+
// Only "sparql": see PackExpand.source — the SPARQL lane is every lookup's
|
|
242
|
+
// default, so an expand opting INTO GraphQL would ask for a document the
|
|
243
|
+
// lookup never builds.
|
|
244
|
+
source: z.literal("sparql").optional(),
|
|
190
245
|
})
|
|
191
246
|
.strict();
|
|
192
247
|
|
|
@@ -247,6 +302,24 @@ const scopeWeightSchema = z
|
|
|
247
302
|
})
|
|
248
303
|
.strict();
|
|
249
304
|
|
|
305
|
+
/**
|
|
306
|
+
* The declared tier hierarchy (`PackTierScope`) that SCOPES a noun's reads.
|
|
307
|
+
*
|
|
308
|
+
* Four terms, no options: the class whose instances are the tiers, the edge
|
|
309
|
+
* from an entity to its tier, the tier property whose `/`-separated name IS the
|
|
310
|
+
* hierarchy, and the base tier every scope keeps. Everything else about the
|
|
311
|
+
* scope — the default, the ancestors, the escape — is the kernel's rule, not a
|
|
312
|
+
* knob, so a pack cannot declare a noun that scopes differently from the rest.
|
|
313
|
+
*/
|
|
314
|
+
const tierScopeSchema = z
|
|
315
|
+
.object({
|
|
316
|
+
type: term,
|
|
317
|
+
via: term,
|
|
318
|
+
by: term,
|
|
319
|
+
base: term.optional(),
|
|
320
|
+
})
|
|
321
|
+
.strict();
|
|
322
|
+
|
|
250
323
|
const lookupSchema = z
|
|
251
324
|
.object({
|
|
252
325
|
source: z.enum(["sparql", "graphql"]).optional(),
|
|
@@ -277,6 +350,7 @@ const definitionSchema = z
|
|
|
277
350
|
verbs: z.array(verbSchema).min(1).optional(),
|
|
278
351
|
lookup: lookupSchema.optional(),
|
|
279
352
|
colophon: z.string().optional(),
|
|
353
|
+
tierScope: tierScopeSchema.optional(),
|
|
280
354
|
})
|
|
281
355
|
.strict()
|
|
282
356
|
.superRefine((def, ctx) => {
|
|
@@ -286,17 +360,19 @@ const definitionSchema = z
|
|
|
286
360
|
message: 'a pack must declare at least one of "list" or "lookup".',
|
|
287
361
|
});
|
|
288
362
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
message: '"list.query" must be a SPARQL SELECT query.',
|
|
293
|
-
path: ["list", "query"],
|
|
294
|
-
});
|
|
295
|
-
}
|
|
363
|
+
// "must be a SELECT" for a `list.query` is `refineListShape`'s to say, and
|
|
364
|
+
// it says it from the reader that actually splits the query — which admits
|
|
365
|
+
// the prologue shapes this file's own pattern does not.
|
|
296
366
|
refineVerbNames(def, ctx);
|
|
297
|
-
if (def.list)
|
|
367
|
+
if (def.list) {
|
|
368
|
+
refineFilterParams(def.list.filters, ["list"], ctx);
|
|
369
|
+
refineListShape(def.list, ["list"], ctx);
|
|
370
|
+
refineTierScope(def.tierScope, def.list, ["list"], ctx);
|
|
371
|
+
}
|
|
298
372
|
for (const [index, verb] of (def.verbs ?? []).entries()) {
|
|
299
373
|
refineFilterParams(verb.filters, ["verbs", index], ctx);
|
|
374
|
+
refineListShape(verb, ["verbs", index], ctx);
|
|
375
|
+
refineTierScope(def.tierScope, verb, ["verbs", index], ctx);
|
|
300
376
|
}
|
|
301
377
|
if (def.lookup) refineLookup(def.lookup, ctx);
|
|
302
378
|
});
|
|
@@ -360,6 +436,60 @@ function refineFilterParams(
|
|
|
360
436
|
}
|
|
361
437
|
}
|
|
362
438
|
|
|
439
|
+
/**
|
|
440
|
+
* The COMPILABILITY rules for one list-shaped body, reported as zod issues.
|
|
441
|
+
*
|
|
442
|
+
* The rules themselves live in {@link ./storyRules}, deliberately outside this
|
|
443
|
+
* module: zod runs only for config- and package-declared stories, and a rule
|
|
444
|
+
* whose violation is a CONFIG_ERROR has to reach the distribution's own stories
|
|
445
|
+
* too — which it does by being checked in `compileStoryModule`, the door every
|
|
446
|
+
* tier comes through. Stated in two places it would be two rules; delegated, a
|
|
447
|
+
* third-party author and the distribution read the same sentence.
|
|
448
|
+
*
|
|
449
|
+
* What they cover: a query a page cannot wrap (its own prologue is lifted, but
|
|
450
|
+
* a `FROM` clause and an unreadable projection cannot be), a filter or search
|
|
451
|
+
* over a variable the query does not project, a filtered query shadowing the
|
|
452
|
+
* generated variable prefix, and a filter that declares neither `values` nor a
|
|
453
|
+
* `vocabulary`. None of them could be checked while filters were predicates
|
|
454
|
+
* over returned rows: a variable that was not there simply matched nothing,
|
|
455
|
+
* silently, with exit 0.
|
|
456
|
+
*/
|
|
457
|
+
function refineListShape(
|
|
458
|
+
shape: PackList,
|
|
459
|
+
path: readonly (string | number)[],
|
|
460
|
+
ctx: z.RefinementCtx,
|
|
461
|
+
): void {
|
|
462
|
+
for (const issue of listShapeIssues(shape, path)) {
|
|
463
|
+
ctx.addIssue({
|
|
464
|
+
code: z.ZodIssueCode.custom,
|
|
465
|
+
message: issue.message,
|
|
466
|
+
path: [...issue.path],
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* The tier-scope rules, stated by the same reader the compiler uses.
|
|
473
|
+
*
|
|
474
|
+
* Delegated for the reason every other compilability rule is: a third-party
|
|
475
|
+
* author must be refused where they declare, and the distribution's own stories
|
|
476
|
+
* — which never see zod — must be held to the same rule at compile time.
|
|
477
|
+
*/
|
|
478
|
+
function refineTierScope(
|
|
479
|
+
tierScope: PackTierScope | undefined,
|
|
480
|
+
shape: PackList,
|
|
481
|
+
path: readonly (string | number)[],
|
|
482
|
+
ctx: z.RefinementCtx,
|
|
483
|
+
): void {
|
|
484
|
+
for (const issue of tierScopeIssues(tierScope, shape, path)) {
|
|
485
|
+
ctx.addIssue({
|
|
486
|
+
code: z.ZodIssueCode.custom,
|
|
487
|
+
message: issue.message,
|
|
488
|
+
path: [...issue.path],
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
363
493
|
/** Cross-field lookup rules — the source rule (§3, F8). */
|
|
364
494
|
function refineLookup(
|
|
365
495
|
lookup: z.infer<typeof lookupSchema>,
|
|
@@ -446,14 +576,51 @@ function refineLookup(
|
|
|
446
576
|
path: ["lookup"],
|
|
447
577
|
});
|
|
448
578
|
}
|
|
579
|
+
// The EXPAND's lane, not the lookup's: an expand may opt out of a graphql
|
|
580
|
+
// lookup's document, and the three rules below are rules about the lane
|
|
581
|
+
// that will actually resolve it.
|
|
582
|
+
const expandSource = expand.source ?? source;
|
|
449
583
|
for (const entry of expand.select) {
|
|
450
|
-
if ("relation" in entry &&
|
|
584
|
+
if ("relation" in entry && expandSource !== "graphql") {
|
|
451
585
|
ctx.addIssue({
|
|
452
586
|
code: z.ZodIssueCode.custom,
|
|
453
587
|
message: `expand "${expand.name}" nests a relation, which requires lookup.source "graphql" (the SPARQL expand sub-SELECT is single-hop).`,
|
|
454
588
|
path: ["lookup"],
|
|
455
589
|
});
|
|
456
590
|
}
|
|
591
|
+
if (
|
|
592
|
+
"blankWhenSelf" in entry &&
|
|
593
|
+
entry.blankWhenSelf &&
|
|
594
|
+
expandSource !== "sparql"
|
|
595
|
+
) {
|
|
596
|
+
ctx.addIssue({
|
|
597
|
+
code: z.ZodIssueCode.custom,
|
|
598
|
+
message: `expand "${expand.name}" field "${entry.name}" sets "blankWhenSelf", which only the SPARQL lane can express — set the expand's "source" to "sparql".`,
|
|
599
|
+
path: ["lookup"],
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (expand.orderBy !== undefined) {
|
|
604
|
+
if (expandSource !== "sparql") {
|
|
605
|
+
ctx.addIssue({
|
|
606
|
+
code: z.ZodIssueCode.custom,
|
|
607
|
+
message: `expand "${expand.name}" declares "orderBy", which only the SPARQL lane can express — a GraphQL collection's order is the schema's. Set the expand's "source" to "sparql".`,
|
|
608
|
+
path: ["lookup"],
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
// A name that no select entry declares would order by an unbound
|
|
612
|
+
// variable, which SPARQL permits and which silently orders by nothing —
|
|
613
|
+
// so it is refused where it is written rather than ignored at runtime.
|
|
614
|
+
const selected = new Set(expand.select.map((entry) => entry.name));
|
|
615
|
+
for (const name of expand.orderBy) {
|
|
616
|
+
if (!selected.has(name)) {
|
|
617
|
+
ctx.addIssue({
|
|
618
|
+
code: z.ZodIssueCode.custom,
|
|
619
|
+
message: `expand "${expand.name}" orders by "${name}", which its "select" does not declare.`,
|
|
620
|
+
path: ["lookup"],
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
}
|
|
457
624
|
}
|
|
458
625
|
}
|
|
459
626
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The author-query reader: the prologue it lifts, the projection it reproduces,
|
|
3
|
+
* and the shapes it refuses BY NAME.
|
|
4
|
+
*
|
|
5
|
+
* The last of those is the point. Every refusal here used to be the same
|
|
6
|
+
* sentence — "project its variables by name" — whether the reader had found
|
|
7
|
+
* `SELECT *`, a comment where it wanted a variable, or a `FROM` clause. Two of
|
|
8
|
+
* those three were wrong, and a wrong reason sends an author to the wrong part
|
|
9
|
+
* of their own file. So each case below asserts what the message SAYS, not only
|
|
10
|
+
* that it refused.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { describe, expect, it } from "vitest";
|
|
14
|
+
import { readAuthorQuery } from "./authorQuery.js";
|
|
15
|
+
|
|
16
|
+
/** The parts, for a query the reader can read. */
|
|
17
|
+
function parts(text: string) {
|
|
18
|
+
const read = readAuthorQuery(text);
|
|
19
|
+
if (!read.ok) throw new Error(`unreadable: ${read.reason}`);
|
|
20
|
+
return read.query;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The reason, for one it cannot. */
|
|
24
|
+
function reason(text: string): string {
|
|
25
|
+
const read = readAuthorQuery(text);
|
|
26
|
+
if (read.ok) throw new Error("expected a refusal");
|
|
27
|
+
return read.reason;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("the projection", () => {
|
|
31
|
+
it("reads bare variables and aliased expressions, in author order", () => {
|
|
32
|
+
expect(
|
|
33
|
+
parts(
|
|
34
|
+
[
|
|
35
|
+
"SELECT ?uri ?name ?category",
|
|
36
|
+
' (GROUP_CONCAT(DISTINCT ?slug; SEPARATOR=" ") AS ?categories)',
|
|
37
|
+
"WHERE { ?uri a cs:CodeStandard }",
|
|
38
|
+
].join("\n"),
|
|
39
|
+
).projection,
|
|
40
|
+
).toEqual(["uri", "name", "category", "categories"]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("reads through DISTINCT and through a nested projection expression", () => {
|
|
44
|
+
expect(
|
|
45
|
+
parts(
|
|
46
|
+
"SELECT DISTINCT ?a (COALESCE(IF(?d = 0, 1, 1 - (0.2 * ?d)), 0) AS ?rank) WHERE { ?a ds:x ?d }",
|
|
47
|
+
).projection,
|
|
48
|
+
).toEqual(["a", "rank"]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("reads $-sigil variables, and a projection with no WHERE keyword", () => {
|
|
52
|
+
expect(parts("SELECT $a $b { $a ds:x $b }").projection).toEqual(["a", "b"]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("answers undefined for SELECT * — the one projection it cannot enumerate", () => {
|
|
56
|
+
expect(parts("SELECT * WHERE { ?s ?p ?o }").projection).toBeUndefined();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("is not fooled by a brace or a WHERE inside a string literal", () => {
|
|
60
|
+
expect(
|
|
61
|
+
parts(
|
|
62
|
+
'SELECT ?a (REPLACE(?a, "WHERE {", ")") AS ?b) WHERE { ?a ds:x ?y }',
|
|
63
|
+
).projection,
|
|
64
|
+
).toEqual(["a", "b"]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("reads through a comment sitting where a variable was expected", () => {
|
|
68
|
+
// Previously undefined, which refused the story with a message saying it
|
|
69
|
+
// must not use `SELECT *` — which it does not.
|
|
70
|
+
expect(
|
|
71
|
+
parts("SELECT # the columns\n ?uri ?name\nWHERE { ?uri ds:name ?name }")
|
|
72
|
+
.projection,
|
|
73
|
+
).toEqual(["uri", "name"]);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("the prologue", () => {
|
|
78
|
+
it("is empty, and the body is the whole text, when the author declares none", () => {
|
|
79
|
+
const query = "SELECT ?s WHERE { ?s ?p ?o }";
|
|
80
|
+
expect(parts(query)).toEqual({
|
|
81
|
+
prologue: "",
|
|
82
|
+
projection: ["s"],
|
|
83
|
+
body: query,
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("is lifted whole, and the body starts at SELECT", () => {
|
|
88
|
+
const read = parts(
|
|
89
|
+
[
|
|
90
|
+
"PREFIX a: <https://example.test/a#>",
|
|
91
|
+
"PREFIX b: <https://example.test/b#>",
|
|
92
|
+
"SELECT ?s WHERE { ?s a:p ?o }",
|
|
93
|
+
].join("\n"),
|
|
94
|
+
);
|
|
95
|
+
expect(read.prologue).toBe(
|
|
96
|
+
"PREFIX a: <https://example.test/a#>\nPREFIX b: <https://example.test/b#>",
|
|
97
|
+
);
|
|
98
|
+
expect(read.body).toBe("SELECT ?s WHERE { ?s a:p ?o }");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("reads a `#` and a `>`-free IRI as an IRI, not as a comment", () => {
|
|
102
|
+
// `IRIREF` excludes `>` and admits `#`, so the IRI ends at its own `>` and
|
|
103
|
+
// a `#` inside it does not start a comment.
|
|
104
|
+
const read = parts(
|
|
105
|
+
"prefix q: <https://example.test/x#select> SELECT ?s WHERE { ?s q:p ?o }",
|
|
106
|
+
);
|
|
107
|
+
expect(read.prologue).toBe("prefix q: <https://example.test/x#select>");
|
|
108
|
+
expect(read.projection).toEqual(["s"]);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("tolerates lower case, a leading comment, and comments between declarations", () => {
|
|
112
|
+
const read = parts(
|
|
113
|
+
[
|
|
114
|
+
"# select the standards",
|
|
115
|
+
"base <https://example.test/>",
|
|
116
|
+
"# and their categories",
|
|
117
|
+
"Prefix cs: <https://example.test/cs#>",
|
|
118
|
+
"select ?s where { ?s cs:p ?o }",
|
|
119
|
+
].join("\n"),
|
|
120
|
+
);
|
|
121
|
+
expect(read.prologue).toBe(
|
|
122
|
+
[
|
|
123
|
+
"base <https://example.test/>",
|
|
124
|
+
"# and their categories",
|
|
125
|
+
"Prefix cs: <https://example.test/cs#>",
|
|
126
|
+
].join("\n"),
|
|
127
|
+
);
|
|
128
|
+
expect(read.body).toBe("select ?s where { ?s cs:p ?o }");
|
|
129
|
+
expect(read.projection).toEqual(["s"]);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("is not confused by a `PREFIX` IRI containing the word select", () => {
|
|
133
|
+
expect(
|
|
134
|
+
parts("PREFIX p: <https://example.test/select#> SELECT ?s { ?s p:x ?o }")
|
|
135
|
+
.projection,
|
|
136
|
+
).toEqual(["s"]);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("takes the default prefix (`PREFIX : <…>`) as a declaration", () => {
|
|
140
|
+
const read = parts(
|
|
141
|
+
"PREFIX : <https://example.test/> SELECT ?s { ?s :x ?o }",
|
|
142
|
+
);
|
|
143
|
+
expect(read.prologue).toBe("PREFIX : <https://example.test/>");
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
describe("what it refuses, and what it says it found", () => {
|
|
148
|
+
it("names a FROM dataset clause", () => {
|
|
149
|
+
expect(
|
|
150
|
+
reason("SELECT ?s FROM <https://example.test/g> WHERE { ?s ?p ?o }"),
|
|
151
|
+
).toContain("FROM dataset clause");
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("names a FROM NAMED clause the same way", () => {
|
|
155
|
+
expect(
|
|
156
|
+
reason(
|
|
157
|
+
"SELECT ?s FROM NAMED <https://example.test/g> WHERE { ?s ?p ?o }",
|
|
158
|
+
),
|
|
159
|
+
).toContain("FROM dataset clause");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("names a text with no SELECT at all", () => {
|
|
163
|
+
expect(reason("ASK { ?s ?p ?o }")).toContain("no SPARQL SELECT");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("names a projection that binds no result variable", () => {
|
|
167
|
+
expect(reason("SELECT (COUNT(?s)) WHERE { ?s ?p ?o }")).toContain(
|
|
168
|
+
"names no result variable",
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("names a token it does not recognise, and quotes it", () => {
|
|
173
|
+
expect(reason("SELECT ?a , ?b WHERE { ?a ?p ?b }")).toContain('","');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("names a SELECT clause that projects nothing", () => {
|
|
177
|
+
expect(reason("SELECT WHERE { ?s ?p ?o }")).toContain("projects nothing");
|
|
178
|
+
});
|
|
179
|
+
});
|