@capacms/sdk 1.0.0-next.0 → 1.0.0-next.10

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.
Files changed (92) hide show
  1. package/CHANGELOG.md +450 -0
  2. package/README.md +1754 -156
  3. package/bin/capa-codegen.js +192 -5
  4. package/bin/capa.js +235 -0
  5. package/bin/graphql-project.js +142 -0
  6. package/bin/project-env.js +58 -0
  7. package/dist/client.d.ts +5 -0
  8. package/dist/client.js +17 -0
  9. package/dist/codegen.d.ts +55 -0
  10. package/dist/codegen.js +320 -39
  11. package/dist/config.d.ts +5 -36
  12. package/dist/config.js +47 -1
  13. package/dist/esm/image/index.d.ts +120 -0
  14. package/dist/esm/image/index.js +250 -0
  15. package/dist/esm/image/shared-params.generated.d.ts +190 -0
  16. package/dist/esm/image/shared-params.generated.js +461 -0
  17. package/dist/esm/nextjs/image-loader.d.ts +60 -0
  18. package/dist/esm/nextjs/image-loader.js +67 -0
  19. package/dist/esm/nextjs/overlay.d.ts +30 -0
  20. package/dist/esm/nextjs/overlay.js +75 -0
  21. package/dist/esm/overlay/index.d.ts +32 -0
  22. package/dist/esm/overlay/index.js +576 -0
  23. package/dist/esm/overlay/protocol.d.ts +187 -0
  24. package/dist/esm/overlay/protocol.js +240 -0
  25. package/dist/esm/package.json +4 -0
  26. package/dist/graphql-codegen.d.ts +117 -0
  27. package/dist/graphql-codegen.js +705 -0
  28. package/dist/http.js +1 -1
  29. package/dist/image/index.d.ts +120 -0
  30. package/dist/image/index.js +257 -0
  31. package/dist/image/shared-params.generated.d.ts +190 -0
  32. package/dist/image/shared-params.generated.js +471 -0
  33. package/dist/index.d.ts +2 -2
  34. package/dist/index.js +2 -1
  35. package/dist/next/attrs.d.ts +98 -0
  36. package/dist/next/attrs.js +125 -0
  37. package/dist/next/client.d.ts +176 -32
  38. package/dist/next/client.js +212 -90
  39. package/dist/next/entry-fields.d.ts +162 -0
  40. package/dist/next/entry-fields.js +2 -0
  41. package/dist/next/errors.d.ts +136 -0
  42. package/dist/next/errors.js +214 -0
  43. package/dist/next/field-names.d.ts +37 -0
  44. package/dist/next/field-names.js +145 -0
  45. package/dist/next/graphql/build.d.ts +27 -0
  46. package/dist/next/graphql/build.js +98 -0
  47. package/dist/next/graphql/documents.d.ts +67 -0
  48. package/dist/next/graphql/documents.js +35 -0
  49. package/dist/next/graphql/edit-mode.d.ts +16 -0
  50. package/dist/next/graphql/edit-mode.js +93 -0
  51. package/dist/next/graphql/filter-values.d.ts +34 -0
  52. package/dist/next/graphql/filter-values.js +96 -0
  53. package/dist/next/graphql/introspection.d.ts +89 -0
  54. package/dist/next/graphql/introspection.js +102 -0
  55. package/dist/next/graphql/plan.d.ts +115 -0
  56. package/dist/next/graphql/plan.js +531 -0
  57. package/dist/next/graphql/request.d.ts +228 -0
  58. package/dist/next/graphql/request.js +283 -0
  59. package/dist/next/graphql/rest.d.ts +66 -0
  60. package/dist/next/graphql/rest.js +502 -0
  61. package/dist/next/graphql/selection.d.ts +55 -0
  62. package/dist/next/graphql/selection.js +212 -0
  63. package/dist/next/graphql/sha256.d.ts +13 -0
  64. package/dist/next/graphql/sha256.js +86 -0
  65. package/dist/next/graphql/summary.d.ts +83 -0
  66. package/dist/next/graphql/summary.js +151 -0
  67. package/dist/next/graphql/tree-layout.d.ts +36 -0
  68. package/dist/next/graphql/tree-layout.js +20 -0
  69. package/dist/next/graphql/tree.d.ts +171 -0
  70. package/dist/next/graphql/tree.js +249 -0
  71. package/dist/next/graphql/typed.d.ts +261 -0
  72. package/dist/next/graphql/typed.js +146 -0
  73. package/dist/next/index.d.ts +30 -3
  74. package/dist/next/index.js +34 -1
  75. package/dist/next/inflate.d.ts +51 -0
  76. package/dist/next/inflate.js +243 -0
  77. package/dist/next/key-family.d.ts +31 -0
  78. package/dist/next/key-family.js +66 -0
  79. package/dist/next/select-types.d.ts +58 -5
  80. package/dist/next/system-keys.d.ts +27 -0
  81. package/dist/next/system-keys.js +42 -0
  82. package/dist/nextjs/image-loader.d.ts +60 -0
  83. package/dist/nextjs/image-loader.js +71 -0
  84. package/dist/nextjs/index.d.ts +484 -5
  85. package/dist/nextjs/index.js +704 -9
  86. package/dist/nextjs/overlay.d.ts +30 -0
  87. package/dist/nextjs/overlay.js +78 -0
  88. package/dist/overlay/index.d.ts +32 -0
  89. package/dist/overlay/index.js +596 -0
  90. package/dist/overlay/protocol.d.ts +187 -0
  91. package/dist/overlay/protocol.js +253 -0
  92. package/package.json +70 -15
