@knitli/openapi-mcp 0.0.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.
Files changed (116) hide show
  1. package/LICENSE-APACHE +202 -0
  2. package/LICENSE-MIT +21 -0
  3. package/README.md +362 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +249 -0
  6. package/dist/compile.d.ts +14 -0
  7. package/dist/compile.js +132 -0
  8. package/dist/compiler.d.ts +13 -0
  9. package/dist/compiler.js +7 -0
  10. package/dist/conformance/fixtures.d.ts +24 -0
  11. package/dist/conformance/fixtures.js +119 -0
  12. package/dist/conformance/index.d.ts +2 -0
  13. package/dist/conformance/index.js +2 -0
  14. package/dist/conformance/runtime-suite.d.ts +31 -0
  15. package/dist/conformance/runtime-suite.js +343 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +1 -0
  18. package/dist/load.d.ts +31 -0
  19. package/dist/load.js +20 -0
  20. package/dist/operations.d.ts +27 -0
  21. package/dist/operations.js +231 -0
  22. package/dist/permissions.d.ts +31 -0
  23. package/dist/permissions.js +89 -0
  24. package/dist/release/compile-release.d.ts +33 -0
  25. package/dist/release/compile-release.js +1620 -0
  26. package/dist/release/load-v4.d.ts +29 -0
  27. package/dist/release/load-v4.js +297 -0
  28. package/dist/release/manifest-builder.d.ts +19 -0
  29. package/dist/release/manifest-builder.js +28 -0
  30. package/dist/release/publish.d.ts +45 -0
  31. package/dist/release/publish.js +346 -0
  32. package/dist/release/reference-map.d.ts +53 -0
  33. package/dist/release/reference-map.js +347 -0
  34. package/dist/release/schema-v4.d.ts +5 -0
  35. package/dist/release/schema-v4.js +91 -0
  36. package/dist/runtime/action-permit.d.ts +11 -0
  37. package/dist/runtime/action-permit.js +1 -0
  38. package/dist/runtime/classify.d.ts +16 -0
  39. package/dist/runtime/classify.js +279 -0
  40. package/dist/runtime/credential-binding.d.ts +3 -0
  41. package/dist/runtime/credential-binding.js +101 -0
  42. package/dist/runtime/digest.d.ts +8 -0
  43. package/dist/runtime/digest.js +80 -0
  44. package/dist/runtime/errors.d.ts +14 -0
  45. package/dist/runtime/errors.js +15 -0
  46. package/dist/runtime/index.d.ts +20 -0
  47. package/dist/runtime/index.js +14 -0
  48. package/dist/runtime/manifest.d.ts +33 -0
  49. package/dist/runtime/manifest.js +489 -0
  50. package/dist/runtime/prepared-call.d.ts +39 -0
  51. package/dist/runtime/prepared-call.js +566 -0
  52. package/dist/runtime/references.d.ts +7 -0
  53. package/dist/runtime/references.js +197 -0
  54. package/dist/runtime/runtime.d.ts +25 -0
  55. package/dist/runtime/runtime.js +1272 -0
  56. package/dist/runtime/schema-keywords.d.ts +2 -0
  57. package/dist/runtime/schema-keywords.js +30 -0
  58. package/dist/runtime/schema-resolver.d.ts +10 -0
  59. package/dist/runtime/schema-resolver.js +237 -0
  60. package/dist/runtime/serialize.d.ts +12 -0
  61. package/dist/runtime/serialize.js +877 -0
  62. package/dist/runtime/store.d.ts +52 -0
  63. package/dist/runtime/store.js +582 -0
  64. package/dist/runtime/strict-json.d.ts +19 -0
  65. package/dist/runtime/strict-json.js +539 -0
  66. package/dist/runtime/types.d.ts +457 -0
  67. package/dist/runtime/types.js +1 -0
  68. package/dist/runtime/verify-record.d.ts +6 -0
  69. package/dist/runtime/verify-record.js +492 -0
  70. package/dist/runtime/versions.d.ts +63 -0
  71. package/dist/runtime/versions.js +86 -0
  72. package/dist/safety.d.ts +28 -0
  73. package/dist/safety.js +60 -0
  74. package/dist/schema.d.ts +5 -0
  75. package/dist/schema.js +52 -0
  76. package/dist/schemas.d.ts +9 -0
  77. package/dist/schemas.js +16 -0
  78. package/dist/sign.d.ts +20 -0
  79. package/dist/sign.js +55 -0
  80. package/dist/sqlite/auth.d.ts +33 -0
  81. package/dist/sqlite/auth.js +973 -0
  82. package/dist/sqlite/catalog-store.d.ts +20 -0
  83. package/dist/sqlite/catalog-store.js +370 -0
  84. package/dist/sqlite/destination-guard.d.ts +24 -0
  85. package/dist/sqlite/destination-guard.js +140 -0
  86. package/dist/sqlite/generation-store.d.ts +12 -0
  87. package/dist/sqlite/generation-store.js +721 -0
  88. package/dist/sqlite/guarded-fetch.d.ts +24 -0
  89. package/dist/sqlite/guarded-fetch.js +626 -0
  90. package/dist/sqlite/index.d.ts +5 -0
  91. package/dist/sqlite/index.js +5 -0
  92. package/dist/sqlite/oauth-resource.d.ts +2 -0
  93. package/dist/sqlite/oauth-resource.js +15 -0
  94. package/dist/sqlite/release-completion.d.ts +5 -0
  95. package/dist/sqlite/release-completion.js +80 -0
  96. package/dist/stdio/action-broker.d.ts +15 -0
  97. package/dist/stdio/action-broker.js +136 -0
  98. package/dist/stdio/authorizer.d.ts +30 -0
  99. package/dist/stdio/authorizer.js +484 -0
  100. package/dist/stdio/config.d.ts +77 -0
  101. package/dist/stdio/config.js +144 -0
  102. package/dist/stdio/exact-policy.d.ts +41 -0
  103. package/dist/stdio/exact-policy.js +361 -0
  104. package/dist/stdio/index.d.ts +6 -0
  105. package/dist/stdio/index.js +148 -0
  106. package/dist/stdio/render.d.ts +8 -0
  107. package/dist/stdio/render.js +253 -0
  108. package/dist/stdio/request-state.d.ts +18 -0
  109. package/dist/stdio/request-state.js +79 -0
  110. package/dist/stdio/search.d.ts +10 -0
  111. package/dist/stdio/search.js +76 -0
  112. package/dist/stdio/server.d.ts +29 -0
  113. package/dist/stdio/server.js +309 -0
  114. package/dist/types.d.ts +32 -0
  115. package/dist/types.js +1 -0
  116. package/package.json +131 -0
