@hraness/oh 0.2.6 → 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.
- package/README.md +116 -12
- package/dist/canonical.d.ts.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +91 -19
- package/dist/cloudflare-embedding.d.ts +104 -0
- package/dist/cloudflare-embedding.d.ts.map +1 -0
- package/dist/graph.d.ts.map +1 -1
- package/dist/index.js +90 -18
- package/dist/libsql-semantic.d.ts +111 -0
- package/dist/libsql-semantic.d.ts.map +1 -0
- package/dist/libsql.js +105 -18
- package/dist/memory-page.d.ts +2 -0
- package/dist/memory-page.d.ts.map +1 -0
- package/dist/memory-page.js +725 -0
- package/dist/memory-pages.d.ts +76 -0
- package/dist/memory-pages.d.ts.map +1 -0
- package/dist/memory.d.ts +1 -0
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +478 -18
- package/dist/projection-public.js +105 -18
- package/dist/projection-suss.js +105 -18
- package/dist/sdk.js +90 -18
- package/dist/semantic-cloud.d.ts +3 -0
- package/dist/semantic-cloud.d.ts.map +1 -0
- package/dist/semantic-cloud.js +1843 -0
- package/dist/semantic.d.ts.map +1 -1
- package/dist/semantic.js +104 -21
- package/dist/sqlite/index.js +90 -18
- package/dist/store.js +105 -18
- package/dist/sync.js +90 -18
- package/package.json +10 -2
- package/skills/oh/SKILL.md +28 -2
- package/spec/README.md +11 -3
- package/spec/manifest.json +9 -1
- package/spec/v1/cloudflare-embedding-profile.json +13 -0
- package/spec/v1/cloudflare-embedding-renderer.json +8 -0
- package/spec/v1/memory-page.md +153 -0
- package/spec/v1/memory-page.schema.json +154 -0
- package/spec/v1/memory.md +18 -0
- package/spec/v1/migration.md +13 -0
- package/spec/v1/semantic-cloud.md +87 -0
- package/src/canonical.ts +28 -13
- package/src/cli.ts +1 -1
- package/src/cloudflare-embedding.test.ts +306 -0
- package/src/cloudflare-embedding.ts +385 -0
- package/src/contracts.test.ts +20 -0
- package/src/graph.ts +63 -6
- package/src/libsql-semantic.test.ts +478 -0
- package/src/libsql-semantic.ts +1117 -0
- package/src/memory-page.ts +1 -0
- package/src/memory-pages.test.ts +277 -0
- package/src/memory-pages.ts +440 -0
- package/src/memory.ts +2 -0
- package/src/semantic-cloud.ts +2 -0
- package/src/semantic.ts +14 -3
package/dist/semantic.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic.d.ts","sourceRoot":"","sources":["../src/semantic.ts"],"names":[],"mappings":"AAGA,OAAO,EACgB,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAsB,KAAK,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,eAAO,MAAM,uBAAuB;;;;;;;;;;EAUzB,CAAC;AAEZ,MAAM,MAAM,oBAAoB,GAAG,OAAO,uBAAuB,CAAC;AAElE,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAElF;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"semantic.d.ts","sourceRoot":"","sources":["../src/semantic.ts"],"names":[],"mappings":"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"}
|
package/dist/semantic.js
CHANGED
|
@@ -65,17 +65,27 @@ function encodeCanonical(value, path, ancestors) {
|
|
|
65
65
|
ancestors.add(value);
|
|
66
66
|
try {
|
|
67
67
|
if (Array.isArray(value)) {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
encoded.push(encodeCanonical(value[index], `${path}[${index}]`, ancestors));
|
|
68
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
69
|
+
const length = lengthDescriptor?.value;
|
|
70
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0) {
|
|
71
|
+
throw new OhValidationError("non-json-property", path, "array has an invalid length descriptor");
|
|
74
72
|
}
|
|
75
|
-
const
|
|
76
|
-
if (
|
|
73
|
+
const ownKeys2 = Reflect.ownKeys(value);
|
|
74
|
+
if (!ownKeys2.includes("length") || ownKeys2.some((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= length))) {
|
|
77
75
|
throw new OhValidationError("non-json-property", path, "array has non-index properties");
|
|
78
76
|
}
|
|
77
|
+
const elements = [];
|
|
78
|
+
for (let index = 0;index < length; index += 1) {
|
|
79
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
80
|
+
if (descriptor === undefined) {
|
|
81
|
+
throw new OhValidationError("sparse-array", `${path}[${index}]`, "must not contain holes");
|
|
82
|
+
}
|
|
83
|
+
if (!descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined) {
|
|
84
|
+
throw new OhValidationError("non-json-property", `${path}[${index}]`, "must be an enumerable data property");
|
|
85
|
+
}
|
|
86
|
+
elements.push(descriptor.value);
|
|
87
|
+
}
|
|
88
|
+
const encoded = elements.map((element, index) => encodeCanonical(element, `${path}[${index}]`, ancestors));
|
|
79
89
|
return `[${encoded.join(",")}]`;
|
|
80
90
|
}
|
|
81
91
|
if (!isPlainRecord(value)) {
|
|
@@ -85,19 +95,21 @@ function encodeCanonical(value, path, ancestors) {
|
|
|
85
95
|
if (ownKeys.some((key) => typeof key !== "string")) {
|
|
86
96
|
throw new OhValidationError("non-json-property", path, "object has a symbol property");
|
|
87
97
|
}
|
|
98
|
+
const entries = [];
|
|
88
99
|
const keys = ownKeys;
|
|
89
100
|
for (const key of keys) {
|
|
90
101
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
91
102
|
if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined) {
|
|
92
103
|
throw new OhValidationError("non-json-property", `${path}.${key}`, "must be an enumerable data property");
|
|
93
104
|
}
|
|
105
|
+
entries.push([key, descriptor.value]);
|
|
94
106
|
}
|
|
95
|
-
|
|
96
|
-
const
|
|
107
|
+
entries.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
108
|
+
const encodedEntries = entries.map(([key, entryValue]) => {
|
|
97
109
|
assertUnicodeScalarString(key, `${path}.<key>`);
|
|
98
|
-
return `${JSON.stringify(key)}:${encodeCanonical(
|
|
110
|
+
return `${JSON.stringify(key)}:${encodeCanonical(entryValue, `${path}.${key}`, ancestors)}`;
|
|
99
111
|
});
|
|
100
|
-
return `{${
|
|
112
|
+
return `{${encodedEntries.join(",")}}`;
|
|
101
113
|
} finally {
|
|
102
114
|
ancestors.delete(value);
|
|
103
115
|
}
|
|
@@ -209,17 +221,70 @@ var OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1 = [
|
|
|
209
221
|
"view",
|
|
210
222
|
"vocabulary"
|
|
211
223
|
];
|
|
224
|
+
var KNOWLEDGE_GRAPH_RECORD_KEYS_V1 = [
|
|
225
|
+
"dependencies",
|
|
226
|
+
"key",
|
|
227
|
+
"kind",
|
|
228
|
+
"recordSha256",
|
|
229
|
+
"v",
|
|
230
|
+
"value"
|
|
231
|
+
];
|
|
232
|
+
function exactKnowledgeGraphRecordEnvelopeV1(value) {
|
|
233
|
+
try {
|
|
234
|
+
if (!isPlainRecord(value))
|
|
235
|
+
return null;
|
|
236
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
237
|
+
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)))
|
|
238
|
+
return null;
|
|
239
|
+
const detached = {};
|
|
240
|
+
for (const key of KNOWLEDGE_GRAPH_RECORD_KEYS_V1) {
|
|
241
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
242
|
+
if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
|
|
243
|
+
return null;
|
|
244
|
+
detached[key] = descriptor.value;
|
|
245
|
+
}
|
|
246
|
+
return detached;
|
|
247
|
+
} catch {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function exactGraphDependenciesV1(value) {
|
|
252
|
+
try {
|
|
253
|
+
if (!Array.isArray(value))
|
|
254
|
+
return null;
|
|
255
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
256
|
+
const length = lengthDescriptor?.value;
|
|
257
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0 || length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord)
|
|
258
|
+
return null;
|
|
259
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
260
|
+
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)))
|
|
261
|
+
return null;
|
|
262
|
+
const detached = [];
|
|
263
|
+
for (let index = 0;index < length; index += 1) {
|
|
264
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
265
|
+
if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
|
|
266
|
+
return null;
|
|
267
|
+
detached.push(descriptor.value);
|
|
268
|
+
}
|
|
269
|
+
return detached;
|
|
270
|
+
} catch {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
212
274
|
function recordKey(value) {
|
|
213
275
|
return typeof value === "string" && value.length <= 512 && /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u.test(value) ? value : null;
|
|
214
276
|
}
|
|
215
277
|
function createKnowledgeGraphRecordV1(input) {
|
|
216
|
-
if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"]) || input.v !== 1
|
|
278
|
+
if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"]) || input.v !== 1)
|
|
217
279
|
throw new TypeError("Invalid graph record input.");
|
|
280
|
+
const dependencyInput = exactGraphDependenciesV1(input.dependencies);
|
|
281
|
+
if (dependencyInput === null)
|
|
282
|
+
throw new TypeError("Invalid graph record dependencies.");
|
|
218
283
|
const key = recordKey(input.key);
|
|
219
284
|
const kind = OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1.find((candidate) => candidate === input.kind);
|
|
220
|
-
if (key === null || kind === undefined
|
|
285
|
+
if (key === null || kind === undefined)
|
|
221
286
|
throw new TypeError("Invalid graph record identity.");
|
|
222
|
-
const dependencies =
|
|
287
|
+
const dependencies = dependencyInput.map(recordKey);
|
|
223
288
|
if (dependencies.some((dependency) => dependency === null) || !orderedUnique(dependencies, String) || dependencies.includes(key)) {
|
|
224
289
|
throw new TypeError("Graph dependencies must be ordered, unique, and non-reflexive.");
|
|
225
290
|
}
|
|
@@ -231,10 +296,17 @@ function createKnowledgeGraphRecordV1(input) {
|
|
|
231
296
|
return { ...payload, recordSha256: canonicalSha256(payload) };
|
|
232
297
|
}
|
|
233
298
|
function parseKnowledgeGraphRecordV1(value) {
|
|
234
|
-
|
|
299
|
+
const envelope = exactKnowledgeGraphRecordEnvelopeV1(value);
|
|
300
|
+
if (envelope === null)
|
|
235
301
|
return null;
|
|
236
|
-
const recordSha256 = parseSha256Hex(
|
|
237
|
-
const
|
|
302
|
+
const recordSha256 = parseSha256Hex(envelope.recordSha256);
|
|
303
|
+
const input = {
|
|
304
|
+
dependencies: envelope.dependencies,
|
|
305
|
+
key: envelope.key,
|
|
306
|
+
kind: envelope.kind,
|
|
307
|
+
v: envelope.v,
|
|
308
|
+
value: envelope.value
|
|
309
|
+
};
|
|
238
310
|
try {
|
|
239
311
|
const created = createKnowledgeGraphRecordV1(input);
|
|
240
312
|
return recordSha256 !== null && created.recordSha256 === recordSha256 ? { ...created, recordSha256 } : null;
|
|
@@ -431,10 +503,21 @@ function normalizeOhEmbeddingV1(vector) {
|
|
|
431
503
|
if (vector.length !== OH_EMBEDDING_PROFILE_V1.dimensions || vector.some((component) => !Number.isFinite(component))) {
|
|
432
504
|
throw new TypeError(`Embedding vectors must contain ${OH_EMBEDDING_PROFILE_V1.dimensions} finite values.`);
|
|
433
505
|
}
|
|
434
|
-
const
|
|
435
|
-
if (
|
|
506
|
+
const scale = vector.reduce((maximum, component) => Math.max(maximum, Math.abs(component)), 0);
|
|
507
|
+
if (scale === 0)
|
|
436
508
|
throw new TypeError("Embedding vectors must have nonzero magnitude.");
|
|
437
|
-
|
|
509
|
+
const scaledMagnitude = Math.sqrt(vector.reduce((sum, component) => {
|
|
510
|
+
const scaled = component / scale;
|
|
511
|
+
return sum + scaled * scaled;
|
|
512
|
+
}, 0));
|
|
513
|
+
if (!Number.isFinite(scaledMagnitude) || scaledMagnitude === 0) {
|
|
514
|
+
throw new TypeError("Embedding vectors must have finite nonzero magnitude.");
|
|
515
|
+
}
|
|
516
|
+
const normalized = vector.map((component) => component / scale / scaledMagnitude);
|
|
517
|
+
if (normalized.some((component) => !Number.isFinite(component))) {
|
|
518
|
+
throw new TypeError("Embedding vectors must normalize to finite values.");
|
|
519
|
+
}
|
|
520
|
+
return normalized;
|
|
438
521
|
}
|
|
439
522
|
function cosineSimilarityV1(left, right) {
|
|
440
523
|
const normalizedLeft = normalizeOhEmbeddingV1(left);
|
package/dist/sqlite/index.js
CHANGED
|
@@ -135,17 +135,27 @@ function encodeCanonical(value, path, ancestors) {
|
|
|
135
135
|
ancestors.add(value);
|
|
136
136
|
try {
|
|
137
137
|
if (Array.isArray(value)) {
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
encoded.push(encodeCanonical(value[index], `${path}[${index}]`, ancestors));
|
|
138
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
139
|
+
const length = lengthDescriptor?.value;
|
|
140
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0) {
|
|
141
|
+
throw new OhValidationError("non-json-property", path, "array has an invalid length descriptor");
|
|
144
142
|
}
|
|
145
|
-
const
|
|
146
|
-
if (
|
|
143
|
+
const ownKeys2 = Reflect.ownKeys(value);
|
|
144
|
+
if (!ownKeys2.includes("length") || ownKeys2.some((key) => key !== "length" && (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= length))) {
|
|
147
145
|
throw new OhValidationError("non-json-property", path, "array has non-index properties");
|
|
148
146
|
}
|
|
147
|
+
const elements = [];
|
|
148
|
+
for (let index = 0;index < length; index += 1) {
|
|
149
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
150
|
+
if (descriptor === undefined) {
|
|
151
|
+
throw new OhValidationError("sparse-array", `${path}[${index}]`, "must not contain holes");
|
|
152
|
+
}
|
|
153
|
+
if (!descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined) {
|
|
154
|
+
throw new OhValidationError("non-json-property", `${path}[${index}]`, "must be an enumerable data property");
|
|
155
|
+
}
|
|
156
|
+
elements.push(descriptor.value);
|
|
157
|
+
}
|
|
158
|
+
const encoded = elements.map((element, index) => encodeCanonical(element, `${path}[${index}]`, ancestors));
|
|
149
159
|
return `[${encoded.join(",")}]`;
|
|
150
160
|
}
|
|
151
161
|
if (!isPlainRecord(value)) {
|
|
@@ -155,19 +165,21 @@ function encodeCanonical(value, path, ancestors) {
|
|
|
155
165
|
if (ownKeys.some((key) => typeof key !== "string")) {
|
|
156
166
|
throw new OhValidationError("non-json-property", path, "object has a symbol property");
|
|
157
167
|
}
|
|
168
|
+
const entries = [];
|
|
158
169
|
const keys = ownKeys;
|
|
159
170
|
for (const key of keys) {
|
|
160
171
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
161
172
|
if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined) {
|
|
162
173
|
throw new OhValidationError("non-json-property", `${path}.${key}`, "must be an enumerable data property");
|
|
163
174
|
}
|
|
175
|
+
entries.push([key, descriptor.value]);
|
|
164
176
|
}
|
|
165
|
-
|
|
166
|
-
const
|
|
177
|
+
entries.sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
178
|
+
const encodedEntries = entries.map(([key, entryValue]) => {
|
|
167
179
|
assertUnicodeScalarString(key, `${path}.<key>`);
|
|
168
|
-
return `${JSON.stringify(key)}:${encodeCanonical(
|
|
180
|
+
return `${JSON.stringify(key)}:${encodeCanonical(entryValue, `${path}.${key}`, ancestors)}`;
|
|
169
181
|
});
|
|
170
|
-
return `{${
|
|
182
|
+
return `{${encodedEntries.join(",")}}`;
|
|
171
183
|
} finally {
|
|
172
184
|
ancestors.delete(value);
|
|
173
185
|
}
|
|
@@ -449,17 +461,70 @@ var OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1 = [
|
|
|
449
461
|
"view",
|
|
450
462
|
"vocabulary"
|
|
451
463
|
];
|
|
464
|
+
var KNOWLEDGE_GRAPH_RECORD_KEYS_V1 = [
|
|
465
|
+
"dependencies",
|
|
466
|
+
"key",
|
|
467
|
+
"kind",
|
|
468
|
+
"recordSha256",
|
|
469
|
+
"v",
|
|
470
|
+
"value"
|
|
471
|
+
];
|
|
472
|
+
function exactKnowledgeGraphRecordEnvelopeV1(value) {
|
|
473
|
+
try {
|
|
474
|
+
if (!isPlainRecord(value))
|
|
475
|
+
return null;
|
|
476
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
477
|
+
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)))
|
|
478
|
+
return null;
|
|
479
|
+
const detached = {};
|
|
480
|
+
for (const key of KNOWLEDGE_GRAPH_RECORD_KEYS_V1) {
|
|
481
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
482
|
+
if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
|
|
483
|
+
return null;
|
|
484
|
+
detached[key] = descriptor.value;
|
|
485
|
+
}
|
|
486
|
+
return detached;
|
|
487
|
+
} catch {
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
function exactGraphDependenciesV1(value) {
|
|
492
|
+
try {
|
|
493
|
+
if (!Array.isArray(value))
|
|
494
|
+
return null;
|
|
495
|
+
const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
|
|
496
|
+
const length = lengthDescriptor?.value;
|
|
497
|
+
if (typeof length !== "number" || !Number.isSafeInteger(length) || length < 0 || length > OH_GRAPH_LIMITS_V1.dependenciesPerRecord)
|
|
498
|
+
return null;
|
|
499
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
500
|
+
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)))
|
|
501
|
+
return null;
|
|
502
|
+
const detached = [];
|
|
503
|
+
for (let index = 0;index < length; index += 1) {
|
|
504
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
505
|
+
if (descriptor === undefined || !descriptor.enumerable || descriptor.get !== undefined || descriptor.set !== undefined)
|
|
506
|
+
return null;
|
|
507
|
+
detached.push(descriptor.value);
|
|
508
|
+
}
|
|
509
|
+
return detached;
|
|
510
|
+
} catch {
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
452
514
|
function recordKey(value) {
|
|
453
515
|
return typeof value === "string" && value.length <= 512 && /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u.test(value) ? value : null;
|
|
454
516
|
}
|
|
455
517
|
function createKnowledgeGraphRecordV1(input) {
|
|
456
|
-
if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"]) || input.v !== 1
|
|
518
|
+
if (!isPlainRecord(input) || !hasExactKeys(input, ["dependencies", "key", "kind", "v", "value"]) || input.v !== 1)
|
|
457
519
|
throw new TypeError("Invalid graph record input.");
|
|
520
|
+
const dependencyInput = exactGraphDependenciesV1(input.dependencies);
|
|
521
|
+
if (dependencyInput === null)
|
|
522
|
+
throw new TypeError("Invalid graph record dependencies.");
|
|
458
523
|
const key = recordKey(input.key);
|
|
459
524
|
const kind = OH_KNOWLEDGE_GRAPH_RECORD_KINDS_V1.find((candidate) => candidate === input.kind);
|
|
460
|
-
if (key === null || kind === undefined
|
|
525
|
+
if (key === null || kind === undefined)
|
|
461
526
|
throw new TypeError("Invalid graph record identity.");
|
|
462
|
-
const dependencies =
|
|
527
|
+
const dependencies = dependencyInput.map(recordKey);
|
|
463
528
|
if (dependencies.some((dependency) => dependency === null) || !orderedUnique(dependencies, String) || dependencies.includes(key)) {
|
|
464
529
|
throw new TypeError("Graph dependencies must be ordered, unique, and non-reflexive.");
|
|
465
530
|
}
|
|
@@ -471,10 +536,17 @@ function createKnowledgeGraphRecordV1(input) {
|
|
|
471
536
|
return { ...payload, recordSha256: canonicalSha256(payload) };
|
|
472
537
|
}
|
|
473
538
|
function parseKnowledgeGraphRecordV1(value) {
|
|
474
|
-
|
|
539
|
+
const envelope = exactKnowledgeGraphRecordEnvelopeV1(value);
|
|
540
|
+
if (envelope === null)
|
|
475
541
|
return null;
|
|
476
|
-
const recordSha256 = parseSha256Hex(
|
|
477
|
-
const
|
|
542
|
+
const recordSha256 = parseSha256Hex(envelope.recordSha256);
|
|
543
|
+
const input = {
|
|
544
|
+
dependencies: envelope.dependencies,
|
|
545
|
+
key: envelope.key,
|
|
546
|
+
kind: envelope.kind,
|
|
547
|
+
v: envelope.v,
|
|
548
|
+
value: envelope.value
|
|
549
|
+
};
|
|
478
550
|
try {
|
|
479
551
|
const created = createKnowledgeGraphRecordV1(input);
|
|
480
552
|
return recordSha256 !== null && created.recordSha256 === recordSha256 ? { ...created, recordSha256 } : null;
|
package/dist/store.js
CHANGED
|
@@ -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
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
71
|
-
if (
|
|
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
|
-
|
|
91
|
-
const
|
|
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(
|
|
105
|
+
return `${JSON.stringify(key)}:${encodeCanonical(entryValue, `${path}.${key}`, ancestors)}`;
|
|
94
106
|
});
|
|
95
|
-
return `{${
|
|
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
|
|
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
|
|
274
|
+
if (key === null || kind === undefined)
|
|
195
275
|
throw new TypeError("Invalid graph record identity.");
|
|
196
|
-
const dependencies =
|
|
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
|
-
|
|
288
|
+
const envelope = exactKnowledgeGraphRecordEnvelopeV1(value);
|
|
289
|
+
if (envelope === null)
|
|
209
290
|
return null;
|
|
210
|
-
const recordSha256 = parseSha256Hex(
|
|
211
|
-
const
|
|
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;
|