@canonical/pragma-cli 0.38.0 → 0.40.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 +1 -1
- package/dist/package.json +14 -14
- package/dist/pragma.conf.js +273 -176
- package/dist/pragma.conf.js.map +1 -1
- package/dist/src/capabilities/capabilities/capabilities.verb.js +2 -0
- package/dist/src/capabilities/capabilities/capabilities.verb.js.map +1 -1
- package/dist/src/capabilities/capabilities/catalog.js +47 -51
- package/dist/src/capabilities/capabilities/catalog.js.map +1 -1
- package/dist/src/capabilities/capabilities/index.js +2 -0
- package/dist/src/capabilities/capabilities/index.js.map +1 -1
- package/dist/src/capabilities/capabilities/types.js +5 -5
- package/dist/src/capabilities/colophon/colophon.render.js +7 -4
- package/dist/src/capabilities/colophon/colophon.render.js.map +1 -1
- package/dist/src/capabilities/colophon/colophon.verb.js +1 -0
- package/dist/src/capabilities/colophon/colophon.verb.js.map +1 -1
- package/dist/src/capabilities/config/get.verb.js +2 -0
- package/dist/src/capabilities/config/get.verb.js.map +1 -1
- package/dist/src/capabilities/config/runField.js +2 -2
- package/dist/src/capabilities/config/runField.js.map +1 -1
- package/dist/src/capabilities/config/set.verb.js +2 -0
- package/dist/src/capabilities/config/set.verb.js.map +1 -1
- package/dist/src/capabilities/config/show.verb.js +1 -0
- package/dist/src/capabilities/config/show.verb.js.map +1 -1
- package/dist/src/capabilities/config/unset.verb.js +2 -0
- package/dist/src/capabilities/config/unset.verb.js.map +1 -1
- package/dist/src/capabilities/create/constants.js +2 -2
- package/dist/src/capabilities/create/create.verb.js +14 -4
- package/dist/src/capabilities/create/create.verb.js.map +1 -1
- package/dist/src/capabilities/distribution.js +3 -1
- package/dist/src/capabilities/distribution.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/checkPackageRefs.js +10 -2
- package/dist/src/capabilities/doctor/checks/checkPackageRefs.js.map +1 -1
- package/dist/src/capabilities/doctor/doctor.verb.js +2 -0
- package/dist/src/capabilities/doctor/doctor.verb.js.map +1 -1
- package/dist/src/capabilities/graph/connect.verb.js +2 -0
- package/dist/src/capabilities/graph/connect.verb.js.map +1 -1
- package/dist/src/capabilities/graph/inspect.render.js +7 -1
- package/dist/src/capabilities/graph/inspect.render.js.map +1 -1
- package/dist/src/capabilities/graph/inspect.verb.js +2 -0
- package/dist/src/capabilities/graph/inspect.verb.js.map +1 -1
- package/dist/src/capabilities/graph/query.render.js +37 -5
- package/dist/src/capabilities/graph/query.render.js.map +1 -1
- package/dist/src/capabilities/graph/query.verb.js +2 -0
- package/dist/src/capabilities/graph/query.verb.js.map +1 -1
- package/dist/src/capabilities/graph/queryText.js +28 -0
- package/dist/src/capabilities/graph/queryText.js.map +1 -1
- package/dist/src/capabilities/graph/runConnect.js +3 -3
- package/dist/src/capabilities/graph/runConnect.js.map +1 -1
- package/dist/src/capabilities/graph/runQuery.js +8 -6
- package/dist/src/capabilities/graph/runQuery.js.map +1 -1
- package/dist/src/capabilities/info/info.verb.js +2 -0
- package/dist/src/capabilities/info/info.verb.js.map +1 -1
- package/dist/src/capabilities/meta/mcp.verb.js +1 -0
- package/dist/src/capabilities/meta/mcp.verb.js.map +1 -1
- package/dist/src/capabilities/ontology/verbs.js +7 -10
- package/dist/src/capabilities/ontology/verbs.js.map +1 -1
- package/dist/src/capabilities/prompt/verbs.js +5 -4
- package/dist/src/capabilities/prompt/verbs.js.map +1 -1
- package/dist/src/capabilities/resources/provider.js +6 -3
- package/dist/src/capabilities/resources/provider.js.map +1 -1
- package/dist/src/capabilities/setup/setup.verb.js +13 -5
- package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
- package/dist/src/capabilities/shared/calls.js +16 -0
- package/dist/src/capabilities/shared/calls.js.map +1 -0
- package/dist/src/capabilities/skill/render.js +7 -5
- package/dist/src/capabilities/skill/render.js.map +1 -1
- package/dist/src/capabilities/skill/verbs.js +5 -4
- package/dist/src/capabilities/skill/verbs.js.map +1 -1
- package/dist/src/capabilities/sources/collectStatus.js +6 -1
- package/dist/src/capabilities/sources/collectStatus.js.map +1 -1
- package/dist/src/capabilities/sources/index.js +4 -3
- package/dist/src/capabilities/sources/index.js.map +1 -1
- package/dist/src/capabilities/sources/reset.render.js +37 -0
- package/dist/src/capabilities/sources/reset.render.js.map +1 -0
- package/dist/src/capabilities/sources/reset.verb.js +51 -0
- package/dist/src/capabilities/sources/reset.verb.js.map +1 -0
- package/dist/src/capabilities/sources/runReset.js +59 -0
- package/dist/src/capabilities/sources/runReset.js.map +1 -0
- package/dist/src/capabilities/sources/runUpdate.js +7 -6
- package/dist/src/capabilities/sources/runUpdate.js.map +1 -1
- package/dist/src/capabilities/sources/status.render.js +24 -8
- package/dist/src/capabilities/sources/status.render.js.map +1 -1
- package/dist/src/capabilities/sources/status.verb.js +1 -0
- package/dist/src/capabilities/sources/status.verb.js.map +1 -1
- package/dist/src/capabilities/sources/update.verb.js +2 -0
- package/dist/src/capabilities/sources/update.verb.js.map +1 -1
- package/dist/src/capabilities/upgrade/upgrade.verb.js +1 -0
- package/dist/src/capabilities/upgrade/upgrade.verb.js.map +1 -1
- package/dist/src/capabilities/version/version.verb.js +1 -0
- package/dist/src/capabilities/version/version.verb.js.map +1 -1
- package/dist/src/constants.js +4 -3
- package/dist/src/constants.js.map +1 -1
- package/dist/src/kernel/error/index.js +1 -1
- package/dist/src/kernel/error/index.js.map +1 -1
- package/dist/src/kernel/error/recovery.js +24 -42
- package/dist/src/kernel/error/recovery.js.map +1 -1
- package/dist/src/kernel/packs/collect.js +123 -9
- package/dist/src/kernel/packs/collect.js.map +1 -1
- package/dist/src/kernel/packs/compile.js +74 -15
- package/dist/src/kernel/packs/compile.js.map +1 -1
- package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js +3 -3
- package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js.map +1 -1
- package/dist/src/kernel/packs/iri.js +2 -4
- package/dist/src/kernel/packs/iri.js.map +1 -1
- package/dist/src/kernel/packs/renderPack.js +90 -50
- package/dist/src/kernel/packs/renderPack.js.map +1 -1
- package/dist/src/kernel/packs/resolveEntity.js +202 -90
- package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
- package/dist/src/kernel/packs/runBodies.js +89 -30
- package/dist/src/kernel/packs/runBodies.js.map +1 -1
- package/dist/src/kernel/packs/schema.js +66 -12
- package/dist/src/kernel/packs/schema.js.map +1 -1
- package/dist/src/kernel/packs/sparql/buildListQuery.js +80 -13
- package/dist/src/kernel/packs/sparql/buildListQuery.js.map +1 -1
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js +41 -13
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
- package/dist/src/kernel/packs/sparql/filterValues.js +32 -23
- package/dist/src/kernel/packs/sparql/filterValues.js.map +1 -1
- package/dist/src/kernel/packs/sparql/runSelect.js +3 -5
- package/dist/src/kernel/packs/sparql/runSelect.js.map +1 -1
- package/dist/src/kernel/packs/storyRules.js +31 -4
- package/dist/src/kernel/packs/storyRules.js.map +1 -1
- package/dist/src/kernel/packs/tierScope.js +3 -3
- package/dist/src/kernel/packs/tierScope.js.map +1 -1
- package/dist/src/kernel/packs/types.js +4 -0
- package/dist/src/kernel/packs/types.js.map +1 -1
- package/dist/src/kernel/project/cli/buildProgram.js +4 -0
- package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
- package/dist/src/kernel/project/cli/dispatch.js +32 -8
- package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
- package/dist/src/kernel/project/cli/suggestNames.js +44 -1
- package/dist/src/kernel/project/cli/suggestNames.js.map +1 -1
- package/dist/src/kernel/project/cli/verbHelp.js +17 -18
- package/dist/src/kernel/project/cli/verbHelp.js.map +1 -1
- package/dist/src/kernel/project/mcp/buildServer.js +11 -4
- package/dist/src/kernel/project/mcp/buildServer.js.map +1 -1
- package/dist/src/kernel/project/mcp/envelope.js +3 -2
- package/dist/src/kernel/project/mcp/envelope.js.map +1 -1
- package/dist/src/kernel/project/mcp/instructions.js +101 -31
- package/dist/src/kernel/project/mcp/instructions.js.map +1 -1
- package/dist/src/kernel/project/mcp/registerVerb.js +59 -23
- package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
- package/dist/src/kernel/render/renderers.js +3 -3
- package/dist/src/kernel/render/turtle.js +21 -4
- package/dist/src/kernel/render/turtle.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/build.js +54 -1
- package/dist/src/kernel/runtime/graphpack/build.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/manifest.js +14 -0
- package/dist/src/kernel/runtime/graphpack/manifest.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/read.js +3 -5
- package/dist/src/kernel/runtime/graphpack/read.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/schemas.js +1 -0
- package/dist/src/kernel/runtime/graphpack/schemas.js.map +1 -1
- package/dist/src/kernel/runtime/loadSession.js +8 -1
- package/dist/src/kernel/runtime/loadSession.js.map +1 -1
- package/dist/src/kernel/runtime/packVersion.js +75 -0
- package/dist/src/kernel/runtime/packVersion.js.map +1 -0
- package/dist/src/kernel/runtime/readEntity.js +43 -4
- package/dist/src/kernel/runtime/readEntity.js.map +1 -1
- package/dist/src/kernel/runtime/resolveSources.js +61 -9
- package/dist/src/kernel/runtime/resolveSources.js.map +1 -1
- package/dist/src/kernel/runtime/storeReadiness.js +3 -2
- package/dist/src/kernel/runtime/storeReadiness.js.map +1 -1
- package/dist/src/kernel/spec/call.js +167 -0
- package/dist/src/kernel/spec/call.js.map +1 -0
- package/dist/src/kernel/spec/emitReference.js +9 -13
- package/dist/src/kernel/spec/emitReference.js.map +1 -1
- package/dist/src/kernel/spec/emitSurface.js +8 -1
- package/dist/src/kernel/spec/emitSurface.js.map +1 -1
- package/dist/src/kernel/spec/guidance.js +63 -0
- package/dist/src/kernel/spec/guidance.js.map +1 -0
- package/dist/src/kernel/spec/wireType.js +36 -0
- package/dist/src/kernel/spec/wireType.js.map +1 -0
- package/package.json +14 -14
- package/pragma.conf.ts +294 -177
- package/src/capabilities/block.parity.test.ts +1 -1
- package/src/capabilities/callRule.test.ts +147 -0
- package/src/capabilities/capabilities/capabilities.test.ts +47 -19
- package/src/capabilities/capabilities/capabilities.verb.ts +2 -0
- package/src/capabilities/capabilities/catalog.ts +60 -55
- package/src/capabilities/capabilities/index.ts +2 -0
- package/src/capabilities/capabilities/types.ts +16 -19
- package/src/capabilities/colophon/colophon.render.ts +9 -5
- package/src/capabilities/colophon/colophon.verb.ts +1 -0
- package/src/capabilities/config/get.verb.ts +2 -0
- package/src/capabilities/config/runField.ts +3 -3
- package/src/capabilities/config/set.verb.ts +3 -0
- package/src/capabilities/config/show.verb.ts +2 -0
- package/src/capabilities/config/unset.verb.ts +2 -0
- package/src/capabilities/create/constants.ts +2 -2
- package/src/capabilities/create/create.verb.ts +16 -0
- package/src/capabilities/create/createGrammar.test.ts +1 -1
- package/src/capabilities/distribution.test.ts +6 -3
- package/src/capabilities/distribution.ts +6 -0
- package/src/capabilities/doctor/checks/checkPackageRefs.ts +14 -2
- package/src/capabilities/doctor/doctor.test.ts +38 -1
- package/src/capabilities/doctor/doctor.verb.ts +2 -0
- package/src/capabilities/emptyRecovery.test.ts +5 -0
- package/src/capabilities/graph/connect.verb.ts +2 -0
- package/src/capabilities/graph/inspect.render.ts +9 -1
- package/src/capabilities/graph/inspect.test.ts +73 -0
- package/src/capabilities/graph/inspect.verb.ts +2 -0
- package/src/capabilities/graph/query.render.test.ts +35 -0
- package/src/capabilities/graph/query.render.ts +45 -7
- package/src/capabilities/graph/query.shipped.exec.test.ts +44 -0
- package/src/capabilities/graph/query.verb.ts +2 -0
- package/src/capabilities/graph/queryText.test.ts +55 -1
- package/src/capabilities/graph/queryText.ts +47 -0
- package/src/capabilities/graph/runConnect.ts +4 -5
- package/src/capabilities/graph/runQuery.ts +23 -9
- package/src/capabilities/info/info.verb.ts +2 -0
- package/src/capabilities/links.shipped.exec.test.ts +398 -0
- package/src/capabilities/listBudget.shipped.exec.test.ts +2 -2
- package/src/capabilities/listLookup.shipped.exec.test.ts +122 -13
- package/src/capabilities/listQuery.shipped.exec.test.ts +39 -16
- package/src/capabilities/lookupBudget.shipped.exec.test.ts +208 -0
- package/src/capabilities/meta/mcp.verb.ts +2 -0
- package/src/capabilities/ontology/ontology.test.ts +24 -0
- package/src/capabilities/ontology/verbs.ts +16 -12
- package/src/capabilities/prompt/verbs.ts +10 -4
- package/src/capabilities/resources/provider.ts +7 -3
- package/src/capabilities/resources/resources.test.ts +2 -1
- package/src/capabilities/setup/setup.verb.ts +16 -0
- package/src/capabilities/shared/calls.ts +20 -0
- package/src/capabilities/skill/render.ts +9 -6
- package/src/capabilities/skill/verbs.ts +8 -4
- package/src/capabilities/sources/collectStatus.ts +6 -1
- package/src/capabilities/sources/index.ts +4 -3
- package/src/capabilities/sources/reset.render.test.ts +33 -0
- package/src/capabilities/sources/reset.render.ts +43 -0
- package/src/capabilities/sources/reset.verb.ts +59 -0
- package/src/capabilities/sources/runReset.ts +65 -0
- package/src/capabilities/sources/runUpdate.ts +12 -11
- package/src/capabilities/sources/sources.test.ts +158 -3
- package/src/capabilities/sources/status.render.test.ts +63 -1
- package/src/capabilities/sources/status.render.ts +25 -8
- package/src/capabilities/sources/status.verb.ts +2 -0
- package/src/capabilities/sources/types.ts +26 -0
- package/src/capabilities/sources/update.verb.ts +3 -0
- package/src/capabilities/surface.test.ts +2 -2
- package/src/capabilities/token.parity.test.ts +3 -2
- package/src/capabilities/tokenGraph.shipped.exec.test.ts +14 -2
- package/src/capabilities/upgrade/upgrade.verb.ts +1 -0
- package/src/capabilities/variable.parity.test.ts +2 -1
- package/src/capabilities/version/version.verb.ts +1 -0
- package/src/capabilities/wireContract.test.ts +110 -0
- package/src/constants.ts +4 -3
- package/src/kernel/copy.test.ts +1 -1
- package/src/kernel/error/PragmaError.test.ts +7 -3
- package/src/kernel/error/index.ts +1 -1
- package/src/kernel/error/recovery.test.ts +52 -18
- package/src/kernel/error/recovery.ts +25 -46
- package/src/kernel/error/types.ts +1 -1
- package/src/kernel/packs/collect.test.ts +79 -0
- package/src/kernel/packs/collect.ts +151 -11
- package/src/kernel/packs/compile.test.ts +107 -10
- package/src/kernel/packs/compile.ts +100 -11
- package/src/kernel/packs/filteredEmpty.test.ts +22 -5
- package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +5 -6
- package/src/kernel/packs/graphqlSource.test.ts +42 -0
- package/src/kernel/packs/iri.ts +5 -4
- package/src/kernel/packs/listNotice.test.ts +1 -1
- package/src/kernel/packs/nounLink.test.ts +385 -0
- package/src/kernel/packs/renderPack.ts +137 -57
- package/src/kernel/packs/resolveEntity.test.ts +170 -1
- package/src/kernel/packs/resolveEntity.ts +281 -101
- package/src/kernel/packs/runBodies.ts +133 -31
- package/src/kernel/packs/schema.ts +77 -12
- package/src/kernel/packs/sparql/buildListQuery.test.ts +78 -1
- package/src/kernel/packs/sparql/buildListQuery.ts +106 -16
- package/src/kernel/packs/sparql/buildLookupQuery.ts +44 -13
- package/src/kernel/packs/sparql/filterValues.test.ts +36 -4
- package/src/kernel/packs/sparql/filterValues.ts +50 -31
- package/src/kernel/packs/sparql/runSelect.ts +4 -6
- package/src/kernel/packs/storyRules.ts +37 -4
- package/src/kernel/packs/tierScope.test.ts +120 -6
- package/src/kernel/packs/tierScope.ts +5 -7
- package/src/kernel/packs/toolDescription.test.ts +59 -74
- package/src/kernel/packs/types.ts +93 -11
- package/src/kernel/project/cli/buildProgram.ts +4 -0
- package/src/kernel/project/cli/dispatch.ts +34 -8
- package/src/kernel/project/cli/suggest.test.ts +77 -0
- package/src/kernel/project/cli/suggestNames.ts +47 -1
- package/src/kernel/project/cli/verbHelp.ts +16 -19
- package/src/kernel/project/mcp/buildServer.ts +15 -4
- package/src/kernel/project/mcp/envelope.ts +3 -2
- package/src/kernel/project/mcp/instructions.ts +123 -35
- package/src/kernel/project/mcp/prompts/prompts.test.ts +136 -20
- package/src/kernel/project/mcp/registerVerb.test.ts +28 -0
- package/src/kernel/project/mcp/registerVerb.ts +67 -29
- package/src/kernel/render/__snapshots__/renderers.test.ts.snap +1 -1
- package/src/kernel/render/renderers.test.ts +2 -2
- package/src/kernel/render/renderers.ts +3 -3
- package/src/kernel/render/turtle.ts +24 -4
- package/src/kernel/runtime/graphpack/build.ts +58 -1
- package/src/kernel/runtime/graphpack/graphpack.test.ts +20 -0
- package/src/kernel/runtime/graphpack/manifest.ts +15 -0
- package/src/kernel/runtime/graphpack/read.ts +4 -6
- package/src/kernel/runtime/graphpack/schemas.ts +1 -0
- package/src/kernel/runtime/graphpack/types.ts +13 -0
- package/src/kernel/runtime/loadSession.ts +8 -1
- package/src/kernel/runtime/packVersion.test.ts +61 -0
- package/src/kernel/runtime/packVersion.ts +84 -0
- package/src/kernel/runtime/readEntity.ts +61 -6
- package/src/kernel/runtime/resolveSources.test.ts +90 -7
- package/src/kernel/runtime/resolveSources.ts +91 -10
- package/src/kernel/runtime/storeReadiness.ts +4 -4
- package/src/kernel/runtime/types.ts +12 -0
- package/src/kernel/spec/call.test.ts +95 -0
- package/src/kernel/spec/call.ts +186 -0
- package/src/kernel/spec/emitReference.ts +9 -13
- package/src/kernel/spec/emitSurface.ts +9 -1
- package/src/kernel/spec/guidance.test.ts +49 -0
- package/src/kernel/spec/guidance.ts +68 -0
- package/src/kernel/spec/index.ts +4 -0
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/kernel/spec/types.ts +64 -7
- package/src/kernel/spec/wireType.ts +53 -0
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +2 -1
- package/src/testing/behavioral/journeys.firstInstall.test.ts +72 -2
- package/src/testing/behavioral/parity.test.ts +40 -0
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +1 -1
- package/src/testing/eval/cases/assembly.ts +4 -3
- package/src/testing/eval/cases/stable.ts +2 -3
- package/src/testing/helpers/callSchema.ts +40 -0
- package/src/testing/helpers/parity.ts +35 -2
- package/src/testing/setupCallChecking.ts +30 -0
- package/dist/src/capabilities/capabilities/hints.js +0 -223
- package/dist/src/capabilities/capabilities/hints.js.map +0 -1
- package/src/capabilities/capabilities/hints.ts +0 -266
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
*/
|
|
86
86
|
|
|
87
87
|
import { PragmaError } from "../../error/index.js";
|
|
88
|
-
import { RESERVED_VARIABLE_PREFIX } from "../types.js";
|
|
88
|
+
import { type PackRow, RESERVED_VARIABLE_PREFIX } from "../types.js";
|
|
89
89
|
import { readAuthorQuery } from "./authorQuery.js";
|
|
90
90
|
import { escapeSparqlString, formatTerm } from "./escape.js";
|
|
91
91
|
|
|
@@ -93,13 +93,20 @@ import { escapeSparqlString, formatTerm } from "./escape.js";
|
|
|
93
93
|
export interface ListPredicate {
|
|
94
94
|
/** SELECT variable the filter constrains (without `?`). */
|
|
95
95
|
readonly variable: string;
|
|
96
|
-
/**
|
|
97
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Whole-cell comparison, membership of a whitespace-separated set, or — for
|
|
98
|
+
* `"iri"` — identity with one of the entity IRIs in {@link terms}.
|
|
99
|
+
*/
|
|
100
|
+
readonly match: "exact" | "set" | "iri";
|
|
98
101
|
/**
|
|
99
102
|
* The admitted values, in the graph's own display spelling. Several are a
|
|
100
103
|
* union (the row matches any of them); several FILTERS are a conjunction.
|
|
104
|
+
* For `"iri"` they are absolute IRIs the store itself returned, and none
|
|
105
|
+
* at all matches no row.
|
|
101
106
|
*/
|
|
102
107
|
readonly terms: readonly string[];
|
|
108
|
+
/** `"iri"` only: the path from the variable to the entity {@link terms} name. */
|
|
109
|
+
readonly via?: string;
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
/** A declared search, with the term a caller actually supplied. */
|
|
@@ -152,6 +159,8 @@ export interface ListQueryInput {
|
|
|
152
159
|
readonly search?: ListSearch;
|
|
153
160
|
/** The tier scope this read answers under, absent when it answers from all. */
|
|
154
161
|
readonly scope?: ListTierScope;
|
|
162
|
+
/** Projected variables the page leaves out of its rows. */
|
|
163
|
+
readonly omit?: readonly string[];
|
|
155
164
|
/** The page to return. */
|
|
156
165
|
readonly window: ListWindow;
|
|
157
166
|
/** The story's label, for a configuration diagnosis. */
|
|
@@ -171,7 +180,79 @@ export interface ListQueryInput {
|
|
|
171
180
|
* the wrong part of their own file.
|
|
172
181
|
*/
|
|
173
182
|
export function buildListQuery(input: ListQueryInput): string {
|
|
174
|
-
const {
|
|
183
|
+
const { prologue, projection, body, clauses } = readWrapped(input);
|
|
184
|
+
const shown = projection?.filter((name) => !input.omit?.includes(name));
|
|
185
|
+
return [
|
|
186
|
+
...(prologue === "" ? [] : [prologue]),
|
|
187
|
+
`SELECT ${shown ? shown.map((variable) => `?${variable}`).join(" ") : "*"}`,
|
|
188
|
+
"WHERE {",
|
|
189
|
+
" {",
|
|
190
|
+
body,
|
|
191
|
+
" }",
|
|
192
|
+
...clauses.map((clause) => ` ${clause}`),
|
|
193
|
+
"}",
|
|
194
|
+
modifier(input.window),
|
|
195
|
+
].join("\n");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Build the SELECT counting a scoped list's whole filtered answer per tier —
|
|
200
|
+
* the page's wrap without the page and without the scope clause — and the
|
|
201
|
+
* reader of its rows.
|
|
202
|
+
*
|
|
203
|
+
* @param input - The author query, the supplied filters/search, and the scope
|
|
204
|
+
* whose `entity` and `via` say how a row reaches its tier.
|
|
205
|
+
* @returns The query text, and `read`: its rows as tier IRI → row count, with
|
|
206
|
+
* `""` for rows whose entity is in no tier. An entity in two tiers counts
|
|
207
|
+
* under each.
|
|
208
|
+
* @throws PragmaError CONFIG_ERROR for the reasons {@link buildListQuery} does.
|
|
209
|
+
*/
|
|
210
|
+
export function buildTierCountQuery(
|
|
211
|
+
input: Omit<ListQueryInput, "window"> & { readonly scope: ListTierScope },
|
|
212
|
+
): { text: string; read: (rows: readonly PackRow[]) => Map<string, number> } {
|
|
213
|
+
const { scope } = input;
|
|
214
|
+
const tier = `${RESERVED_VARIABLE_PREFIX}TierOf`;
|
|
215
|
+
const count = `${RESERVED_VARIABLE_PREFIX}TierRows`;
|
|
216
|
+
const { prologue, body, clauses } = readWrapped({
|
|
217
|
+
...input,
|
|
218
|
+
scope: { ...scope, tiers: [] },
|
|
219
|
+
reserved: true,
|
|
220
|
+
});
|
|
221
|
+
const text = [
|
|
222
|
+
...(prologue === "" ? [] : [prologue]),
|
|
223
|
+
`SELECT ?${tier} (COUNT(*) AS ?${count})`,
|
|
224
|
+
"WHERE {",
|
|
225
|
+
" {",
|
|
226
|
+
body,
|
|
227
|
+
" }",
|
|
228
|
+
...clauses.map((clause) => ` ${clause}`),
|
|
229
|
+
` OPTIONAL { ?${scope.entity} ${formatTerm(scope.via)} ?${tier} }`,
|
|
230
|
+
"}",
|
|
231
|
+
`GROUP BY ?${tier}`,
|
|
232
|
+
].join("\n");
|
|
233
|
+
return {
|
|
234
|
+
text,
|
|
235
|
+
read: (rows) =>
|
|
236
|
+
new Map(rows.map((row) => [row[tier] ?? "", Number(row[count])])),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Split the author query and compile the clauses that go around it — the part
|
|
242
|
+
* the page and the tier count share.
|
|
243
|
+
*
|
|
244
|
+
* @param input - As {@link buildListQuery}; `reserved` says the caller binds a
|
|
245
|
+
* generated variable of its own even when no clause does.
|
|
246
|
+
*/
|
|
247
|
+
function readWrapped(
|
|
248
|
+
input: Omit<ListQueryInput, "window"> & { readonly reserved?: boolean },
|
|
249
|
+
): {
|
|
250
|
+
prologue: string;
|
|
251
|
+
projection: readonly string[] | undefined;
|
|
252
|
+
body: string;
|
|
253
|
+
clauses: string[];
|
|
254
|
+
} {
|
|
255
|
+
const { query, predicates, search, scope, label } = input;
|
|
175
256
|
const clauses = [
|
|
176
257
|
...predicates.map((predicate, index) =>
|
|
177
258
|
filterClause(predicate, `${RESERVED_VARIABLE_PREFIX}Filter${index}`),
|
|
@@ -183,7 +264,10 @@ export function buildListQuery(input: ListQueryInput): string {
|
|
|
183
264
|
? [scopeClause(scope, `${RESERVED_VARIABLE_PREFIX}Tier`)]
|
|
184
265
|
: []),
|
|
185
266
|
];
|
|
186
|
-
if (
|
|
267
|
+
if (
|
|
268
|
+
(clauses.length > 0 || input.reserved === true) &&
|
|
269
|
+
query.includes(RESERVED_VARIABLE_PREFIX)
|
|
270
|
+
) {
|
|
187
271
|
throw PragmaError.configError(
|
|
188
272
|
`Story query in ${label} uses the reserved variable prefix "?${RESERVED_VARIABLE_PREFIX}", ` +
|
|
189
273
|
"which the generated filter clauses bind a caller's values to. Rename it.",
|
|
@@ -211,17 +295,7 @@ export function buildListQuery(input: ListQueryInput): string {
|
|
|
211
295
|
"same order, which `SELECT *` cannot promise.",
|
|
212
296
|
);
|
|
213
297
|
}
|
|
214
|
-
return
|
|
215
|
-
...(prologue === "" ? [] : [prologue]),
|
|
216
|
-
`SELECT ${projection ? projection.map((variable) => `?${variable}`).join(" ") : "*"}`,
|
|
217
|
-
"WHERE {",
|
|
218
|
-
" {",
|
|
219
|
-
body,
|
|
220
|
-
" }",
|
|
221
|
-
...clauses.map((clause) => ` ${clause}`),
|
|
222
|
-
"}",
|
|
223
|
-
modifier(window),
|
|
224
|
-
].join("\n");
|
|
298
|
+
return { prologue, projection, body, clauses };
|
|
225
299
|
}
|
|
226
300
|
|
|
227
301
|
/** The page as SPARQL solution modifiers (`OFFSET 0` omitted as the no-op it is). */
|
|
@@ -239,6 +313,7 @@ function modifier(window: ListWindow): string {
|
|
|
239
313
|
* `set` cell carrying two of them would be returned twice.
|
|
240
314
|
*/
|
|
241
315
|
function filterClause(predicate: ListPredicate, bound: string): string {
|
|
316
|
+
if (predicate.match === "iri") return entityClause(predicate, bound);
|
|
242
317
|
const values = predicate.terms
|
|
243
318
|
.map((term) => `"${escapeSparqlString(term)}"`)
|
|
244
319
|
.join(" ");
|
|
@@ -250,6 +325,21 @@ function filterClause(predicate: ListPredicate, bound: string): string {
|
|
|
250
325
|
return `FILTER EXISTS { VALUES ?${bound} { ${values} } FILTER(${comparison}) }`;
|
|
251
326
|
}
|
|
252
327
|
|
|
328
|
+
/**
|
|
329
|
+
* A row's entity is one of the named ones, or reaches one along `via`.
|
|
330
|
+
*
|
|
331
|
+
* The IRIs are the store's own, returned by the named noun's resolve — a
|
|
332
|
+
* caller's text never reaches this clause.
|
|
333
|
+
*/
|
|
334
|
+
function entityClause(predicate: ListPredicate, bound: string): string {
|
|
335
|
+
const values = predicate.terms.map((iri) => `<${iri}>`).join(" ");
|
|
336
|
+
const entity = `?${predicate.variable}`;
|
|
337
|
+
const test = predicate.via
|
|
338
|
+
? `${entity} ${formatTerm(predicate.via)} ?${bound}`
|
|
339
|
+
: `FILTER(${entity} = ?${bound})`;
|
|
340
|
+
return `FILTER EXISTS { VALUES ?${bound} { ${values} } ${test} }`;
|
|
341
|
+
}
|
|
342
|
+
|
|
253
343
|
/**
|
|
254
344
|
* Membership of a whitespace-separated set, spelled as a padded substring.
|
|
255
345
|
*
|
|
@@ -540,17 +540,19 @@ export function buildIriResolveQuery(lookup: PackLookup, iri: string): string {
|
|
|
540
540
|
.join("\n");
|
|
541
541
|
}
|
|
542
542
|
|
|
543
|
-
/**
|
|
544
|
-
|
|
545
|
-
|
|
543
|
+
/**
|
|
544
|
+
* Build the SELECT listing every entity IRI a lookup can address, with the name
|
|
545
|
+
* it carries when it carries one and, for a scoped lookup, its tier.
|
|
546
|
+
*/
|
|
547
|
+
export function buildLookupIrisQuery(lookup: PackLookup, via?: string): string {
|
|
548
|
+
const tier = scopeTierProjection(via);
|
|
546
549
|
return [
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
//
|
|
550
|
-
|
|
551
|
-
|
|
550
|
+
`SELECT DISTINCT ?uri ?name${tier.variable} WHERE {`,
|
|
551
|
+
buildTypeConstraint(lookup).trimEnd(),
|
|
552
|
+
// OPTIONAL under a class, required without one — the only bound there is.
|
|
553
|
+
iriNameBinding(lookup),
|
|
554
|
+
tier.optional,
|
|
552
555
|
"}",
|
|
553
|
-
"ORDER BY STR(?uri)",
|
|
554
556
|
]
|
|
555
557
|
.filter((line) => line !== "")
|
|
556
558
|
.join("\n");
|
|
@@ -568,28 +570,51 @@ export function buildLookupIrisQuery(lookup: PackLookup): string {
|
|
|
568
570
|
* `lookup` is read for ONE thing — the entity's own identity property, which a
|
|
569
571
|
* `blankWhenSelf` field compares its value against. A lookup declaring no such
|
|
570
572
|
* field never binds it.
|
|
573
|
+
*
|
|
574
|
+
* A `many` field is aggregated to one space-separated cell, and the rows are
|
|
575
|
+
* then grouped by child — so it adds values to a row and never rows. A grouped
|
|
576
|
+
* expand's rows come back in one order on every run.
|
|
571
577
|
*/
|
|
572
578
|
export function buildExpandQuery(
|
|
573
579
|
expand: PackExpand,
|
|
574
580
|
entityUri: string,
|
|
575
581
|
lookup?: Pick<PackLookup, "by">,
|
|
576
582
|
): string {
|
|
577
|
-
const
|
|
583
|
+
const fields = expand.select.filter((field) => "property" in field);
|
|
584
|
+
const single = fields.filter((field) => !field.many);
|
|
585
|
+
const vars = fields
|
|
586
|
+
.map((field) =>
|
|
587
|
+
field.many
|
|
588
|
+
? `(GROUP_CONCAT(DISTINCT ?${eachOf(field)}; SEPARATOR=" ") AS ?${field.name})`
|
|
589
|
+
: `?${field.name}`,
|
|
590
|
+
)
|
|
591
|
+
.join(" ");
|
|
592
|
+
const grouped = single.length < fields.length;
|
|
593
|
+
const group = grouped
|
|
594
|
+
? `GROUP BY ?child${single.map((field) => ` ?${field.name}`).join("")}`
|
|
595
|
+
: "";
|
|
578
596
|
const optionals = expand.select
|
|
579
597
|
.map((field) =>
|
|
580
598
|
"property" in field ? expandFieldClause(field, entityUri, lookup) : "",
|
|
581
599
|
)
|
|
582
600
|
.filter((line) => line !== "")
|
|
583
601
|
.join("\n");
|
|
602
|
+
// Grouping discards the store's scan order, so a grouped expand orders by
|
|
603
|
+
// every single-valued field: the declared names first, the rest as tiebreaks.
|
|
604
|
+
const declared = expand.orderBy ?? [];
|
|
605
|
+
const keys = grouped
|
|
606
|
+
? [...new Set([...declared, ...single.map((field) => field.name)])]
|
|
607
|
+
: declared;
|
|
584
608
|
const order =
|
|
585
|
-
|
|
586
|
-
? `ORDER BY ${
|
|
609
|
+
keys.length > 0
|
|
610
|
+
? `ORDER BY ${keys.map((name) => `?${name}`).join(" ")}`
|
|
587
611
|
: "";
|
|
588
612
|
return [
|
|
589
613
|
`SELECT ${vars} WHERE {`,
|
|
590
614
|
` <${entityUri}> ${formatTerm(expand.relation)} ?child .`,
|
|
591
615
|
optionals,
|
|
592
616
|
"}",
|
|
617
|
+
group,
|
|
593
618
|
order,
|
|
594
619
|
]
|
|
595
620
|
.filter((line) => line !== "")
|
|
@@ -611,7 +636,8 @@ function expandFieldClause(
|
|
|
611
636
|
entityUri: string,
|
|
612
637
|
lookup?: Pick<PackLookup, "by">,
|
|
613
638
|
): string {
|
|
614
|
-
const
|
|
639
|
+
const variable = field.many ? eachOf(field) : field.name;
|
|
640
|
+
const read = `?child ${formatTerm(field.property)} ?${variable} .`;
|
|
615
641
|
if (!field.blankWhenSelf || !lookup) return ` OPTIONAL { ${read} }`;
|
|
616
642
|
const self = `?${RESERVED_VARIABLE_PREFIX}Self`;
|
|
617
643
|
return [
|
|
@@ -623,6 +649,11 @@ function expandFieldClause(
|
|
|
623
649
|
].join("\n");
|
|
624
650
|
}
|
|
625
651
|
|
|
652
|
+
/** The variable one value of a `many` field is bound to before aggregation. */
|
|
653
|
+
function eachOf(field: PackExpandField): string {
|
|
654
|
+
return `${RESERVED_VARIABLE_PREFIX}Each_${field.name}`;
|
|
655
|
+
}
|
|
656
|
+
|
|
626
657
|
/** Build the SELECT listing all entity names — lookup-miss suggestions. */
|
|
627
658
|
export function buildLookupNamesQuery(lookup: PackLookup): string {
|
|
628
659
|
return [
|
|
@@ -30,7 +30,7 @@ const SET_FILTER: PackFilter = {
|
|
|
30
30
|
/** The error a filter raises for a value it does not admit. */
|
|
31
31
|
function rejection(
|
|
32
32
|
filter: PackFilter,
|
|
33
|
-
value: string,
|
|
33
|
+
value: string | readonly string[],
|
|
34
34
|
vocabulary?: readonly string[],
|
|
35
35
|
): PragmaError {
|
|
36
36
|
try {
|
|
@@ -58,8 +58,8 @@ describe("a refused filter value names its vocabulary once", () => {
|
|
|
58
58
|
expect(occurrences(plain, "color.text.0,")).toBe(1);
|
|
59
59
|
expect(plain).toContain("Valid options (745), first 40:");
|
|
60
60
|
// The recovery says what to do, and names the flag — without the list.
|
|
61
|
-
expect(plain).toContain("
|
|
62
|
-
expect(renderErrorLlm(error)).toContain("
|
|
61
|
+
expect(plain).toContain("Every value must be one of the 745 accepted.");
|
|
62
|
+
expect(renderErrorLlm(error)).toContain("must be one of the 745");
|
|
63
63
|
expect(occurrences(renderErrorLlm(error), "color.text.0,")).toBe(1);
|
|
64
64
|
});
|
|
65
65
|
|
|
@@ -75,7 +75,7 @@ describe("a refused filter value names its vocabulary once", () => {
|
|
|
75
75
|
|
|
76
76
|
expect(occurrences(plain, "dimension")).toBe(1);
|
|
77
77
|
expect(plain).toContain("Valid options: color, dimension, duration");
|
|
78
|
-
expect(plain).toContain("
|
|
78
|
+
expect(plain).toContain("Every value must be one of the 3 accepted.");
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
it("a short vocabulary stays cheap to read", () => {
|
|
@@ -84,3 +84,35 @@ describe("a refused filter value names its vocabulary once", () => {
|
|
|
84
84
|
expect(plain.length).toBeLessThan(200);
|
|
85
85
|
});
|
|
86
86
|
});
|
|
87
|
+
|
|
88
|
+
describe("a filter handed several values", () => {
|
|
89
|
+
it("admits them as one union predicate", () => {
|
|
90
|
+
const predicates = resolveFilterPredicates(
|
|
91
|
+
[SET_FILTER],
|
|
92
|
+
{ type: ["Color", "duration"] },
|
|
93
|
+
undefined,
|
|
94
|
+
"test:story",
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
expect(predicates).toEqual([
|
|
98
|
+
{ variable: "type", match: "exact", terms: ["color", "duration"] },
|
|
99
|
+
]);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("names EVERY refused value, not just the first", () => {
|
|
103
|
+
const error = rejection(
|
|
104
|
+
FREE_FILTER,
|
|
105
|
+
["color.text.1", "nope", "color.text.2", "neither"],
|
|
106
|
+
VOCABULARY,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(error.message).toBe('Invalid symbol "nope", "neither".');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("names every refused value of a declared set", () => {
|
|
113
|
+
const error = rejection(SET_FILTER, ["color", "nope", "neither"]);
|
|
114
|
+
|
|
115
|
+
expect(error.message).toBe('Invalid type "nope", "neither".');
|
|
116
|
+
expect(error.validOptions).toEqual(["color", "dimension", "duration"]);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -59,17 +59,18 @@ export function resolveFilterPredicates(
|
|
|
59
59
|
for (const filter of filters ?? []) {
|
|
60
60
|
const provided = params[filter.param];
|
|
61
61
|
if (provided === undefined) continue;
|
|
62
|
-
// A repeated CLI flag accumulates into an array
|
|
63
|
-
// Several values for ONE filter are a union (a row matches any of them);
|
|
62
|
+
// A repeated CLI flag accumulates into an array, and the MCP schema hands
|
|
63
|
+
// over the same array (one bare value coerced into it). Several values for ONE filter are a union (a row matches any of them);
|
|
64
64
|
// several filters still combine conjunctively.
|
|
65
65
|
const occurrences = Array.isArray(provided) ? provided : [provided];
|
|
66
66
|
if (occurrences.length === 0) continue;
|
|
67
67
|
const values = filter.values;
|
|
68
|
-
const terms =
|
|
68
|
+
const terms =
|
|
69
69
|
values === undefined
|
|
70
|
-
?
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
? occurrences.map((occurrence) =>
|
|
71
|
+
requireStringValue(occurrence, filter),
|
|
72
|
+
)
|
|
73
|
+
: canonicalizeFilterValues(occurrences, filter, values);
|
|
73
74
|
if (values === undefined) {
|
|
74
75
|
rejectUnknownValue(
|
|
75
76
|
requireVocabulary(filter, vocabularies, label),
|
|
@@ -135,18 +136,38 @@ function rejectUnknownValue(
|
|
|
135
136
|
if (!admissible.has(key)) admissible.set(key, value.normalize("NFC"));
|
|
136
137
|
}
|
|
137
138
|
if (admissible.size === 0) return;
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
const refused = occurrences.filter(
|
|
140
|
+
(_, index) => !admissible.has((terms.at(index) ?? "").toLowerCase()),
|
|
141
|
+
);
|
|
142
|
+
if (refused.length === 0) return;
|
|
141
143
|
// The VALUES ride `validOptions`, which every renderer already prints (and
|
|
142
144
|
// truncates, and counts). Repeating them in the recovery printed the same
|
|
143
145
|
// 40-name, 1.4KB list twice on one error — see `error/validOptions.ts`.
|
|
144
|
-
throw
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
throw refuseValues(filter, refused, [...admissible.values()].sort());
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The refusal for values a filter does not admit — EVERY one of them.
|
|
151
|
+
*
|
|
152
|
+
* A filter takes several values, so a caller who sent twenty and mistyped three
|
|
153
|
+
* is told about all three at once: naming only the first would cost a round
|
|
154
|
+
* trip per typo, which is the cost taking several values exists to remove.
|
|
155
|
+
*/
|
|
156
|
+
function refuseValues(
|
|
157
|
+
filter: PackFilter,
|
|
158
|
+
refused: readonly unknown[],
|
|
159
|
+
validOptions: string[],
|
|
160
|
+
): PragmaError {
|
|
161
|
+
return PragmaError.invalidInput(
|
|
162
|
+
filter.param,
|
|
163
|
+
refused.map(String).join('", "'),
|
|
164
|
+
{
|
|
165
|
+
validOptions,
|
|
166
|
+
recovery: {
|
|
167
|
+
message: `Every value must be one of the ${validOptions.length} accepted.`,
|
|
168
|
+
},
|
|
148
169
|
},
|
|
149
|
-
|
|
170
|
+
);
|
|
150
171
|
}
|
|
151
172
|
|
|
152
173
|
/** @throws PragmaError INVALID_INPUT when a value-free filter value is not a string. */
|
|
@@ -159,24 +180,22 @@ function requireStringValue(provided: unknown, filter: PackFilter): string {
|
|
|
159
180
|
return provided.trim().normalize("NFC");
|
|
160
181
|
}
|
|
161
182
|
|
|
162
|
-
/** @throws PragmaError INVALID_INPUT when
|
|
163
|
-
function
|
|
164
|
-
provided: unknown,
|
|
183
|
+
/** @throws PragmaError INVALID_INPUT when a value is not in the declared set. */
|
|
184
|
+
function canonicalizeFilterValues(
|
|
185
|
+
provided: readonly unknown[],
|
|
165
186
|
filter: PackFilter,
|
|
166
187
|
values: readonly string[],
|
|
167
|
-
): string {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (match !== undefined) return match.normalize("NFC");
|
|
175
|
-
}
|
|
176
|
-
throw PragmaError.invalidInput(filter.param, String(provided), {
|
|
177
|
-
validOptions: [...values],
|
|
178
|
-
recovery: {
|
|
179
|
-
message: `Pick one of the ${values.length} values --${filter.param} accepts.`,
|
|
180
|
-
},
|
|
188
|
+
): string[] {
|
|
189
|
+
const matches = provided.map((occurrence) => {
|
|
190
|
+
if (typeof occurrence !== "string") return undefined;
|
|
191
|
+
const normalized = occurrence.trim().normalize("NFC").toLowerCase();
|
|
192
|
+
return values
|
|
193
|
+
.find((value) => value.normalize("NFC").toLowerCase() === normalized)
|
|
194
|
+
?.normalize("NFC");
|
|
181
195
|
});
|
|
196
|
+
const refused = provided.filter(
|
|
197
|
+
(_, index) => matches.at(index) === undefined,
|
|
198
|
+
);
|
|
199
|
+
if (refused.length > 0) throw refuseValues(filter, refused, [...values]);
|
|
200
|
+
return matches.filter((match): match is string => match !== undefined);
|
|
182
201
|
}
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* (`graph query`) goes through the facade directly and stays INVALID_INPUT.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { callRecovery, PragmaError } from "../../error/index.js";
|
|
14
14
|
import type { PragmaRuntime } from "../../runtime/index.js";
|
|
15
|
+
import { BUILD_STORE_CALL } from "../../spec/call.js";
|
|
15
16
|
import type { PackRow, StorySource } from "../types.js";
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -63,12 +64,9 @@ function unboundPrefixError(source: StorySource): PragmaError {
|
|
|
63
64
|
return PragmaError.storeUnavailable(
|
|
64
65
|
"The local store was not built from a pack that defines every term this read uses.",
|
|
65
66
|
{
|
|
66
|
-
recovery:
|
|
67
|
-
|
|
67
|
+
recovery: callRecovery(
|
|
68
|
+
BUILD_STORE_CALL,
|
|
68
69
|
"Build the local store from the configured packs.",
|
|
69
|
-
// An agent recovers by calling the tool, then retrying (PR9 C1 cold-
|
|
70
|
-
// store retry makes the post-update retry succeed).
|
|
71
|
-
{ tool: "sources_update" },
|
|
72
70
|
),
|
|
73
71
|
},
|
|
74
72
|
);
|
|
@@ -32,9 +32,9 @@ import {
|
|
|
32
32
|
TIER_PARAM,
|
|
33
33
|
} from "./types.js";
|
|
34
34
|
|
|
35
|
-
/** The message a filter declaring
|
|
35
|
+
/** The message a filter declaring nothing to admit a value against gets. */
|
|
36
36
|
const FILTER_VOCABULARY_MESSAGE =
|
|
37
|
-
'a filter must declare "values"
|
|
37
|
+
'a filter must declare "values", a "vocabulary" query or a "noun" — a value-free filter with none has nothing to check a caller\'s value against';
|
|
38
38
|
|
|
39
39
|
/** One reason a declaration cannot be compiled, and where in it to look. */
|
|
40
40
|
export interface StoryIssue {
|
|
@@ -61,15 +61,28 @@ export function listShapeIssues(
|
|
|
61
61
|
): StoryIssue[] {
|
|
62
62
|
const issues: StoryIssue[] = [];
|
|
63
63
|
for (const [index, filter] of (shape.filters ?? []).entries()) {
|
|
64
|
-
if (
|
|
64
|
+
if (
|
|
65
|
+
filter.values === undefined &&
|
|
66
|
+
filter.vocabulary === undefined &&
|
|
67
|
+
filter.noun === undefined
|
|
68
|
+
) {
|
|
65
69
|
issues.push({
|
|
66
70
|
path: [...path, "filters", index],
|
|
67
71
|
message: FILTER_VOCABULARY_MESSAGE,
|
|
68
72
|
});
|
|
69
73
|
}
|
|
74
|
+
if (filter.noun !== undefined && filter.entity === undefined) {
|
|
75
|
+
issues.push({
|
|
76
|
+
path: [...path, "filters", index],
|
|
77
|
+
message:
|
|
78
|
+
'a filter naming a "noun" must declare the "entity" variable its IRIs constrain',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
70
81
|
}
|
|
71
82
|
const constrained = [
|
|
72
|
-
...(shape.filters ?? []).
|
|
83
|
+
...(shape.filters ?? []).flatMap((filter) =>
|
|
84
|
+
filter.entity ? [filter.variable, filter.entity] : [filter.variable],
|
|
85
|
+
),
|
|
73
86
|
...(shape.search?.variables ?? []),
|
|
74
87
|
];
|
|
75
88
|
const read = readAuthorQuery(shape.query);
|
|
@@ -107,6 +120,26 @@ export function listShapeIssues(
|
|
|
107
120
|
return issues;
|
|
108
121
|
}
|
|
109
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Every noun a story's cells and filters name, once each.
|
|
125
|
+
*
|
|
126
|
+
* @param definition - The story to read.
|
|
127
|
+
* @returns The named nouns, in declaration order.
|
|
128
|
+
*/
|
|
129
|
+
export function nounsNamed(definition: PackDefinition): string[] {
|
|
130
|
+
const lookup = definition.lookup;
|
|
131
|
+
const named = [
|
|
132
|
+
...[
|
|
133
|
+
...(definition.list ? [definition.list] : []),
|
|
134
|
+
...(definition.verbs ?? []),
|
|
135
|
+
].flatMap((shape) => [...shape.columns, ...(shape.filters ?? [])]),
|
|
136
|
+
...(lookup?.fields ?? []),
|
|
137
|
+
...(lookup?.sections ?? []),
|
|
138
|
+
...(lookup?.expand ?? []).flatMap((expand) => expand.select),
|
|
139
|
+
].flatMap((cell) => ("noun" in cell && cell.noun ? [cell.noun] : []));
|
|
140
|
+
return [...new Set(named)];
|
|
141
|
+
}
|
|
142
|
+
|
|
110
143
|
/**
|
|
111
144
|
* Every compilability rule a whole definition breaks, `list` and extra verbs
|
|
112
145
|
* alike.
|