@hraness/oh 0.2.7 → 0.3.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 (55) hide show
  1. package/README.md +116 -12
  2. package/dist/canonical.d.ts.map +1 -1
  3. package/dist/cli.d.ts +1 -1
  4. package/dist/cli.js +91 -19
  5. package/dist/cloudflare-embedding.d.ts +104 -0
  6. package/dist/cloudflare-embedding.d.ts.map +1 -0
  7. package/dist/graph.d.ts.map +1 -1
  8. package/dist/index.js +90 -18
  9. package/dist/libsql-semantic.d.ts +111 -0
  10. package/dist/libsql-semantic.d.ts.map +1 -0
  11. package/dist/libsql.js +105 -18
  12. package/dist/memory-page.d.ts +2 -0
  13. package/dist/memory-page.d.ts.map +1 -0
  14. package/dist/memory-page.js +725 -0
  15. package/dist/memory-pages.d.ts +76 -0
  16. package/dist/memory-pages.d.ts.map +1 -0
  17. package/dist/memory.d.ts +1 -0
  18. package/dist/memory.d.ts.map +1 -1
  19. package/dist/memory.js +478 -18
  20. package/dist/projection-public.js +105 -18
  21. package/dist/projection-suss.js +105 -18
  22. package/dist/sdk.js +90 -18
  23. package/dist/semantic-cloud.d.ts +3 -0
  24. package/dist/semantic-cloud.d.ts.map +1 -0
  25. package/dist/semantic-cloud.js +1843 -0
  26. package/dist/semantic.d.ts.map +1 -1
  27. package/dist/semantic.js +104 -21
  28. package/dist/sqlite/index.js +90 -18
  29. package/dist/store.js +105 -18
  30. package/dist/sync.js +90 -18
  31. package/package.json +10 -2
  32. package/skills/oh/SKILL.md +28 -2
  33. package/spec/README.md +11 -3
  34. package/spec/manifest.json +9 -1
  35. package/spec/v1/cloudflare-embedding-profile.json +13 -0
  36. package/spec/v1/cloudflare-embedding-renderer.json +8 -0
  37. package/spec/v1/memory-page.md +153 -0
  38. package/spec/v1/memory-page.schema.json +154 -0
  39. package/spec/v1/memory.md +18 -0
  40. package/spec/v1/migration.md +13 -0
  41. package/spec/v1/semantic-cloud.md +87 -0
  42. package/src/canonical.ts +28 -13
  43. package/src/cli.ts +1 -1
  44. package/src/cloudflare-embedding.test.ts +306 -0
  45. package/src/cloudflare-embedding.ts +385 -0
  46. package/src/contracts.test.ts +20 -0
  47. package/src/graph.ts +63 -6
  48. package/src/libsql-semantic.test.ts +478 -0
  49. package/src/libsql-semantic.ts +1117 -0
  50. package/src/memory-page.ts +1 -0
  51. package/src/memory-pages.test.ts +277 -0
  52. package/src/memory-pages.ts +440 -0
  53. package/src/memory.ts +2 -0
  54. package/src/semantic-cloud.ts +2 -0
  55. package/src/semantic.ts +14 -3