@@ -0,0 +1,52 @@
1
+ import type { CatalogStore } from "./types.ts";
2
+ import { type RuntimeLimits } from "./versions.ts";
3
+ /** Stable, redacted public errors required of every CatalogStore adapter. */
4
+ export declare const CATALOG_STORE_PUBLIC_MESSAGES: Readonly<{
5
+ catalogIdentityInvalid: "Catalog identity is invalid";
6
+ releaseIdentityInvalid: "Release identity is invalid";
7
+ operationIdentityInvalid: "Operation identity is invalid";
8
+ schemaIdentityInvalid: "Schema identity is invalid";
9
+ storedCatalogIdentifierInvalid: "Stored catalog identifier is invalid";
10
+ storedReleaseIdentifierInvalid: "Stored release identifier is invalid";
11
+ storedOperationIdentifierInvalid: "Stored operation identifier is invalid";
12
+ storedSchemaIdentifierInvalid: "Stored schema identifier is invalid";
13
+ searchQueryInvalid: "Search query is invalid";
14
+ searchLimitInvalid: "Search limit is invalid";
15
+ searchExpressionInvalid: "Search expression is invalid";
16
+ searchTransportTooManyRows: "Search transport returned too many rows";
17
+ searchTransportOutsideRequestedApi: "Search transport returned an operation outside the requested API";
18
+ searchTransportDuplicateRows: "Search transport returned duplicate rows";
19
+ manifestTransportUnavailable: "Manifest transport is unavailable";
20
+ manifestTransportAbsentOrAmbiguous: "Manifest transport row is absent or ambiguous";
21
+ manifestTransportRowInvalid: "Manifest transport row is invalid";
22
+ manifestTransportLimitExceeded: "Manifest transport row exceeds its limit";
23
+ manifestSignatureMetadataInvalid: "Manifest signature metadata is invalid";
24
+ manifestIdentityMismatch: "Manifest identity does not match request";
25
+ recordTransportUnavailable: "Stored record transport is unavailable";
26
+ recordTransportRowInvalid: "Stored record row is invalid";
27
+ recordDigestInvalid: "Stored record digest is invalid";
28
+ recordJsonInvalid: "Stored record JSON is invalid";
29
+ operationRowAmbiguous: "Stored operation row is ambiguous";
30
+ operationIdentifierMismatch: "Stored operation identifier does not match request";
31
+ schemaRequestLimitExceeded: "Schema request exceeds its limit";
32
+ schemaRequestInvalid: "Schema request is invalid";
33
+ schemaTransportTooManyRows: "Stored schema transport returned too many rows";
34
+ schemaRowsInvalid: "Stored schema rows are invalid";
35
+ schemaRowsIncomplete: "Stored schema rows are incomplete";
36
+ }>;
37
+ export type D1CatalogValue = string | number | null;
38
+ export interface D1CatalogResult<RowType> {
39
+ readonly success: boolean;
40
+ readonly results: readonly RowType[];
41
+ readonly error?: string;
42
+ }
43
+ export interface D1CatalogPreparedStatement {
44
+ bind(...values: readonly D1CatalogValue[]): D1CatalogPreparedStatement;
45
+ all<RowType extends Record<string, unknown>>(): Promise<D1CatalogResult<RowType>>;
46
+ first<RowType extends Record<string, unknown>>(): Promise<RowType | null>;
47
+ }
48
+ export interface D1CatalogDatabase {
49
+ prepare(sql: string): D1CatalogPreparedStatement;
50
+ }
51
+ /** Construct the Worker-safe D1 implementation using only fixed SQL statements. */
52
+ export declare function createD1CatalogStore(binding: D1CatalogDatabase, limits?: Partial<RuntimeLimits>): CatalogStore;
@@ -0,0 +1,582 @@
1
+ import { OpenApiMcpError } from "./errors.js";
2
+ import { parseTypedRecordId } from "./references.js";
3
+ import { canonicalJson, parseJsonStrict } from "./strict-json.js";
4
+ import { MAX_SEARCH_QUERY_BYTES, resolveRuntimeLimits, } from "./versions.js";
5
+ const GET_MANIFEST_SQL = `SELECT
6
+ CASE WHEN typeof(manifest_json) = 'text' AND length(CAST(manifest_json AS BLOB)) <= ? THEN manifest_json ELSE NULL END AS manifest_json,
7
+ length(CAST(manifest_json AS BLOB)) AS manifest_json_bytes,
8
+ CASE WHEN typeof(signature_algorithm) = 'text' AND length(CAST(signature_algorithm AS BLOB)) <= ? THEN signature_algorithm ELSE NULL END AS signature_algorithm,
9
+ length(CAST(signature_algorithm AS BLOB)) AS signature_algorithm_bytes,
10
+ CASE WHEN typeof(signature_key_id) = 'text' AND length(CAST(signature_key_id AS BLOB)) <= ? THEN signature_key_id ELSE NULL END AS signature_key_id,
11
+ length(CAST(signature_key_id AS BLOB)) AS signature_key_id_bytes,
12
+ CASE WHEN typeof(signature) = 'text' AND length(CAST(signature AS BLOB)) <= ? THEN signature ELSE NULL END AS signature,
13
+ length(CAST(signature AS BLOB)) AS signature_bytes
14
+ FROM release_metadata
15
+ WHERE catalog_id = ? AND release_id = ? AND format IN (4, 5) AND contract = 1
16
+ LIMIT 2;`;
17
+ const SEARCH_CANDIDATES_SQL = `SELECT
18
+ CASE WHEN typeof(o.catalog_id) = 'text' AND length(CAST(o.catalog_id AS BLOB)) <= ? THEN o.catalog_id ELSE NULL END AS catalog_id,
19
+ length(CAST(o.catalog_id AS BLOB)) AS catalog_id_bytes,
20
+ CASE WHEN typeof(o.release_id) = 'text' AND length(CAST(o.release_id AS BLOB)) <= ? THEN o.release_id ELSE NULL END AS release_id,
21
+ length(CAST(o.release_id AS BLOB)) AS release_id_bytes,
22
+ CASE WHEN typeof(o.record_id) = 'text' AND length(CAST(o.record_id AS BLOB)) <= ? THEN o.record_id ELSE NULL END AS record_id,
23
+ length(CAST(o.record_id AS BLOB)) AS record_id_bytes
24
+ FROM operations_fts
25
+ JOIN operations AS o ON o.rowid = operations_fts.rowid
26
+ JOIN release_metadata AS r
27
+ ON r.catalog_id = o.catalog_id AND r.release_id = o.release_id
28
+ WHERE operations_fts MATCH ?
29
+ AND (? IS NULL OR o.api = ?)
30
+ AND r.format IN (4, 5) AND r.contract = 1
31
+ ORDER BY bm25(operations_fts),
32
+ o.catalog_id COLLATE BINARY,
33
+ o.release_id COLLATE BINARY,
34
+ o.record_id COLLATE BINARY
35
+ LIMIT ?;`;
36
+ const GET_OPERATION_SQL = `SELECT
37
+ CASE WHEN typeof(o.record_id) = 'text' AND length(CAST(o.record_id AS BLOB)) <= ? THEN o.record_id ELSE NULL END AS record_id,
38
+ length(CAST(o.record_id AS BLOB)) AS record_id_bytes,
39
+ CASE WHEN typeof(o.logical_digest) = 'text' AND length(CAST(o.logical_digest AS BLOB)) <= ? THEN o.logical_digest ELSE NULL END AS logical_digest,
40
+ length(CAST(o.logical_digest AS BLOB)) AS logical_digest_bytes,
41
+ CASE WHEN typeof(o.record_json) = 'text' AND length(CAST(o.record_json AS BLOB)) <= ? THEN o.record_json ELSE NULL END AS record_json,
42
+ length(CAST(o.record_json AS BLOB)) AS record_json_bytes
43
+ FROM operations AS o
44
+ JOIN release_metadata AS r
45
+ ON r.catalog_id = o.catalog_id AND r.release_id = o.release_id
46
+ WHERE o.catalog_id = ? AND o.release_id = ? AND o.record_id = ?
47
+ AND r.format IN (4, 5) AND r.contract = 1
48
+ LIMIT 2;`;
49
+ const GET_SCHEMAS_SQL = `WITH requested(record_id) AS (
50
+ SELECT DISTINCT value FROM json_each(?) WHERE typeof(value) = 'text'
51
+ )
52
+ SELECT
53
+ CASE WHEN typeof(s.record_id) = 'text' AND length(CAST(s.record_id AS BLOB)) <= ? THEN s.record_id ELSE NULL END AS record_id,
54
+ length(CAST(s.record_id AS BLOB)) AS record_id_bytes,
55
+ CASE WHEN typeof(s.logical_digest) = 'text' AND length(CAST(s.logical_digest AS BLOB)) <= ? THEN s.logical_digest ELSE NULL END AS logical_digest,
56
+ length(CAST(s.logical_digest AS BLOB)) AS logical_digest_bytes,
57
+ CASE WHEN typeof(s.record_json) = 'text' AND length(CAST(s.record_json AS BLOB)) <= ? THEN s.record_json ELSE NULL END AS record_json,
58
+ length(CAST(s.record_json AS BLOB)) AS record_json_bytes
59
+ FROM requested
60
+ JOIN schemas AS s ON s.record_id = requested.record_id
61
+ JOIN release_metadata AS r
62
+ ON r.catalog_id = s.catalog_id AND r.release_id = s.release_id
63
+ WHERE s.catalog_id = ? AND s.release_id = ?
64
+ AND r.format IN (4, 5) AND r.contract = 1
65
+ ORDER BY s.record_id COLLATE BINARY
66
+ LIMIT ?;`;
67
+ const digestPattern = /^[0-9a-f]{64}$/;
68
+ const identifierPattern = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
69
+ const signaturePattern = /^[A-Za-z0-9_-]+$/;
70
+ const signatureAlgorithmBytes = 7;
71
+ const signatureKeyIdBytes = 128;
72
+ const signatureBytes = 256;
73
+ const catalogIdBytes = 128;
74
+ const releaseIdBytes = 128;
75
+ const operationIdBytes = 651;
76
+ const schemaIdBytes = 669;
77
+ const digestBytes = 64;
78
+ const textEncoder = new TextEncoder();
79
+ /** Stable, redacted public errors required of every CatalogStore adapter. */
80
+ export const CATALOG_STORE_PUBLIC_MESSAGES = Object.freeze({
81
+ catalogIdentityInvalid: "Catalog identity is invalid",
82
+ releaseIdentityInvalid: "Release identity is invalid",
83
+ operationIdentityInvalid: "Operation identity is invalid",
84
+ schemaIdentityInvalid: "Schema identity is invalid",
85
+ storedCatalogIdentifierInvalid: "Stored catalog identifier is invalid",
86
+ storedReleaseIdentifierInvalid: "Stored release identifier is invalid",
87
+ storedOperationIdentifierInvalid: "Stored operation identifier is invalid",
88
+ storedSchemaIdentifierInvalid: "Stored schema identifier is invalid",
89
+ searchQueryInvalid: "Search query is invalid",
90
+ searchLimitInvalid: "Search limit is invalid",
91
+ searchExpressionInvalid: "Search expression is invalid",
92
+ searchTransportTooManyRows: "Search transport returned too many rows",
93
+ searchTransportOutsideRequestedApi: "Search transport returned an operation outside the requested API",
94
+ searchTransportDuplicateRows: "Search transport returned duplicate rows",
95
+ manifestTransportUnavailable: "Manifest transport is unavailable",
96
+ manifestTransportAbsentOrAmbiguous: "Manifest transport row is absent or ambiguous",
97
+ manifestTransportRowInvalid: "Manifest transport row is invalid",
98
+ manifestTransportLimitExceeded: "Manifest transport row exceeds its limit",
99
+ manifestSignatureMetadataInvalid: "Manifest signature metadata is invalid",
100
+ manifestIdentityMismatch: "Manifest identity does not match request",
101
+ recordTransportUnavailable: "Stored record transport is unavailable",
102
+ recordTransportRowInvalid: "Stored record row is invalid",
103
+ recordDigestInvalid: "Stored record digest is invalid",
104
+ recordJsonInvalid: "Stored record JSON is invalid",
105
+ operationRowAmbiguous: "Stored operation row is ambiguous",
106
+ operationIdentifierMismatch: "Stored operation identifier does not match request",
107
+ schemaRequestLimitExceeded: "Schema request exceeds its limit",
108
+ schemaRequestInvalid: "Schema request is invalid",
109
+ schemaTransportTooManyRows: "Stored schema transport returned too many rows",
110
+ schemaRowsInvalid: "Stored schema rows are invalid",
111
+ schemaRowsIncomplete: "Stored schema rows are incomplete",
112
+ });
113
+ function failure(code, message) {
114
+ return new OpenApiMcpError(code, message);
115
+ }
116
+ function input(message) {
117
+ return new OpenApiMcpError("INPUT_INVALID", message);
118
+ }
119
+ function plainRow(value, expected, code) {
120
+ try {
121
+ if (typeof value !== "object" || value === null || Array.isArray(value))
122
+ throw new Error();
123
+ const prototype = Object.getPrototypeOf(value);
124
+ if (prototype !== null && prototype !== Object.prototype)
125
+ throw new Error();
126
+ if (Object.getOwnPropertySymbols(value).length !== 0)
127
+ throw new Error();
128
+ const keys = Object.getOwnPropertyNames(value).sort();
129
+ const sortedExpected = [...expected].sort();
130
+ if (keys.length !== sortedExpected.length ||
131
+ keys.some((key, index) => key !== sortedExpected[index]))
132
+ throw new Error();
133
+ const snapshot = Object.create(null);
134
+ for (const key of expected) {
135
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
136
+ if (!descriptor?.enumerable || !("value" in descriptor))
137
+ throw new Error();
138
+ snapshot[key] = descriptor.value;
139
+ }
140
+ return snapshot;
141
+ }
142
+ catch {
143
+ throw failure(code, code === "MANIFEST_INVALID"
144
+ ? CATALOG_STORE_PUBLIC_MESSAGES.manifestTransportRowInvalid
145
+ : CATALOG_STORE_PUBLIC_MESSAGES.recordTransportRowInvalid);
146
+ }
147
+ }
148
+ function stringField(row, key, code) {
149
+ const value = row[key];
150
+ if (typeof value !== "string")
151
+ throw failure(code, code === "MANIFEST_INVALID"
152
+ ? CATALOG_STORE_PUBLIC_MESSAGES.manifestTransportRowInvalid
153
+ : CATALOG_STORE_PUBLIC_MESSAGES.recordTransportRowInvalid);
154
+ return value;
155
+ }
156
+ function boundedTextField(row, key, byteLengthKey, maximumBytes, code, overflowMessage) {
157
+ const byteLength = row[byteLengthKey];
158
+ if (typeof byteLength !== "number" ||
159
+ !Number.isSafeInteger(byteLength) ||
160
+ byteLength < 0) {
161
+ throw failure(code, code === "MANIFEST_INVALID"
162
+ ? CATALOG_STORE_PUBLIC_MESSAGES.manifestTransportRowInvalid
163
+ : CATALOG_STORE_PUBLIC_MESSAGES.recordTransportRowInvalid);
164
+ }
165
+ if (byteLength > maximumBytes)
166
+ throw failure(code, overflowMessage);
167
+ const value = stringField(row, key, code);
168
+ if (textEncoder.encode(value).byteLength !== byteLength)
169
+ throw failure(code, code === "MANIFEST_INVALID"
170
+ ? CATALOG_STORE_PUBLIC_MESSAGES.manifestTransportRowInvalid
171
+ : CATALOG_STORE_PUBLIC_MESSAGES.recordTransportRowInvalid);
172
+ return value;
173
+ }
174
+ function catalogId(value) {
175
+ try {
176
+ if (typeof value !== "string")
177
+ throw new Error();
178
+ return parseTypedRecordId(`operation:${value}:x`).slice("operation:".length, -2);
179
+ }
180
+ catch {
181
+ throw input(CATALOG_STORE_PUBLIC_MESSAGES.catalogIdentityInvalid);
182
+ }
183
+ }
184
+ function releaseId(value) {
185
+ try {
186
+ if (typeof value !== "string")
187
+ throw new Error();
188
+ return parseTypedRecordId(`operation:${value}:x`).slice("operation:".length, -2);
189
+ }
190
+ catch {
191
+ throw input(CATALOG_STORE_PUBLIC_MESSAGES.releaseIdentityInvalid);
192
+ }
193
+ }
194
+ function storedCatalogId(value) {
195
+ try {
196
+ return parseTypedRecordId(`operation:${value}:x`).slice("operation:".length, -2);
197
+ }
198
+ catch {
199
+ throw failure("RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.storedCatalogIdentifierInvalid);
200
+ }
201
+ }
202
+ function storedReleaseId(value) {
203
+ try {
204
+ return parseTypedRecordId(`operation:${value}:x`).slice("operation:".length, -2);
205
+ }
206
+ catch {
207
+ throw failure("RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.storedReleaseIdentifierInvalid);
208
+ }
209
+ }
210
+ function operationId(value, code) {
211
+ try {
212
+ const parsed = parseTypedRecordId(value);
213
+ if (!parsed.startsWith("operation:"))
214
+ throw new Error();
215
+ return parsed;
216
+ }
217
+ catch {
218
+ throw failure(code, CATALOG_STORE_PUBLIC_MESSAGES.storedOperationIdentifierInvalid);
219
+ }
220
+ }
221
+ function requestedOperationId(value) {
222
+ try {
223
+ if (typeof value !== "string")
224
+ throw new Error();
225
+ const parsed = parseTypedRecordId(value);
226
+ if (!parsed.startsWith("operation:"))
227
+ throw new Error();
228
+ return parsed;
229
+ }
230
+ catch {
231
+ throw input(CATALOG_STORE_PUBLIC_MESSAGES.operationIdentityInvalid);
232
+ }
233
+ }
234
+ function schemaId(value, code) {
235
+ try {
236
+ const parsed = parseTypedRecordId(value);
237
+ if (!parsed.startsWith("schema:"))
238
+ throw new Error();
239
+ return parsed;
240
+ }
241
+ catch {
242
+ throw failure(code, CATALOG_STORE_PUBLIC_MESSAGES.storedSchemaIdentifierInvalid);
243
+ }
244
+ }
245
+ function requestedSchemaId(value) {
246
+ try {
247
+ if (typeof value !== "string")
248
+ throw new Error();
249
+ const parsed = parseTypedRecordId(value);
250
+ if (!parsed.startsWith("schema:"))
251
+ throw new Error();
252
+ return parsed;
253
+ }
254
+ catch {
255
+ throw input(CATALOG_STORE_PUBLIC_MESSAGES.schemaIdentityInvalid);
256
+ }
257
+ }
258
+ function requireManifestIdentity(manifestJson, catalog, release, limits) {
259
+ try {
260
+ const manifest = parseJsonStrict(manifestJson, {
261
+ maxBytes: limits.maxManifestBytes,
262
+ maxDepth: limits.maxJsonDepth,
263
+ maxKeys: limits.maxManifestRecords + 32,
264
+ });
265
+ if (typeof manifest !== "object" ||
266
+ manifest === null ||
267
+ Array.isArray(manifest))
268
+ throw new Error();
269
+ const manifestCatalog = Object.getOwnPropertyDescriptor(manifest, "catalogId");
270
+ const manifestRelease = Object.getOwnPropertyDescriptor(manifest, "releaseId");
271
+ if (!manifestCatalog?.enumerable ||
272
+ !("value" in manifestCatalog) ||
273
+ manifestCatalog.value !== catalog ||
274
+ !manifestRelease?.enumerable ||
275
+ !("value" in manifestRelease) ||
276
+ manifestRelease.value !== release)
277
+ throw new Error();
278
+ }
279
+ catch {
280
+ throw failure("MANIFEST_INVALID", CATALOG_STORE_PUBLIC_MESSAGES.manifestIdentityMismatch);
281
+ }
282
+ }
283
+ function decodeManifest(rows, catalog, release, limits) {
284
+ if (rows.length !== 1)
285
+ throw failure("MANIFEST_INVALID", CATALOG_STORE_PUBLIC_MESSAGES.manifestTransportAbsentOrAmbiguous);
286
+ const row = plainRow(rows[0], [
287
+ "manifest_json",
288
+ "manifest_json_bytes",
289
+ "signature_algorithm",
290
+ "signature_algorithm_bytes",
291
+ "signature_key_id",
292
+ "signature_key_id_bytes",
293
+ "signature",
294
+ "signature_bytes",
295
+ ], "MANIFEST_INVALID");
296
+ const manifestJson = boundedTextField(row, "manifest_json", "manifest_json_bytes", limits.maxManifestBytes, "MANIFEST_INVALID", CATALOG_STORE_PUBLIC_MESSAGES.manifestTransportLimitExceeded);
297
+ const algorithm = boundedTextField(row, "signature_algorithm", "signature_algorithm_bytes", signatureAlgorithmBytes, "MANIFEST_INVALID", CATALOG_STORE_PUBLIC_MESSAGES.manifestSignatureMetadataInvalid);
298
+ const keyId = boundedTextField(row, "signature_key_id", "signature_key_id_bytes", signatureKeyIdBytes, "MANIFEST_INVALID", CATALOG_STORE_PUBLIC_MESSAGES.manifestSignatureMetadataInvalid);
299
+ const signature = boundedTextField(row, "signature", "signature_bytes", signatureBytes, "MANIFEST_INVALID", CATALOG_STORE_PUBLIC_MESSAGES.manifestSignatureMetadataInvalid);
300
+ if (algorithm !== "Ed25519" ||
301
+ !identifierPattern.test(keyId) ||
302
+ keyId === "." ||
303
+ keyId === ".." ||
304
+ !signaturePattern.test(signature))
305
+ throw failure("MANIFEST_INVALID", CATALOG_STORE_PUBLIC_MESSAGES.manifestSignatureMetadataInvalid);
306
+ requireManifestIdentity(manifestJson, catalog, release, limits);
307
+ return { manifestJson, signature: { algorithm, keyId, signature } };
308
+ }
309
+ function decodeRecord(rowValue, kind, limits) {
310
+ const row = plainRow(rowValue, [
311
+ "record_id",
312
+ "record_id_bytes",
313
+ "logical_digest",
314
+ "logical_digest_bytes",
315
+ "record_json",
316
+ "record_json_bytes",
317
+ ], "RECORD_DIGEST_MISMATCH");
318
+ const idValue = boundedTextField(row, "record_id", "record_id_bytes", kind === "operation" ? operationIdBytes : schemaIdBytes, "RECORD_DIGEST_MISMATCH", kind === "operation"
319
+ ? CATALOG_STORE_PUBLIC_MESSAGES.storedOperationIdentifierInvalid
320
+ : CATALOG_STORE_PUBLIC_MESSAGES.storedSchemaIdentifierInvalid);
321
+ const id = kind === "operation"
322
+ ? operationId(idValue, "RECORD_DIGEST_MISMATCH")
323
+ : schemaId(idValue, "RECORD_DIGEST_MISMATCH");
324
+ const logicalDigest = boundedTextField(row, "logical_digest", "logical_digest_bytes", digestBytes, "RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.recordDigestInvalid);
325
+ if (!digestPattern.test(logicalDigest))
326
+ throw failure("RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.recordDigestInvalid);
327
+ const recordJson = boundedTextField(row, "record_json", "record_json_bytes", limits.maxRecordBytes, "RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.recordJsonInvalid);
328
+ try {
329
+ const record = parseJsonStrict(recordJson, {
330
+ maxBytes: limits.maxRecordBytes,
331
+ maxDepth: limits.maxJsonDepth,
332
+ maxKeys: limits.maxManifestRecords,
333
+ });
334
+ if (typeof record !== "object" || record === null || Array.isArray(record))
335
+ throw new Error();
336
+ const recordId = Object.getOwnPropertyDescriptor(record, "id");
337
+ if (!recordId?.enumerable ||
338
+ !("value" in recordId) ||
339
+ typeof recordId.value !== "string" ||
340
+ recordId.value !== id)
341
+ throw new Error();
342
+ if (kind === "operation")
343
+ operationId(recordId.value, "RECORD_DIGEST_MISMATCH");
344
+ else
345
+ schemaId(recordId.value, "RECORD_DIGEST_MISMATCH");
346
+ return {
347
+ id,
348
+ logicalDigest: logicalDigest,
349
+ record: record,
350
+ };
351
+ }
352
+ catch {
353
+ throw failure("RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.recordJsonInvalid);
354
+ }
355
+ }
356
+ function snapshotSearchQuery(value, limits) {
357
+ try {
358
+ if (typeof value !== "object" || value === null || Array.isArray(value))
359
+ throw new Error();
360
+ const prototype = Object.getPrototypeOf(value);
361
+ if (prototype !== null && prototype !== Object.prototype)
362
+ throw new Error();
363
+ if (Object.getOwnPropertySymbols(value).length !== 0)
364
+ throw new Error();
365
+ const api = Object.getOwnPropertyDescriptor(value, "api");
366
+ const expected = api === undefined ? ["limit", "query"] : ["api", "limit", "query"];
367
+ const names = Object.getOwnPropertyNames(value).sort();
368
+ if (names.length !== expected.length ||
369
+ names.some((key, index) => key !== expected[index]))
370
+ throw new Error();
371
+ const query = Object.getOwnPropertyDescriptor(value, "query");
372
+ const limit = Object.getOwnPropertyDescriptor(value, "limit");
373
+ if (!query?.enumerable ||
374
+ !("value" in query) ||
375
+ !limit?.enumerable ||
376
+ !("value" in limit) ||
377
+ (api !== undefined && (!api.enumerable || !("value" in api))))
378
+ throw new Error();
379
+ if (typeof query.value !== "string" || query.value.length === 0)
380
+ throw input(CATALOG_STORE_PUBLIC_MESSAGES.searchQueryInvalid);
381
+ if (query.value.length > MAX_SEARCH_QUERY_BYTES ||
382
+ new TextEncoder().encode(query.value).byteLength > MAX_SEARCH_QUERY_BYTES)
383
+ throw input(CATALOG_STORE_PUBLIC_MESSAGES.searchQueryInvalid);
384
+ if (!Number.isSafeInteger(limit.value) ||
385
+ limit.value < 1 ||
386
+ limit.value > limits.maxSearchResults)
387
+ throw input(CATALOG_STORE_PUBLIC_MESSAGES.searchLimitInvalid);
388
+ return {
389
+ query: query.value,
390
+ api: api === undefined || api.value === undefined
391
+ ? null
392
+ : catalogId(api.value),
393
+ limit: limit.value,
394
+ };
395
+ }
396
+ catch (error) {
397
+ if (error instanceof OpenApiMcpError)
398
+ throw error;
399
+ throw input(CATALOG_STORE_PUBLIC_MESSAGES.searchQueryInvalid);
400
+ }
401
+ }
402
+ function snapshotSchemaIds(value, limits) {
403
+ try {
404
+ if (!Array.isArray(value) ||
405
+ Object.getPrototypeOf(value) !== Array.prototype)
406
+ throw new Error();
407
+ if (Object.getOwnPropertySymbols(value).length !== 0)
408
+ throw new Error();
409
+ const length = Object.getOwnPropertyDescriptor(value, "length");
410
+ if (length === undefined ||
411
+ !("value" in length) ||
412
+ !Number.isSafeInteger(length.value) ||
413
+ length.value < 0)
414
+ throw new Error();
415
+ if (length.value > limits.maxManifestRecords)
416
+ throw input(CATALOG_STORE_PUBLIC_MESSAGES.schemaRequestLimitExceeded);
417
+ const names = Object.getOwnPropertyNames(value);
418
+ if (names.length !== length.value + 1 || !names.includes("length"))
419
+ throw new Error();
420
+ const ids = [];
421
+ let totalBytes = 0;
422
+ for (let index = 0; index < length.value; index += 1) {
423
+ const id = Object.getOwnPropertyDescriptor(value, String(index));
424
+ if (!id?.enumerable || !("value" in id))
425
+ throw new Error();
426
+ const parsed = requestedSchemaId(id.value);
427
+ totalBytes += textEncoder.encode(parsed).byteLength;
428
+ if (totalBytes > limits.maxSchemaClosureBytes)
429
+ throw input(CATALOG_STORE_PUBLIC_MESSAGES.schemaRequestLimitExceeded);
430
+ ids.push(parsed);
431
+ }
432
+ return ids;
433
+ }
434
+ catch (error) {
435
+ if (error instanceof OpenApiMcpError)
436
+ throw error;
437
+ throw input(CATALOG_STORE_PUBLIC_MESSAGES.schemaRequestInvalid);
438
+ }
439
+ }
440
+ async function d1All(database, sql, values, errorCode) {
441
+ try {
442
+ const result = await database
443
+ .prepare(sql)
444
+ .bind(...values)
445
+ .all();
446
+ if (!result.success || !Array.isArray(result.results)) {
447
+ if (errorCode === "INPUT_INVALID")
448
+ throw input(CATALOG_STORE_PUBLIC_MESSAGES.searchExpressionInvalid);
449
+ throw failure(errorCode, errorCode === "MANIFEST_INVALID"
450
+ ? CATALOG_STORE_PUBLIC_MESSAGES.manifestTransportUnavailable
451
+ : CATALOG_STORE_PUBLIC_MESSAGES.recordTransportUnavailable);
452
+ }
453
+ return [...result.results];
454
+ }
455
+ catch (error) {
456
+ if (error instanceof OpenApiMcpError)
457
+ throw error;
458
+ if (errorCode === "INPUT_INVALID")
459
+ throw input(CATALOG_STORE_PUBLIC_MESSAGES.searchExpressionInvalid);
460
+ throw failure(errorCode, errorCode === "MANIFEST_INVALID"
461
+ ? CATALOG_STORE_PUBLIC_MESSAGES.manifestTransportUnavailable
462
+ : CATALOG_STORE_PUBLIC_MESSAGES.recordTransportUnavailable);
463
+ }
464
+ }
465
+ class D1CatalogStore {
466
+ database;
467
+ limits;
468
+ constructor(database, limits) {
469
+ this.database = database;
470
+ this.limits = limits;
471
+ }
472
+ async getManifest(catalog, release) {
473
+ const catalogIdValue = catalogId(catalog);
474
+ const releaseIdValue = releaseId(release);
475
+ return decodeManifest(await d1All(this.database, GET_MANIFEST_SQL, [
476
+ this.limits.maxManifestBytes,
477
+ signatureAlgorithmBytes,
478
+ signatureKeyIdBytes,
479
+ signatureBytes,
480
+ catalogIdValue,
481
+ releaseIdValue,
482
+ ], "MANIFEST_INVALID"), catalogIdValue, releaseIdValue, this.limits);
483
+ }
484
+ async searchCandidates(query) {
485
+ const snapshot = snapshotSearchQuery(query, this.limits);
486
+ const rows = await d1All(this.database, SEARCH_CANDIDATES_SQL, [
487
+ catalogIdBytes,
488
+ releaseIdBytes,
489
+ operationIdBytes,
490
+ snapshot.query,
491
+ snapshot.api,
492
+ snapshot.api,
493
+ snapshot.limit,
494
+ ], "INPUT_INVALID");
495
+ if (rows.length > snapshot.limit)
496
+ throw failure("RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.searchTransportTooManyRows);
497
+ const seen = new Set();
498
+ return rows.map((value) => {
499
+ const row = plainRow(value, [
500
+ "catalog_id",
501
+ "catalog_id_bytes",
502
+ "release_id",
503
+ "release_id_bytes",
504
+ "record_id",
505
+ "record_id_bytes",
506
+ ], "RECORD_DIGEST_MISMATCH");
507
+ const catalogIdValue = storedCatalogId(boundedTextField(row, "catalog_id", "catalog_id_bytes", catalogIdBytes, "RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.storedCatalogIdentifierInvalid));
508
+ const releaseIdValue = storedReleaseId(boundedTextField(row, "release_id", "release_id_bytes", releaseIdBytes, "RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.storedReleaseIdentifierInvalid));
509
+ const operationIdValue = operationId(boundedTextField(row, "record_id", "record_id_bytes", operationIdBytes, "RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.storedOperationIdentifierInvalid), "RECORD_DIGEST_MISMATCH");
510
+ if (snapshot.api !== null &&
511
+ !operationIdValue.startsWith(`operation:${snapshot.api}:`))
512
+ throw failure("RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.searchTransportOutsideRequestedApi);
513
+ const key = `${catalogIdValue}\u0000${releaseIdValue}\u0000${operationIdValue}`;
514
+ if (seen.has(key))
515
+ throw failure("RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.searchTransportDuplicateRows);
516
+ seen.add(key);
517
+ return {
518
+ catalogId: catalogIdValue,
519
+ releaseId: releaseIdValue,
520
+ operationId: operationIdValue,
521
+ };
522
+ });
523
+ }
524
+ async getOperation(catalog, release, id) {
525
+ const requested = requestedOperationId(id);
526
+ const rows = await d1All(this.database, GET_OPERATION_SQL, [
527
+ operationIdBytes,
528
+ digestBytes,
529
+ this.limits.maxRecordBytes,
530
+ catalogId(catalog),
531
+ releaseId(release),
532
+ requested,
533
+ ], "RECORD_DIGEST_MISMATCH");
534
+ if (rows.length === 0)
535
+ return null;
536
+ if (rows.length !== 1)
537
+ throw failure("RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.operationRowAmbiguous);
538
+ const row = decodeRecord(rows[0], "operation", this.limits);
539
+ if (row.id !== requested)
540
+ throw failure("RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.operationIdentifierMismatch);
541
+ return row;
542
+ }
543
+ async getSchemas(catalog, release, ids) {
544
+ const catalogIdValue = catalogId(catalog);
545
+ const releaseIdValue = releaseId(release);
546
+ const schemaIds = snapshotSchemaIds(ids, this.limits);
547
+ if (schemaIds.length === 0)
548
+ return [];
549
+ const requested = new Set();
550
+ for (const id of schemaIds)
551
+ requested.add(id);
552
+ const requestedIds = [...requested].sort();
553
+ const requestJson = canonicalJson(requestedIds);
554
+ const rows = await d1All(this.database, GET_SCHEMAS_SQL, [
555
+ requestJson,
556
+ schemaIdBytes,
557
+ digestBytes,
558
+ this.limits.maxRecordBytes,
559
+ catalogIdValue,
560
+ releaseIdValue,
561
+ requested.size + 1,
562
+ ], "RECORD_DIGEST_MISMATCH");
563
+ if (rows.length > requested.size)
564
+ throw failure("RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.schemaTransportTooManyRows);
565
+ if (rows.length < requested.size)
566
+ throw failure("RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.schemaRowsIncomplete);
567
+ const records = [];
568
+ let previous = "";
569
+ for (const value of rows) {
570
+ const row = decodeRecord(value, "schema", this.limits);
571
+ if (!requested.has(row.id) || row.id <= previous)
572
+ throw failure("RECORD_DIGEST_MISMATCH", CATALOG_STORE_PUBLIC_MESSAGES.schemaRowsInvalid);
573
+ previous = row.id;
574
+ records.push(row);
575
+ }
576
+ return records;
577
+ }
578
+ }
579
+ /** Construct the Worker-safe D1 implementation using only fixed SQL statements. */
580
+ export function createD1CatalogStore(binding, limits) {
581
+ return new D1CatalogStore(binding, resolveRuntimeLimits(limits));
582
+ }
@@ -0,0 +1,19 @@
1
+ import type { JsonValue } from "./types.ts";
2
+ /** Bounds applied while accepting untrusted JSON text. */
3
+ export interface StrictJsonLimits {
4
+ maxBytes: number;
5
+ maxDepth: number;
6
+ maxKeys: number;
7
+ }
8
+ export declare const DEFAULT_STRICT_JSON_LIMITS: StrictJsonLimits;
9
+ /** Parse JSON without giving duplicate or prototype keys JavaScript object semantics. */
10
+ export declare function parseJsonStrict(text: string, limits?: StrictJsonLimits): JsonValue;
11
+ /** Deterministic JSON serialization for logical signed and hashed values. */
12
+ export declare function canonicalJson(value: JsonValue): string;
13
+ export interface BoundedCanonicalJsonLimits {
14
+ maxBytes: number;
15
+ maxDepth: number;
16
+ maxNodes: number;
17
+ }
18
+ /** Canonicalize hostile values while bounding output and traversal before allocation. */
19
+ export declare function canonicalJsonBounded(value: JsonValue, limits: BoundedCanonicalJsonLimits): string;