@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
|
@@ -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;
|
package/dist/projection-suss.js
CHANGED
|
@@ -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
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
81
|
-
if (
|
|
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
|
-
|
|
101
|
-
const
|
|
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(
|
|
115
|
+
return `${JSON.stringify(key)}:${encodeCanonical(entryValue, `${path}.${key}`, ancestors)}`;
|
|
104
116
|
});
|
|
105
|
-
return `{${
|
|
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
|
|
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
|
|
284
|
+
if (key === null || kind === undefined)
|
|
205
285
|
throw new TypeError("Invalid graph record identity.");
|
|
206
|
-
const dependencies =
|
|
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
|
-
|
|
298
|
+
const envelope = exactKnowledgeGraphRecordEnvelopeV1(value);
|
|
299
|
+
if (envelope === null)
|
|
219
300
|
return null;
|
|
220
|
-
const recordSha256 = parseSha256Hex(
|
|
221
|
-
const
|
|
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
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
72
|
-
if (
|
|
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
|
-
|
|
92
|
-
const
|
|
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(
|
|
106
|
+
return `${JSON.stringify(key)}:${encodeCanonical(entryValue, `${path}.${key}`, ancestors)}`;
|
|
95
107
|
});
|
|
96
|
-
return `{${
|
|
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
|
|
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
|
|
281
|
+
if (key === null || kind === undefined)
|
|
217
282
|
throw new TypeError("Invalid graph record identity.");
|
|
218
|
-
const dependencies =
|
|
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
|
-
|
|
295
|
+
const envelope = exactKnowledgeGraphRecordEnvelopeV1(value);
|
|
296
|
+
if (envelope === null)
|
|
231
297
|
return null;
|
|
232
|
-
const recordSha256 = parseSha256Hex(
|
|
233
|
-
const
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"semantic-cloud.d.ts","sourceRoot":"","sources":["../src/semantic-cloud.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC"}
|