@@ -60,17 +60,27 @@ function encodeCanonical(value, path, ancestors) {
60
60
  ancestors.add(value);
61
61
  try {
62
62
  if (Array.isArray(value)) {
63
- const encoded = [];
64
- for (let index = 0;index < value.length; index += 1) {
65
- if (!Object.hasOwn(value, index)) {
66
- throw new OhValidationError("sparse-array", `${path}[${index}]`, "must not contain holes");
67
- }
68
- encoded.push(encodeCanonical(value[index], `${path}[${index}]`, ancestors));
63
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
64
+ const length = lengthDescriptor?.value;
65
+ if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0) {
66
+ throw new OhValidationError("non-json-property", path, "array has an invalid length descriptor");
69
67
  }
70
- const extraKeys = Reflect.ownKeys(value).filter((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= value.length));
71
- if (extraKeys.length > 0) {
68
+ const ownKeys2 = Reflect.ownKeys(value);
69
+ if (!ownKeys2.includes("length") || ownKeys2.some((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= length))) {
72
70
  throw new OhValidationError("non-json-property", path, "array has non-index properties");
73
71
  }
72
+ const elements = [];
73
+ for (let index = 0;index < length; index += 1) {
74
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
75
+ if (descriptor === undefined) {
76
+ throw new OhValidationError("sparse-array", `${path}[${index}]`, "must not contain holes");
77
+ }
78
+ if (!descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined) {
79
+ throw new OhValidationError("non-json-property", `${path}[${index}]`, "must be an enumerable data property");
80
+ }
81
+ elements.push(descriptor.value);
82
+ }
83
+ const encoded = elements.map((element, index) => encodeCanonical(element, `${path}[${index}]`, ancestors));
74
84
  return `[${encoded.join(",")}]`;
75
85
  }
76
86
  if (!isPlainRecord(value)) {
@@ -80,19 +90,21 @@ function encodeCanonical(value, path, ancestors) {
80
90
  if (ownKeys.some((key) => typeof key !== "string")) {
81
91
  throw new OhValidationError("non-json-property", path, "object has a symbol property");
82
92
  }
93
+ const entries = [];
83
94
  const keys = ownKeys;
84
95
  for (const key of keys) {
85
96
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
86
97
  if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined) {
87
98
  throw new OhValidationError("non-json-property", `${path}.${key}`, "must be an enumerable data property");
88
99
  }
100
+ entries.push([key, descriptor.value]);
89
101
  }
90
- keys.sort();
91
- const entries = keys.map((key) => {
102
+ entries.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
103
+ const encodedEntries = entries.map(([key, entryValue]) => {
92
104
  assertUnicodeScalarString(key, `${path}.<key>`);
93
- return `${JSON.stringify(key)}:${encodeCanonical(value[key], `${path}.${key}`, ancestors)}`;
105
+ return `${JSON.stringify(key)}:${encodeCanonical(entryValue, `${path}.${key}`, ancestors)}`;
94
106
  });
95
- return `{${entries.join(",")}}`;
107
+ return `{${encodedEntries.join(",")}}`;
96
108
  } finally {
97
109
  ancestors.delete(value);
98
110
  }
@@ -140,6 +152,21 @@ function canonicalNow() {
140
152
  function safeCode(value, maximumLength = 128) {
141
153
  return typeof value === "string" && value.length <= maximumLength && /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u.test(value) ? value : null;
142
154
  }
155
+ function boundedText(value, maximumBytes = 64 * 1024) {
156
+ if (typeof value !== "string" || value.length === 0 || value.normalize("NFC") !== value || utf8ByteLength(value) > maximumBytes)
157
+ return null;
158
+ try {
159
+ assertUnicodeScalarString(value, "$text");
160
+ } catch {
161
+ return null;
162
+ }
163
+ for (const character of value) {
164
+ const code = character.codePointAt(0) ?? 0;
165
+ if (code <= 8 || code >= 11 && code <= 12 || code >= 14 && code <= 31 || code >= 127 && code <= 159)
166
+ return null;
167
+ }
168
+ return value;
169
+ }
143
170
  function orderedUnique(values, key) {
144
171
  return values.every((value, index) => index === 0 || key(values[index - 1]) < key(value));
145
172
  }
@@ -183,17 +210,70 @@ var OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1 = [
183
210
  "view",
184
211
  "vocabulary"
185
212
  ];
213
+ var KNOWLEDGE_GRAPH_RECORD_KEYS_V1 = [
214
+ "dependencies",
215
+ "key",
216
+ "kind",
217
+ "recordSha256",
218
+ "v",
219
+ "value"
220
+ ];
221
+ function exactKnowledgeGraphRecordEnvelopeV1(value) {
222
+ try {
223
+ if (!isPlainRecord(value))
224
+ return null;
225
+ const ownKeys = Reflect.ownKeys(value);
226
+ if (ownKeys.length !== KNOWLEDGE_GRAPH_RECORD_KEYS_V1.length || ownKeys.some((key) => typeof key !== "string") || KNOWLEDGE_GRAPH_RECORD_KEYS_V1.some((key) => !ownKeys.includes(key)))
227
+ return null;
228
+ const detached = {};
229
+ for (const key of KNOWLEDGE_GRAPH_RECORD_KEYS_V1) {
230
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
231
+ if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
232
+ return null;
233
+ detached[key] = descriptor.value;
234
+ }
235
+ return detached;
236
+ } catch {
237
+ return null;
238
+ }
239
+ }
240
+ function exactGraphDependenciesV1(value) {
241
+ try {
242
+ if (!Array.isArray(value))
243
+ return null;
244
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
245
+ const length = lengthDescriptor?.value;
246
+ if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0 || length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord)
247
+ return null;
248
+ const ownKeys = Reflect.ownKeys(value);
249
+ if (ownKeys.length !== length + 1 || !ownKeys.includes("length") || ownKeys.some((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= length)))
250
+ return null;
251
+ const detached = [];
252
+ for (let index = 0;index < length; index += 1) {
253
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
254
+ if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
255
+ return null;
256
+ detached.push(descriptor.value);
257
+ }
258
+ return detached;
259
+ } catch {
260
+ return null;
261
+ }
262
+ }
186
263
  function recordKey(value) {
187
264
  return typeof value === "string" && value.length <= 512 && /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u.test(value) ? value : null;
188
265
  }
189
266
  function createKnowledgeGraphRecordV1(input) {
190
- if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"]) || input.v !== 1 || !Array.isArray(input.dependencies))
267
+ if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"]) || input.v !== 1)
191
268
  throw new TypeError("Invalid graph record input.");
269
+ const dependencyInput = exactGraphDependenciesV1(input.dependencies);
270
+ if (dependencyInput === null)
271
+ throw new TypeError("Invalid graph record dependencies.");
192
272
  const key = recordKey(input.key);
193
273
  const kind = OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1.find((candidate) => candidate === input.kind);
194
- if (key === null || kind === undefined || input.dependencies.length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord)
274
+ if (key === null || kind === undefined)
195
275
  throw new TypeError("Invalid graph record identity.");
196
- const dependencies = input.dependencies.map(recordKey);
276
+ const dependencies = dependencyInput.map(recordKey);
197
277
  if (dependencies.some((dependency) => dependency === null) || !orderedUnique(dependencies, String) || dependencies.includes(key)) {
198
278
  throw new TypeError("Graph dependencies must be ordered, unique, and non-reflexive.");
199
279
  }
@@ -205,10 +285,17 @@ function createKnowledgeGraphRecordV1(input) {
205
285
  return { ...payload, recordSha256: canonicalSha256(payload) };
206
286
  }
207
287
  function parseKnowledgeGraphRecordV1(value) {
208
- if (!isPlainRecord(value) || !Object.hasOwn(value, "recordSha256"))
288
+ const envelope = exactKnowledgeGraphRecordEnvelopeV1(value);
289
+ if (envelope === null)
209
290
  return null;
210
- const recordSha256 = parseSha256Hex(value.recordSha256);
211
- const { recordSha256: _digest, ...input } = value;
291
+ const recordSha256 = parseSha256Hex(envelope.recordSha256);
292
+ const input = {
293
+ dependencies: envelope.dependencies,
294
+ key: envelope.key,
295
+ kind: envelope.kind,
296
+ v: envelope.v,
297
+ value: envelope.value
298
+ };
212
299
  try {
213
300
  const created = createKnowledgeGraphRecordV1(input);
214
301
  return recordSha256 !== null && created.recordSha256 === recordSha256 ? { ...created, recordSha256 } : null;
@@ -70,17 +70,27 @@ function encodeCanonical(value, path, ancestors) {
70
70
  ancestors.add(value);
71
71
  try {
72
72
  if (Array.isArray(value)) {
73
- const encoded = [];
74
- for (let index = 0;index < value.length; index += 1) {
75
- if (!Object.hasOwn(value, index)) {
76
- throw new OhValidationError("sparse-array", `${path}[${index}]`, "must not contain holes");
77
- }
78
- encoded.push(encodeCanonical(value[index], `${path}[${index}]`, ancestors));
73
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
74
+ const length = lengthDescriptor?.value;
75
+ if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0) {
76
+ throw new OhValidationError("non-json-property", path, "array has an invalid length descriptor");
79
77
  }
80
- const extraKeys = Reflect.ownKeys(value).filter((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= value.length));
81
- if (extraKeys.length > 0) {
78
+ const ownKeys2 = Reflect.ownKeys(value);
79
+ if (!ownKeys2.includes("length") || ownKeys2.some((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= length))) {
82
80
  throw new OhValidationError("non-json-property", path, "array has non-index properties");
83
81
  }
82
+ const elements = [];
83
+ for (let index = 0;index < length; index += 1) {
84
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
85
+ if (descriptor === undefined) {
86
+ throw new OhValidationError("sparse-array", `${path}[${index}]`, "must not contain holes");
87
+ }
88
+ if (!descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined) {
89
+ throw new OhValidationError("non-json-property", `${path}[${index}]`, "must be an enumerable data property");
90
+ }
91
+ elements.push(descriptor.value);
92
+ }
93
+ const encoded = elements.map((element, index) => encodeCanonical(element, `${path}[${index}]`, ancestors));
84
94
  return `[${encoded.join(",")}]`;
85
95
  }
86
96
  if (!isPlainRecord(value)) {
@@ -90,19 +100,21 @@ function encodeCanonical(value, path, ancestors) {
90
100
  if (ownKeys.some((key) => typeof key !== "string")) {
91
101
  throw new OhValidationError("non-json-property", path, "object has a symbol property");
92
102
  }
103
+ const entries = [];
93
104
  const keys = ownKeys;
94
105
  for (const key of keys) {
95
106
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
96
107
  if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined) {
97
108
  throw new OhValidationError("non-json-property", `${path}.${key}`, "must be an enumerable data property");
98
109
  }
110
+ entries.push([key, descriptor.value]);
99
111
  }
100
- keys.sort();
101
- const entries = keys.map((key) => {
112
+ entries.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
113
+ const encodedEntries = entries.map(([key, entryValue]) => {
102
114
  assertUnicodeScalarString(key, `${path}.<key>`);
103
- return `${JSON.stringify(key)}:${encodeCanonical(value[key], `${path}.${key}`, ancestors)}`;
115
+ return `${JSON.stringify(key)}:${encodeCanonical(entryValue, `${path}.${key}`, ancestors)}`;
104
116
  });
105
- return `{${entries.join(",")}}`;
117
+ return `{${encodedEntries.join(",")}}`;
106
118
  } finally {
107
119
  ancestors.delete(value);
108
120
  }
@@ -150,6 +162,21 @@ function canonicalNow() {
150
162
  function safeCode(value, maximumLength = 128) {
151
163
  return typeof value === "string" && value.length <= maximumLength && /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u.test(value) ? value : null;
152
164
  }
165
+ function boundedText(value, maximumBytes = 64 * 1024) {
166
+ if (typeof value !== "string" || value.length === 0 || value.normalize("NFC") !== value || utf8ByteLength(value) > maximumBytes)
167
+ return null;
168
+ try {
169
+ assertUnicodeScalarString(value, "$text");
170
+ } catch {
171
+ return null;
172
+ }
173
+ for (const character of value) {
174
+ const code = character.codePointAt(0) ?? 0;
175
+ if (code <= 8 || code >= 11 && code <= 12 || code >= 14 && code <= 31 || code >= 127 && code <= 159)
176
+ return null;
177
+ }
178
+ return value;
179
+ }
153
180
  function orderedUnique(values, key) {
154
181
  return values.every((value, index) => index === 0 || key(values[index - 1]) < key(value));
155
182
  }
@@ -193,17 +220,70 @@ var OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1 = [
193
220
  "view",
194
221
  "vocabulary"
195
222
  ];
223
+ var KNOWLEDGE_GRAPH_RECORD_KEYS_V1 = [
224
+ "dependencies",
225
+ "key",
226
+ "kind",
227
+ "recordSha256",
228
+ "v",
229
+ "value"
230
+ ];
231
+ function exactKnowledgeGraphRecordEnvelopeV1(value) {
232
+ try {
233
+ if (!isPlainRecord(value))
234
+ return null;
235
+ const ownKeys = Reflect.ownKeys(value);
236
+ if (ownKeys.length !== KNOWLEDGE_GRAPH_RECORD_KEYS_V1.length || ownKeys.some((key) => typeof key !== "string") || KNOWLEDGE_GRAPH_RECORD_KEYS_V1.some((key) => !ownKeys.includes(key)))
237
+ return null;
238
+ const detached = {};
239
+ for (const key of KNOWLEDGE_GRAPH_RECORD_KEYS_V1) {
240
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
241
+ if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
242
+ return null;
243
+ detached[key] = descriptor.value;
244
+ }
245
+ return detached;
246
+ } catch {
247
+ return null;
248
+ }
249
+ }
250
+ function exactGraphDependenciesV1(value) {
251
+ try {
252
+ if (!Array.isArray(value))
253
+ return null;
254
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
255
+ const length = lengthDescriptor?.value;
256
+ if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0 || length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord)
257
+ return null;
258
+ const ownKeys = Reflect.ownKeys(value);
259
+ if (ownKeys.length !== length + 1 || !ownKeys.includes("length") || ownKeys.some((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= length)))
260
+ return null;
261
+ const detached = [];
262
+ for (let index = 0;index < length; index += 1) {
263
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
264
+ if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
265
+ return null;
266
+ detached.push(descriptor.value);
267
+ }
268
+ return detached;
269
+ } catch {
270
+ return null;
271
+ }
272
+ }
196
273
  function recordKey(value) {
197
274
  return typeof value === "string" && value.length <= 512 && /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u.test(value) ? value : null;
198
275
  }
199
276
  function createKnowledgeGraphRecordV1(input) {
200
- if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"]) || input.v !== 1 || !Array.isArray(input.dependencies))
277
+ if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"]) || input.v !== 1)
201
278
  throw new TypeError("Invalid graph record input.");
279
+ const dependencyInput = exactGraphDependenciesV1(input.dependencies);
280
+ if (dependencyInput === null)
281
+ throw new TypeError("Invalid graph record dependencies.");
202
282
  const key = recordKey(input.key);
203
283
  const kind = OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1.find((candidate) => candidate === input.kind);
204
- if (key === null || kind === undefined || input.dependencies.length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord)
284
+ if (key === null || kind === undefined)
205
285
  throw new TypeError("Invalid graph record identity.");
206
- const dependencies = input.dependencies.map(recordKey);
286
+ const dependencies = dependencyInput.map(recordKey);
207
287
  if (dependencies.some((dependency) => dependency === null) || !orderedUnique(dependencies, String) || dependencies.includes(key)) {
208
288
  throw new TypeError("Graph dependencies must be ordered, unique, and non-reflexive.");
209
289
  }
@@ -215,10 +295,17 @@ function createKnowledgeGraphRecordV1(input) {
215
295
  return { ...payload, recordSha256: canonicalSha256(payload) };
216
296
  }
217
297
  function parseKnowledgeGraphRecordV1(value) {
218
- if (!isPlainRecord(value) || !Object.hasOwn(value, "recordSha256"))
298
+ const envelope = exactKnowledgeGraphRecordEnvelopeV1(value);
299
+ if (envelope === null)
219
300
  return null;
220
- const recordSha256 = parseSha256Hex(value.recordSha256);
221
- const { recordSha256: _digest, ...input } = value;
301
+ const recordSha256 = parseSha256Hex(envelope.recordSha256);
302
+ const input = {
303
+ dependencies: envelope.dependencies,
304
+ key: envelope.key,
305
+ kind: envelope.kind,
306
+ v: envelope.v,
307
+ value: envelope.value
308
+ };
222
309
  try {
223
310
  const created = createKnowledgeGraphRecordV1(input);
224
311
  return recordSha256 !== null && created.recordSha256 === recordSha256 ? { ...created, recordSha256 } : null;
package/dist/sdk.js CHANGED
@@ -61,17 +61,27 @@ function encodeCanonical(value, path, ancestors) {
61
61
  ancestors.add(value);
62
62
  try {
63
63
  if (Array.isArray(value)) {
64
- const encoded = [];
65
- for (let index = 0;index < value.length; index += 1) {
66
- if (!Object.hasOwn(value, index)) {
67
- throw new OhValidationError("sparse-array", `${path}[${index}]`, "must not contain holes");
68
- }
69
- encoded.push(encodeCanonical(value[index], `${path}[${index}]`, ancestors));
64
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
65
+ const length = lengthDescriptor?.value;
66
+ if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0) {
67
+ throw new OhValidationError("non-json-property", path, "array has an invalid length descriptor");
70
68
  }
71
- const extraKeys = Reflect.ownKeys(value).filter((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= value.length));
72
- if (extraKeys.length > 0) {
69
+ const ownKeys2 = Reflect.ownKeys(value);
70
+ if (!ownKeys2.includes("length") || ownKeys2.some((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= length))) {
73
71
  throw new OhValidationError("non-json-property", path, "array has non-index properties");
74
72
  }
73
+ const elements = [];
74
+ for (let index = 0;index < length; index += 1) {
75
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
76
+ if (descriptor === undefined) {
77
+ throw new OhValidationError("sparse-array", `${path}[${index}]`, "must not contain holes");
78
+ }
79
+ if (!descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined) {
80
+ throw new OhValidationError("non-json-property", `${path}[${index}]`, "must be an enumerable data property");
81
+ }
82
+ elements.push(descriptor.value);
83
+ }
84
+ const encoded = elements.map((element, index) => encodeCanonical(element, `${path}[${index}]`, ancestors));
75
85
  return `[${encoded.join(",")}]`;
76
86
  }
77
87
  if (!isPlainRecord(value)) {
@@ -81,19 +91,21 @@ function encodeCanonical(value, path, ancestors) {
81
91
  if (ownKeys.some((key) => typeof key !== "string")) {
82
92
  throw new OhValidationError("non-json-property", path, "object has a symbol property");
83
93
  }
94
+ const entries = [];
84
95
  const keys = ownKeys;
85
96
  for (const key of keys) {
86
97
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
87
98
  if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined) {
88
99
  throw new OhValidationError("non-json-property", `${path}.${key}`, "must be an enumerable data property");
89
100
  }
101
+ entries.push([key, descriptor.value]);
90
102
  }
91
- keys.sort();
92
- const entries = keys.map((key) => {
103
+ entries.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
104
+ const encodedEntries = entries.map(([key, entryValue]) => {
93
105
  assertUnicodeScalarString(key, `${path}.<key>`);
94
- return `${JSON.stringify(key)}:${encodeCanonical(value[key], `${path}.${key}`, ancestors)}`;
106
+ return `${JSON.stringify(key)}:${encodeCanonical(entryValue, `${path}.${key}`, ancestors)}`;
95
107
  });
96
- return `{${entries.join(",")}}`;
108
+ return `{${encodedEntries.join(",")}}`;
97
109
  } finally {
98
110
  ancestors.delete(value);
99
111
  }
@@ -205,17 +217,70 @@ var OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1 = [
205
217
  "view",
206
218
  "vocabulary"
207
219
  ];
220
+ var KNOWLEDGE_GRAPH_RECORD_KEYS_V1 = [
221
+ "dependencies",
222
+ "key",
223
+ "kind",
224
+ "recordSha256",
225
+ "v",
226
+ "value"
227
+ ];
228
+ function exactKnowledgeGraphRecordEnvelopeV1(value) {
229
+ try {
230
+ if (!isPlainRecord(value))
231
+ return null;
232
+ const ownKeys = Reflect.ownKeys(value);
233
+ if (ownKeys.length !== KNOWLEDGE_GRAPH_RECORD_KEYS_V1.length || ownKeys.some((key) => typeof key !== "string") || KNOWLEDGE_GRAPH_RECORD_KEYS_V1.some((key) => !ownKeys.includes(key)))
234
+ return null;
235
+ const detached = {};
236
+ for (const key of KNOWLEDGE_GRAPH_RECORD_KEYS_V1) {
237
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
238
+ if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
239
+ return null;
240
+ detached[key] = descriptor.value;
241
+ }
242
+ return detached;
243
+ } catch {
244
+ return null;
245
+ }
246
+ }
247
+ function exactGraphDependenciesV1(value) {
248
+ try {
249
+ if (!Array.isArray(value))
250
+ return null;
251
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
252
+ const length = lengthDescriptor?.value;
253
+ if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0 || length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord)
254
+ return null;
255
+ const ownKeys = Reflect.ownKeys(value);
256
+ if (ownKeys.length !== length + 1 || !ownKeys.includes("length") || ownKeys.some((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= length)))
257
+ return null;
258
+ const detached = [];
259
+ for (let index = 0;index < length; index += 1) {
260
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
261
+ if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
262
+ return null;
263
+ detached.push(descriptor.value);
264
+ }
265
+ return detached;
266
+ } catch {
267
+ return null;
268
+ }
269
+ }
208
270
  function recordKey(value) {
209
271
  return typeof value === "string" && value.length <= 512 && /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u.test(value) ? value : null;
210
272
  }
211
273
  function createKnowledgeGraphRecordV1(input) {
212
- if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"]) || input.v !== 1 || !Array.isArray(input.dependencies))
274
+ if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"]) || input.v !== 1)
213
275
  throw new TypeError("Invalid graph record input.");
276
+ const dependencyInput = exactGraphDependenciesV1(input.dependencies);
277
+ if (dependencyInput === null)
278
+ throw new TypeError("Invalid graph record dependencies.");
214
279
  const key = recordKey(input.key);
215
280
  const kind = OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1.find((candidate) => candidate === input.kind);
216
- if (key === null || kind === undefined || input.dependencies.length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord)
281
+ if (key === null || kind === undefined)
217
282
  throw new TypeError("Invalid graph record identity.");
218
- const dependencies = input.dependencies.map(recordKey);
283
+ const dependencies = dependencyInput.map(recordKey);
219
284
  if (dependencies.some((dependency) => dependency === null) || !orderedUnique(dependencies, String) || dependencies.includes(key)) {
220
285
  throw new TypeError("Graph dependencies must be ordered, unique, and non-reflexive.");
221
286
  }
@@ -227,10 +292,17 @@ function createKnowledgeGraphRecordV1(input) {
227
292
  return { ...payload, recordSha256: canonicalSha256(payload) };
228
293
  }
229
294
  function parseKnowledgeGraphRecordV1(value) {
230
- if (!isPlainRecord(value) || !Object.hasOwn(value, "recordSha256"))
295
+ const envelope = exactKnowledgeGraphRecordEnvelopeV1(value);
296
+ if (envelope === null)
231
297
  return null;
232
- const recordSha256 = parseSha256Hex(value.recordSha256);
233
- const { recordSha256: _digest, ...input } = value;
298
+ const recordSha256 = parseSha256Hex(envelope.recordSha256);
299
+ const input = {
300
+ dependencies: envelope.dependencies,
301
+ key: envelope.key,
302
+ kind: envelope.kind,
303
+ v: envelope.v,
304
+ value: envelope.value
305
+ };
234
306
  try {
235
307
  const created = createKnowledgeGraphRecordV1(input);
236
308
  return recordSha256 !== null && created.recordSha256 === recordSha256 ? { ...created, recordSha256 } : null;
@@ -0,0 +1,3 @@
1
+ export * from "./cloudflare-embedding";
2
+ export * from "./libsql-semantic";
3
+ //# sourceMappingURL=semantic-cloud.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-cloud.d.ts","sourceRoot":"","sources":["../src/semantic-cloud.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC"}