@@ -0,0 +1,67 @@
1
+ /**
2
+ * documents.ts — GraphQL documents written as literals, typed by their text.
3
+ *
4
+ * A document written in a project as a literal keeps its exact text as its
5
+ * type, in any of three forms:
6
+ *
7
+ * const LATEST = `#graphql
8
+ * query Latest($first: Int) { articles(first: $first) { nodes { title } } }
9
+ * `;
10
+ * const ONE = /* capa *\/ `query One($id: ID!) { article(id: $id) { title } }`;
11
+ * const VERSION = gql(`query Version { version }`);
12
+ *
13
+ * `capa-codegen --graphql` finds each one, checks it against the key's schema,
14
+ * and adds an entry to `CapaDocuments` keyed by that text, holding its result
15
+ * and variables types. `client.graphql(LATEST)` and `graphql(LATEST)` from
16
+ * `/nextjs` look the text up, so `data` and `variables` are typed with no cast
17
+ * and no import from the generated file, the way Hydrogen types
18
+ * `storefront.query`.
19
+ *
20
+ * `gql` used as a tag (gql`query ...`) returns a plain string: TypeScript
21
+ * gives a tagged template no literal type. Codegen still checks it and exports
22
+ * a typed `<Name>Document` for it.
23
+ */
24
+ /**
25
+ * The documents written as literals in your project, by their exact text.
26
+ * Empty here: the module `capa-codegen --graphql` writes adds to it with
27
+ * `declare module "@capacms/sdk/next"`, so keep that file inside your
28
+ * tsconfig's `include`.
29
+ */
30
+ export interface CapaDocuments {
31
+ }
32
+ /** The result type codegen recorded for a literal document. */
33
+ export type CapaDocumentResult<D> = D extends keyof CapaDocuments ? CapaDocuments[D] extends {
34
+ result: infer R;
35
+ } ? R : never : never;
36
+ /** The variables type codegen recorded for a literal document. */
37
+ export type CapaDocumentVariables<D> = D extends keyof CapaDocuments ? CapaDocuments[D] extends {
38
+ variables: infer V;
39
+ } ? V : never : never;
40
+ /**
41
+ * What a call with a `#graphql` literal that `capa-codegen --graphql` has not
42
+ * seen takes instead of the literal, so the call fails to compile with the
43
+ * fix in the message rather than going untyped.
44
+ */
45
+ export type RunCapaCodegen = "This #graphql document is not in CapaDocuments yet: run capa-codegen --graphql (or keep capa-codegen --graphql --watch running) to type its data and variables.";
46
+ /**
47
+ * `T`, unless `D` is a document codegen recorded, or a `#graphql` literal it
48
+ * has not. A recorded document is typed by its record, so the untyped
49
+ * signature refuses it: otherwise wrong variables would compile through it. A
50
+ * `#graphql` literal codegen has not seen (new, or edited since the last run)
51
+ * is `RunCapaCodegen`, so it does not compile until codegen has read it.
52
+ * Text built at run time is a plain `string` and stays untyped, and so does a
53
+ * call that names its data type, `capa.graphql<Data>(text)`.
54
+ */
55
+ export type NotARecordedDocument<D, T> = D extends keyof CapaDocuments ? never : string extends D ? T : D extends `${string}#graphql${string}` ? RunCapaCodegen : T;
56
+ /**
57
+ * A GraphQL document, returned as written.
58
+ *
59
+ * Called with a literal, gql(`query ...`), it returns the literal with its
60
+ * exact text as its type, which is what `client.graphql` looks up once
61
+ * `capa-codegen --graphql` has seen it. Used as a tag, gql`query ...`, it
62
+ * returns a plain string; import the `<Name>Document` codegen writes for its
63
+ * types. Values interpolated into a tag are joined in as text, and codegen
64
+ * skips such a document, since its text is only known at run time.
65
+ */
66
+ export declare function gql<T extends string>(document: T): T;
67
+ export declare function gql(strings: TemplateStringsArray, ...values: ReadonlyArray<string | number>): string;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ /**
3
+ * documents.ts — GraphQL documents written as literals, typed by their text.
4
+ *
5
+ * A document written in a project as a literal keeps its exact text as its
6
+ * type, in any of three forms:
7
+ *
8
+ * const LATEST = `#graphql
9
+ * query Latest($first: Int) { articles(first: $first) { nodes { title } } }
10
+ * `;
11
+ * const ONE = /* capa *\/ `query One($id: ID!) { article(id: $id) { title } }`;
12
+ * const VERSION = gql(`query Version { version }`);
13
+ *
14
+ * `capa-codegen --graphql` finds each one, checks it against the key's schema,
15
+ * and adds an entry to `CapaDocuments` keyed by that text, holding its result
16
+ * and variables types. `client.graphql(LATEST)` and `graphql(LATEST)` from
17
+ * `/nextjs` look the text up, so `data` and `variables` are typed with no cast
18
+ * and no import from the generated file, the way Hydrogen types
19
+ * `storefront.query`.
20
+ *
21
+ * `gql` used as a tag (gql`query ...`) returns a plain string: TypeScript
22
+ * gives a tagged template no literal type. Codegen still checks it and exports
23
+ * a typed `<Name>Document` for it.
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.gql = gql;
27
+ function gql(document, ...values) {
28
+ if (typeof document === "string")
29
+ return document;
30
+ let text = document[0];
31
+ values.forEach((value, i) => {
32
+ text += String(value) + document[i + 1];
33
+ });
34
+ return text;
35
+ }
@@ -0,0 +1,16 @@
1
+ import type { CapaFieldNames } from "./introspection";
2
+ /** How long a key's renamed fields are kept before the names are read again. */
3
+ export declare const RENAMED_TTL_MS = 60000;
4
+ /** Per model namespace, its fields whose GraphQL name is not their namespace, by GraphQL name. */
5
+ export type RenamedFields = ReadonlyMap<string, Readonly<Record<string, string>>>;
6
+ /** The fields of each model that N5 renamed, from the N9 descriptions. Models with none are left out. */
7
+ export declare function renamedFields(names: CapaFieldNames): RenamedFields;
8
+ /**
9
+ * Run `read` in edit mode: the key's renamed fields are read beside it (or
10
+ * come from the cache), and its result's entries are marked once both are in.
11
+ */
12
+ export declare function readMarked<R extends {
13
+ data: unknown;
14
+ }>(document: string, key: string, readNames: () => Promise<CapaFieldNames>, read: () => Promise<R>): Promise<R>;
15
+ /** Forget every key's renamed fields. For tests. */
16
+ export declare function __resetEditSchemaForTests(): void;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RENAMED_TTL_MS = void 0;
4
+ exports.renamedFields = renamedFields;
5
+ exports.readMarked = readMarked;
6
+ exports.__resetEditSchemaForTests = __resetEditSchemaForTests;
7
+ /**
8
+ * edit-mode.ts — a GraphQL read in edit mode, marked for live preview.
9
+ *
10
+ * A client created with `editMode: true` marks what it reads, so `capaAttrs`
11
+ * tags it for the Capa editor (attrs.ts). In a GraphQL result an entry is an
12
+ * object that selected `id` and `model`. A field GraphQL renamed (N5:
13
+ * `hero_image` for `hero-image`, `status_field` for `status`) must still be
14
+ * tagged by its namespace, since the editor knows fields by namespace, and
15
+ * only the schema's descriptions say which fields those are.
16
+ *
17
+ * So a marked read also reads the key's type and field names
18
+ * (`FIELD_NAMES_QUERY`), kept for a minute per host, key and version, the
19
+ * same lifetime as the MCP server's copy. That read starts beside the data
20
+ * read, so the result waits for the slower of the two rather than for both
21
+ * in turn. A document that never says `model` cannot select it, so its
22
+ * result holds no entry and it reads no names. The data request itself is
23
+ * the same with or without edit mode.
24
+ */
25
+ const attrs_1 = require("../attrs");
26
+ const summary_1 = require("./summary");
27
+ /** How long a key's renamed fields are kept before the names are read again. */
28
+ exports.RENAMED_TTL_MS = 60_000;
29
+ const RENAMED_LIMIT = 100;
30
+ const renamedCache = new Map();
31
+ /** The fields of each model that N5 renamed, from the N9 descriptions. Models with none are left out. */
32
+ function renamedFields(names) {
33
+ const out = new Map();
34
+ for (const type of names.__schema.types) {
35
+ const namespace = type.kind === "OBJECT" ? (0, summary_1.modelNamespaceOf)(type.description) : null;
36
+ if (!namespace)
37
+ continue;
38
+ const renamed = {};
39
+ for (const field of type.fields ?? []) {
40
+ const parsed = (0, summary_1.parseFieldDescription)(field.description);
41
+ if (parsed && parsed.namespace !== field.name)
42
+ renamed[field.name] = parsed.namespace;
43
+ }
44
+ if (Object.keys(renamed).length)
45
+ out.set(namespace, renamed);
46
+ }
47
+ return out;
48
+ }
49
+ /**
50
+ * The renamed fields for `key` (host, key and version), from the cache or by
51
+ * `readNames`. A failed read is not kept, and gives none: a preview that
52
+ * cannot read the names still marks its entries, tagging each field by the
53
+ * name it was selected with, and never fails the page.
54
+ */
55
+ function renamedFor(key, readNames, now) {
56
+ const cached = renamedCache.get(key);
57
+ if (cached && cached.until > now)
58
+ return cached.renamed;
59
+ if (renamedCache.size >= RENAMED_LIMIT)
60
+ renamedCache.clear();
61
+ const entry = { until: now + exports.RENAMED_TTL_MS, renamed: Promise.resolve(new Map()) };
62
+ entry.renamed = readNames().then(renamedFields, () => {
63
+ if (renamedCache.get(key) === entry)
64
+ renamedCache.delete(key);
65
+ return new Map();
66
+ });
67
+ renamedCache.set(key, entry);
68
+ return entry.renamed;
69
+ }
70
+ /** Whether a result of `document` can hold an entry: an entry selected `model`, so the document says `model`. */
71
+ function canSelectModel(document) {
72
+ return /\bmodel\b/.test(document);
73
+ }
74
+ /**
75
+ * Run `read` in edit mode: the key's renamed fields are read beside it (or
76
+ * come from the cache), and its result's entries are marked once both are in.
77
+ */
78
+ async function readMarked(document, key, readNames, read) {
79
+ const result = read();
80
+ if (!canSelectModel(document))
81
+ return result;
82
+ const renamed = renamedFor(key, readNames, Date.now());
83
+ const { data } = await result;
84
+ if ((0, attrs_1.hasGraphQLEntry)(data)) {
85
+ const byModel = await renamed;
86
+ (0, attrs_1.markGraphQLEntries)(data, (model) => byModel.get(model));
87
+ }
88
+ return result;
89
+ }
90
+ /** Forget every key's renamed fields. For tests. */
91
+ function __resetEditSchemaForTests() {
92
+ renamedCache.clear();
93
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * filter-values.ts — filter values as the scalar types the schema declares.
3
+ *
4
+ * GraphQL refuses a variable of the wrong type outright, while REST reads
5
+ * loosely: a query string carries everything as text, and a list of
6
+ * true/false values takes `true` or `"true"` alike (spec 17, amendment 30).
7
+ * GraphQL types a list's filter by its items (`BooleanListFilter`, amendment
8
+ * 59), so `has: "true"` sent as GraphQL is refused. Every filter a builder
9
+ * sends is therefore converted to the operand types its filter input declares,
10
+ * whichever side it was written on: `has: "true"` on a `[Boolean]` field goes
11
+ * as `true`, `gte: "10"` on a number as `10`. A value that cannot be converted
12
+ * is left as written, so the API refuses it with its own message.
13
+ */
14
+ import type { GraphQLModelSummary, GraphQLSchemaSummary } from "./summary";
15
+ /** The operand types of a system filter (`IDFilter`, `DateTimeFilter`). */
16
+ export declare const SYSTEM_OPERANDS: Readonly<Record<string, string>>;
17
+ /** A JSON object: a filter, or one field's operators. */
18
+ export declare function isObject(value: unknown): value is Record<string, unknown>;
19
+ /** One operand as the GraphQL scalar `type`, when it reads as one. */
20
+ export declare function toScalar(type: string | undefined, value: unknown): unknown;
21
+ /**
22
+ * An operator object with each operand as the type `operands` gives its
23
+ * operator. `listText` also reads a list operand written as comma-separated
24
+ * text (`in: "1,2"`), as a REST query string carries it.
25
+ */
26
+ export declare function typedOperations(operations: Record<string, unknown>, operands: Readonly<Record<string, string>>, { listText }?: {
27
+ listText?: boolean;
28
+ }): Record<string, unknown>;
29
+ /**
30
+ * A `<Type>Filter` value with every operand converted to the type the schema
31
+ * declares for it: through `and`, `or` and `not`, on system keys and on every
32
+ * field, and one hop into a relation's target. Keys keep the order written.
33
+ */
34
+ export declare function typedFilter(summary: GraphQLSchemaSummary, model: GraphQLModelSummary, filter: Record<string, unknown>): Record<string, unknown>;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SYSTEM_OPERANDS = void 0;
4
+ exports.isObject = isObject;
5
+ exports.toScalar = toScalar;
6
+ exports.typedOperations = typedOperations;
7
+ exports.typedFilter = typedFilter;
8
+ /** Operators whose operand is a list. */
9
+ const LIST_OPERATORS = new Set(["in", "nin", "hasAny", "hasAll"]);
10
+ /** The operand types of a system filter (`IDFilter`, `DateTimeFilter`). */
11
+ exports.SYSTEM_OPERANDS = {
12
+ eq: "String", ne: "String", lt: "String", lte: "String", gt: "String", gte: "String",
13
+ in: "String", nin: "String", exists: "Boolean", null: "Boolean",
14
+ };
15
+ /** Filter keys that combine filters rather than name a field. */
16
+ const LOGIC_KEYS = new Set(["and", "or", "not"]);
17
+ /** A JSON object: a filter, or one field's operators. */
18
+ function isObject(value) {
19
+ return value !== null && typeof value === "object" && !Array.isArray(value);
20
+ }
21
+ /** One operand as the GraphQL scalar `type`, when it reads as one. */
22
+ function toScalar(type, value) {
23
+ switch (type) {
24
+ case "Float":
25
+ case "Int":
26
+ return typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value)) ? Number(value) : value;
27
+ case "Boolean":
28
+ return value === "true" ? true : value === "false" ? false : value;
29
+ case "String":
30
+ case "ID":
31
+ case "DateTime":
32
+ return typeof value === "number" || typeof value === "boolean" ? String(value) : value;
33
+ default:
34
+ return value;
35
+ }
36
+ }
37
+ /** One operand of `operator` as `type`; a list operand item by item. */
38
+ function typedOperand(operator, value, type, listText) {
39
+ if (value === null || value === undefined || !LIST_OPERATORS.has(operator))
40
+ return toScalar(type, value);
41
+ const items = Array.isArray(value) ? value : listText && typeof value === "string" ? value.split(",") : [value];
42
+ return items.map((item) => toScalar(type, item));
43
+ }
44
+ /**
45
+ * An operator object with each operand as the type `operands` gives its
46
+ * operator. `listText` also reads a list operand written as comma-separated
47
+ * text (`in: "1,2"`), as a REST query string carries it.
48
+ */
49
+ function typedOperations(operations, operands, { listText = false } = {}) {
50
+ return Object.fromEntries(Object.entries(operations).map(([operator, value]) => [operator, typedOperand(operator, value, operands[operator], listText)]));
51
+ }
52
+ function targetOf(summary, namespace) {
53
+ return namespace === null ? undefined : summary.models.find((m) => m.namespace === namespace);
54
+ }
55
+ /** A relation's own operators typed, and each hop into its target typed by the target field's operands. */
56
+ function typedRelation(operands, target, operations) {
57
+ const out = {};
58
+ for (const [name, value] of Object.entries(operations)) {
59
+ const hop = target?.fields.find((f) => f.name === name);
60
+ out[name] =
61
+ isObject(value) && (hop || name === "id")
62
+ ? typedOperations(value, hop ? hop.filterInputs ?? {} : exports.SYSTEM_OPERANDS)
63
+ : typedOperand(name, value, operands[name], false);
64
+ }
65
+ return out;
66
+ }
67
+ /**
68
+ * A `<Type>Filter` value with every operand converted to the type the schema
69
+ * declares for it: through `and`, `or` and `not`, on system keys and on every
70
+ * field, and one hop into a relation's target. Keys keep the order written.
71
+ */
72
+ function typedFilter(summary, model, filter) {
73
+ const out = {};
74
+ for (const [key, value] of Object.entries(filter)) {
75
+ if (LOGIC_KEYS.has(key)) {
76
+ const typed = (part) => (isObject(part) ? typedFilter(summary, model, part) : part);
77
+ out[key] = Array.isArray(value) ? value.map(typed) : typed(value);
78
+ continue;
79
+ }
80
+ if (!isObject(value)) {
81
+ out[key] = value;
82
+ continue;
83
+ }
84
+ const field = model.fields.find((f) => f.name === key);
85
+ if (!field) {
86
+ out[key] = typedOperations(value, model.systemFilters.find((system) => system.name === key)?.filterInputs ?? exports.SYSTEM_OPERANDS);
87
+ }
88
+ else if (field.kind === "relation" || field.kind === "relationList") {
89
+ out[key] = typedRelation(field.filterInputs ?? {}, targetOf(summary, field.target), value);
90
+ }
91
+ else {
92
+ out[key] = typedOperations(value, field.filterInputs ?? {});
93
+ }
94
+ }
95
+ return out;
96
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * introspection.ts — the introspection query this package sends, and the part
3
+ * of the answer it reads.
4
+ *
5
+ * One request carries both the standard introspection selection and the
6
+ * `version` root field, so a schema summary always says which platform version
7
+ * it describes. Kept byte-stable: `@capacms/mcp` sends the same text, and a
8
+ * persisted copy of it is one hash for every client.
9
+ *
10
+ * Deprecated arguments and input fields are asked for too, with their
11
+ * reasons: graphql-js leaves them out otherwise, and a document using one
12
+ * would then fail codegen as unknown the day Capa deprecates it, long before
13
+ * a later Capa-Version removes it.
14
+ */
15
+ export declare const INTROSPECTION_QUERY = "query CapaIntrospection {\n version\n __schema {\n queryType { name }\n types { ...FullType }\n }\n}\n\nfragment FullType on __Type {\n kind\n name\n description\n fields(includeDeprecated: true) {\n name\n description\n args(includeDeprecated: true) { ...InputValue }\n type { ...TypeRef }\n isDeprecated\n deprecationReason\n }\n inputFields(includeDeprecated: true) { ...InputValue }\n interfaces { ...TypeRef }\n enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason }\n possibleTypes { ...TypeRef }\n}\n\nfragment InputValue on __InputValue {\n name\n description\n type { ...TypeRef }\n defaultValue\n isDeprecated\n deprecationReason\n}\n\nfragment TypeRef on __Type {\n kind\n name\n ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } }\n}";
16
+ /**
17
+ * The part of the schema edit mode reads (`edit-mode.ts`): each type's name
18
+ * and description, and each field's name and description, which is all that
19
+ * says which GraphQL name is which Capa namespace (N9), and none of the
20
+ * arguments, input types and enum values that make up most of
21
+ * `INTROSPECTION_QUERY`'s answer. It selects the schema and nothing else, so
22
+ * the API answers a repeat from its memo of that answer.
23
+ */
24
+ export declare const FIELD_NAMES_QUERY = "query CapaFieldNames {\n __schema {\n types {\n kind\n name\n description\n fields(includeDeprecated: true) { name description }\n }\n }\n}";
25
+ /** `data` of a `CapaFieldNames` response. */
26
+ export interface CapaFieldNames {
27
+ __schema: {
28
+ types: Array<{
29
+ kind: string;
30
+ name: string;
31
+ description?: string | null;
32
+ fields?: Array<{
33
+ name: string;
34
+ description?: string | null;
35
+ }> | null;
36
+ }>;
37
+ };
38
+ }
39
+ export interface IntrospectionTypeRef {
40
+ kind: string;
41
+ name: string | null;
42
+ ofType?: IntrospectionTypeRef | null;
43
+ }
44
+ export interface IntrospectionInputValue {
45
+ name: string;
46
+ description?: string | null;
47
+ type: IntrospectionTypeRef;
48
+ defaultValue?: string | null;
49
+ isDeprecated?: boolean;
50
+ deprecationReason?: string | null;
51
+ }
52
+ export interface IntrospectionField {
53
+ name: string;
54
+ description?: string | null;
55
+ args: IntrospectionInputValue[];
56
+ type: IntrospectionTypeRef;
57
+ isDeprecated?: boolean;
58
+ deprecationReason?: string | null;
59
+ }
60
+ export interface IntrospectionType {
61
+ kind: string;
62
+ name: string;
63
+ description?: string | null;
64
+ fields?: IntrospectionField[] | null;
65
+ inputFields?: IntrospectionInputValue[] | null;
66
+ enumValues?: Array<{
67
+ name: string;
68
+ description?: string | null;
69
+ isDeprecated?: boolean;
70
+ deprecationReason?: string | null;
71
+ }> | null;
72
+ interfaces?: IntrospectionTypeRef[] | null;
73
+ possibleTypes?: IntrospectionTypeRef[] | null;
74
+ }
75
+ /** `data` of a `CapaIntrospection` response. */
76
+ export interface CapaIntrospection {
77
+ version?: string;
78
+ __schema: {
79
+ queryType: {
80
+ name: string;
81
+ };
82
+ types: IntrospectionType[];
83
+ };
84
+ }
85
+ /** The named type under any `NON_NULL` and `LIST` wrappers. */
86
+ export declare function namedType(ref: IntrospectionTypeRef): string;
87
+ /** The type as SDL writes it: `[Authors!]!`. */
88
+ export declare function printTypeRef(ref: IntrospectionTypeRef): string;
89
+ export declare function isListType(ref: IntrospectionTypeRef): boolean;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ /**
3
+ * introspection.ts — the introspection query this package sends, and the part
4
+ * of the answer it reads.
5
+ *
6
+ * One request carries both the standard introspection selection and the
7
+ * `version` root field, so a schema summary always says which platform version
8
+ * it describes. Kept byte-stable: `@capacms/mcp` sends the same text, and a
9
+ * persisted copy of it is one hash for every client.
10
+ *
11
+ * Deprecated arguments and input fields are asked for too, with their
12
+ * reasons: graphql-js leaves them out otherwise, and a document using one
13
+ * would then fail codegen as unknown the day Capa deprecates it, long before
14
+ * a later Capa-Version removes it.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.FIELD_NAMES_QUERY = exports.INTROSPECTION_QUERY = void 0;
18
+ exports.namedType = namedType;
19
+ exports.printTypeRef = printTypeRef;
20
+ exports.isListType = isListType;
21
+ exports.INTROSPECTION_QUERY = `query CapaIntrospection {
22
+ version
23
+ __schema {
24
+ queryType { name }
25
+ types { ...FullType }
26
+ }
27
+ }
28
+
29
+ fragment FullType on __Type {
30
+ kind
31
+ name
32
+ description
33
+ fields(includeDeprecated: true) {
34
+ name
35
+ description
36
+ args(includeDeprecated: true) { ...InputValue }
37
+ type { ...TypeRef }
38
+ isDeprecated
39
+ deprecationReason
40
+ }
41
+ inputFields(includeDeprecated: true) { ...InputValue }
42
+ interfaces { ...TypeRef }
43
+ enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason }
44
+ possibleTypes { ...TypeRef }
45
+ }
46
+
47
+ fragment InputValue on __InputValue {
48
+ name
49
+ description
50
+ type { ...TypeRef }
51
+ defaultValue
52
+ isDeprecated
53
+ deprecationReason
54
+ }
55
+
56
+ fragment TypeRef on __Type {
57
+ kind
58
+ name
59
+ ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } }
60
+ }`;
61
+ /**
62
+ * The part of the schema edit mode reads (`edit-mode.ts`): each type's name
63
+ * and description, and each field's name and description, which is all that
64
+ * says which GraphQL name is which Capa namespace (N9), and none of the
65
+ * arguments, input types and enum values that make up most of
66
+ * `INTROSPECTION_QUERY`'s answer. It selects the schema and nothing else, so
67
+ * the API answers a repeat from its memo of that answer.
68
+ */
69
+ exports.FIELD_NAMES_QUERY = `query CapaFieldNames {
70
+ __schema {
71
+ types {
72
+ kind
73
+ name
74
+ description
75
+ fields(includeDeprecated: true) { name description }
76
+ }
77
+ }
78
+ }`;
79
+ /** The named type under any `NON_NULL` and `LIST` wrappers. */
80
+ function namedType(ref) {
81
+ let current = ref;
82
+ while (current && current.name === null)
83
+ current = current.ofType;
84
+ return current?.name ?? "";
85
+ }
86
+ /** The type as SDL writes it: `[Authors!]!`. */
87
+ function printTypeRef(ref) {
88
+ if (ref.kind === "NON_NULL" && ref.ofType)
89
+ return `${printTypeRef(ref.ofType)}!`;
90
+ if (ref.kind === "LIST" && ref.ofType)
91
+ return `[${printTypeRef(ref.ofType)}]`;
92
+ return ref.name ?? "";
93
+ }
94
+ function isListType(ref) {
95
+ let current = ref;
96
+ while (current) {
97
+ if (current.kind === "LIST")
98
+ return true;
99
+ current = current.ofType;
100
+ }
101
+ return false;
102
+ }
@@ -0,0 +1,115 @@
1
+ import { type GraphQLFieldSummary, type GraphQLModelSummary, type GraphQLSchemaSummary } from "./summary";
2
+ /** One selected field: a name, or a relation or media field with its own fields. */
3
+ /**
4
+ * A relation list's sort: one value of the target's sort enum, e.g.
5
+ * `name_ASC`, or a list of that one value, the root's shape.
6
+ */
7
+ export type GraphQLFieldSpecSort = string | readonly [string];
8
+ export type GraphQLFieldSpec = string | {
9
+ field: string;
10
+ fields?: GraphQLFieldSpec[];
11
+ /** Array relations only: how many related entries, 1 to 200 (default 100). */
12
+ first?: number;
13
+ /** Array relations only: see `GraphQLFieldSpecSort`. */
14
+ sort?: GraphQLFieldSpecSort;
15
+ /**
16
+ * Array relations in a read of one entry only: the list's `endCursor`,
17
+ * to read on after it. A cursor names one entry's list, so a list read
18
+ * refuses it, as the API does.
19
+ */
20
+ after?: string;
21
+ };
22
+ export interface GraphQLQuerySpec {
23
+ /** Namespace (`articles`) or GraphQL type name (`Articles`). */
24
+ model: string;
25
+ /** `list`, or `single`, which needs `id`. The default is `single` when `id` is given, else `list`. */
26
+ mode?: "list" | "single";
27
+ /** The entry's UUID: reads that one entry. Refused beside `mode: "list"`. */
28
+ id?: string;
29
+ /** Omit for every non-relation field, single relations as `{ id }`, media as `{ id url alt }`. */
30
+ fields?: GraphQLFieldSpec[];
31
+ /**
32
+ * How many entries, 1 to 200 (default 25), as REST's `limit`. Beside
33
+ * `before` they are the entries just before that cursor, which GraphQL
34
+ * writes as `last`, so the query sends `last`.
35
+ */
36
+ first?: number;
37
+ /** An `endCursor`: the page after it. */
38
+ after?: string;
39
+ /**
40
+ * A `startCursor` to page back from: the `first` entries just before it.
41
+ * Or `"end"`, REST's `before=end`: the last `first` entries of the list,
42
+ * which GraphQL reads as `last` with no `before` (spec 17, amendment 132).
43
+ */
44
+ before?: string;
45
+ /** Up to 3 values of the model's sort enum, e.g. `["views_DESC"]`, or one alone, `"views_DESC"`. */
46
+ sort?: string | readonly string[];
47
+ /**
48
+ * The model's filter input, e.g. `{ views: { gte: 10 } }`. Each value is sent
49
+ * as the type the input declares: `{ views: { gte: "10" } }` goes as `10`,
50
+ * and `{ a_bool: { has: "true" } }` on a list of true/false values as `true`,
51
+ * which its `BooleanListFilter` takes.
52
+ */
53
+ filter?: Record<string, unknown>;
54
+ totalCount?: boolean;
55
+ operationName?: string;
56
+ }
57
+ /** REST's `before=end`: the last entries of the list, read back from its end (spec 17, amendment 132). */
58
+ export declare const END_OF_LIST = "end";
59
+ /** REST's and GraphQL's root page size when none is given. */
60
+ export declare const LIST_DEFAULT = 25;
61
+ export declare class CapaBuildError extends Error {
62
+ /** The closest real names, nearest first. */
63
+ readonly didYouMean: string[];
64
+ /** Every name that would have been accepted. */
65
+ readonly available: string[];
66
+ constructor(message: string, didYouMean?: string[], available?: string[]);
67
+ }
68
+ /** The system fields every model type has (N6), in schema order. */
69
+ export declare const SYSTEM_FIELDS: readonly ["id", "model", "status", "createdAt", "updatedAt", "publishedAt", "_version", "_tags", "_folder"];
70
+ export declare const MEDIA_FIELDS: readonly ["id", "url", "alt", "type", "width", "height"];
71
+ /**
72
+ * Relations a query may nest below its root entry. The API reads 5 levels of
73
+ * entries per root field and counts the root's own as the first, so 4
74
+ * relations below it (spec 17, amendment 78).
75
+ */
76
+ export declare const MAX_RELATION_DEPTH = 4;
77
+ export interface PlannedField {
78
+ /** GraphQL name. */
79
+ name: string;
80
+ /** The customer field, or null for a system field or a media subfield. */
81
+ field: GraphQLFieldSummary | null;
82
+ /** Relation, relation list and media fields carry what they select. */
83
+ children?: PlannedField[];
84
+ /** The target model of an expanded relation. */
85
+ target?: GraphQLModelSummary;
86
+ first?: number;
87
+ sort?: string;
88
+ after?: string;
89
+ }
90
+ export interface PlannedQuery {
91
+ model: GraphQLModelSummary;
92
+ mode: "list" | "single";
93
+ id?: string;
94
+ fields: PlannedField[];
95
+ first?: number;
96
+ after?: string;
97
+ before?: string;
98
+ sort?: string[];
99
+ filter?: Record<string, unknown>;
100
+ totalCount: boolean;
101
+ operationName: string;
102
+ summary: GraphQLSchemaSummary;
103
+ }
104
+ /** Names within two edits of `wanted`, nearest first then by name, at most three. */
105
+ export declare function didYouMean(wanted: string, candidates: readonly string[]): string[];
106
+ /**
107
+ * A model by namespace or type name, then by any of those or its display name
108
+ * ignoring case (`Writers`, `scalar specimens`). An unknown name is refused
109
+ * with the nearest models, each by namespace, whichever of its names was near.
110
+ */
111
+ export declare function findModel(summary: GraphQLSchemaSummary, wanted: string): GraphQLModelSummary;
112
+ /** Whether `key` under `field`'s filter is a hop into a field of the related model rather than an operator. */
113
+ export declare function isHop(field: GraphQLFieldSummary, key: string): boolean;
114
+ /** Resolve and check a spec. Throws `CapaBuildError`. */
115
+ export declare function planQuery(summary: GraphQLSchemaSummary, spec: GraphQLQuerySpec): PlannedQuery;