@peers-app/peers-sdk 0.23.4 → 0.23.6

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 (75) hide show
  1. package/dist/context/data-context.d.ts +36 -5
  2. package/dist/context/data-context.js +44 -9
  3. package/dist/context/user-context.d.ts +44 -0
  4. package/dist/context/user-context.js +91 -12
  5. package/dist/contracts/__tests__/contract-provider-router.test.js +26 -0
  6. package/dist/contracts/__tests__/contract-resolvers.test.js +44 -0
  7. package/dist/contracts/__tests__/registry.test.js +104 -0
  8. package/dist/contracts/contract-provider-router.d.ts +21 -0
  9. package/dist/contracts/contract-provider-router.js +31 -5
  10. package/dist/contracts/contract-proxy.d.ts +3 -0
  11. package/dist/contracts/contract-proxy.js +3 -0
  12. package/dist/contracts/contract-resolvers.d.ts +21 -3
  13. package/dist/contracts/contract-resolvers.js +105 -32
  14. package/dist/contracts/index.d.ts +3 -3
  15. package/dist/contracts/registry.d.ts +59 -0
  16. package/dist/contracts/registry.js +155 -25
  17. package/dist/contracts/system-contracts.js +3 -1
  18. package/dist/data/index.d.ts +1 -0
  19. package/dist/data/index.js +1 -0
  20. package/dist/data/orm/types.d.ts +19 -0
  21. package/dist/data/orm/types.js +11 -0
  22. package/dist/data/package-table-identity.d.ts +46 -0
  23. package/dist/data/package-table-identity.js +71 -0
  24. package/dist/data/package-table-identity.test.d.ts +1 -0
  25. package/dist/data/package-table-identity.test.js +46 -0
  26. package/dist/data/package-version-resolver.d.ts +13 -0
  27. package/dist/data/package-version-resolver.js +142 -6
  28. package/dist/data/package-version-resolver.test.js +42 -0
  29. package/dist/data/persistent-var-identity.d.ts +37 -0
  30. package/dist/data/persistent-var-identity.js +69 -0
  31. package/dist/data/persistent-vars.d.ts +3 -5
  32. package/dist/data/persistent-vars.js +25 -13
  33. package/dist/data/persistent-vars.package-scope.test.d.ts +1 -0
  34. package/dist/data/persistent-vars.package-scope.test.js +27 -0
  35. package/dist/data/tools.d.ts +3 -3
  36. package/dist/package-installer/index.d.ts +1 -0
  37. package/dist/package-installer/index.js +1 -0
  38. package/dist/package-installer/package-cloner.d.ts +1 -1
  39. package/dist/package-installer/package-cloner.js +11 -16
  40. package/dist/package-installer/package-cloner.test.js +5 -3
  41. package/dist/package-installer/package-creator.d.ts +4 -3
  42. package/dist/package-installer/package-creator.js +13 -5
  43. package/dist/package-installer/package-creator.test.js +19 -0
  44. package/dist/package-installer/package-dev-tools.d.ts +30 -0
  45. package/dist/package-installer/package-dev-tools.js +63 -0
  46. package/dist/package-installer/package-dev-tools.test.d.ts +1 -0
  47. package/dist/package-installer/package-dev-tools.test.js +64 -0
  48. package/dist/package-loader/contract-package-loader.js +20 -8
  49. package/dist/package-loader/contract-package-loader.test.js +16 -0
  50. package/dist/package-loader/index.d.ts +4 -0
  51. package/dist/package-loader/index.js +4 -0
  52. package/dist/package-loader/isolated-artifact.d.ts +233 -0
  53. package/dist/package-loader/isolated-artifact.js +643 -0
  54. package/dist/package-loader/isolated-artifact.test.d.ts +1 -0
  55. package/dist/package-loader/isolated-artifact.test.js +270 -0
  56. package/dist/package-loader/isolated-persistent-var-gateway.d.ts +20 -0
  57. package/dist/package-loader/isolated-persistent-var-gateway.js +106 -0
  58. package/dist/package-loader/isolated-persistent-var-gateway.test.d.ts +1 -0
  59. package/dist/package-loader/isolated-persistent-var-gateway.test.js +56 -0
  60. package/dist/package-loader/isolated-runtime.d.ts +229 -0
  61. package/dist/package-loader/isolated-runtime.js +37 -0
  62. package/dist/package-loader/isolated-table-gateway.d.ts +16 -0
  63. package/dist/package-loader/isolated-table-gateway.js +375 -0
  64. package/dist/package-loader/isolated-table-gateway.test.d.ts +1 -0
  65. package/dist/package-loader/isolated-table-gateway.test.js +206 -0
  66. package/dist/package-loader/isolated-value.d.ts +14 -0
  67. package/dist/package-loader/isolated-value.js +63 -0
  68. package/dist/package-loader/package-loader.d.ts +123 -4
  69. package/dist/package-loader/package-loader.js +417 -46
  70. package/dist/package-loader/package-loader.test.js +478 -12
  71. package/dist/package-loader/resolve-consumed-contracts.d.ts +3 -3
  72. package/dist/package-loader/resolve-consumed-contracts.js +18 -10
  73. package/dist/utils.d.ts +21 -2
  74. package/dist/utils.js +46 -7
  75. package/package.json +1 -1
@@ -0,0 +1,643 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ISOLATED_PERSISTENT_VAR_HOST_MEMBER = exports.ISOLATED_TABLE_HOST_MEMBER = exports.ISOLATED_CONSUME_HOST_MEMBER = exports.ISOLATED_PACKAGE_MAGIC = void 0;
4
+ exports.looksLikeIsolatedPackageArtifact = looksLikeIsolatedPackageArtifact;
5
+ exports.validateIsolatedPackageArtifact = validateIsolatedPackageArtifact;
6
+ exports.parseIsolatedPackageArtifact = parseIsolatedPackageArtifact;
7
+ exports.serializeIsolatedPackageArtifact = serializeIsolatedPackageArtifact;
8
+ exports.isolatedContractToDefinition = isolatedContractToDefinition;
9
+ exports.packageDeclaresConsume = packageDeclaresConsume;
10
+ exports.packageProvidesTool = packageProvidesTool;
11
+ exports.findProvidedContractTable = findProvidedContractTable;
12
+ exports.findProvidedContractObservable = findProvidedContractObservable;
13
+ exports.packageProvidesTable = packageProvidesTable;
14
+ exports.findPackageTable = findPackageTable;
15
+ exports.toConsumedContracts = toConsumedContracts;
16
+ const access_level_1 = require("../data/access-level");
17
+ const persistent_var_identity_1 = require("../data/persistent-var-identity");
18
+ const field_type_1 = require("../types/field-type");
19
+ const utils_1 = require("../utils");
20
+ /**
21
+ * First line of an isolated package bundle. Existing `package.bundle.js`
22
+ * artifacts never start with this token, so the loader can detect the
23
+ * opt-in format without evaluating source.
24
+ */
25
+ exports.ISOLATED_PACKAGE_MAGIC = "PEERS_ISOLATED_PACKAGE_V1";
26
+ /** Host-contract member guests use to request a consumed-contract tool call. */
27
+ exports.ISOLATED_CONSUME_HOST_MEMBER = "contract.consume";
28
+ /** Host-contract member guests use to read or write a package-owned table. */
29
+ exports.ISOLATED_TABLE_HOST_MEMBER = "package.table";
30
+ /** Host-contract member guests use to read or write package-prefixed pvars. */
31
+ exports.ISOLATED_PERSISTENT_VAR_HOST_MEMBER = "package.pvar";
32
+ const FIELD_TYPES = new Set(Object.values(field_type_1.FieldType));
33
+ const OBSERVABLE_FIELD_TYPES = new Set([
34
+ field_type_1.FieldType.id,
35
+ field_type_1.FieldType.string,
36
+ field_type_1.FieldType.number,
37
+ field_type_1.FieldType.boolean,
38
+ field_type_1.FieldType.object,
39
+ field_type_1.FieldType.any,
40
+ ]);
41
+ const PERSISTENT_VAR_SCOPE_SET = new Set(persistent_var_identity_1.PERSISTENT_VAR_SCOPES);
42
+ function assertOnlyKeys(value, allowed, label) {
43
+ const allowedKeys = new Set(allowed);
44
+ const unsupported = Object.keys(value).find((key) => !allowedKeys.has(key));
45
+ if (unsupported) {
46
+ throw new Error(`${label}.${unsupported} is not supported by isolated package artifacts`);
47
+ }
48
+ }
49
+ function assertPeerId(id, label) {
50
+ if (!(0, utils_1.isid)(id)) {
51
+ throw new Error(`${label} must be a valid 25-character peer id, got "${id}"`);
52
+ }
53
+ }
54
+ function assertContractVersion(version, label) {
55
+ if (!Number.isInteger(version) || version < 1) {
56
+ throw new Error(`${label} must be a positive integer, got ${String(version)}`);
57
+ }
58
+ }
59
+ function validateFields(fields, label) {
60
+ if (fields === undefined) {
61
+ return [];
62
+ }
63
+ if (!Array.isArray(fields)) {
64
+ throw new Error(`${label} must be an array`);
65
+ }
66
+ return fields.map((field, index) => {
67
+ if (!field || typeof field !== "object" || Array.isArray(field)) {
68
+ throw new Error(`${label}[${index}] must be an object`);
69
+ }
70
+ const record = field;
71
+ assertOnlyKeys(record, ["name", "description", "type", "defaultValue", "optional", "isArray", "subType"], `${label}[${index}]`);
72
+ if (typeof record.name !== "string" || record.name.trim() === "") {
73
+ throw new Error(`${label}[${index}].name is required`);
74
+ }
75
+ if (typeof record.type !== "string" || !FIELD_TYPES.has(record.type)) {
76
+ throw new Error(`${label}[${index}].type must be a FieldType`);
77
+ }
78
+ return {
79
+ name: record.name,
80
+ type: record.type,
81
+ ...(typeof record.description === "string" ? { description: record.description } : {}),
82
+ ...(record.optional === true ? { optional: true } : {}),
83
+ ...(record.isArray === true ? { isArray: true } : {}),
84
+ ...(typeof record.subType === "string" ? { subType: record.subType } : {}),
85
+ ...(record.defaultValue !== undefined ? { defaultValue: record.defaultValue } : {}),
86
+ };
87
+ });
88
+ }
89
+ function assertJsonCompatible(value, label, seen = new WeakSet()) {
90
+ if (value === null ||
91
+ typeof value === "string" ||
92
+ typeof value === "boolean" ||
93
+ (typeof value === "number" && Number.isFinite(value))) {
94
+ return;
95
+ }
96
+ if (typeof value !== "object") {
97
+ throw new Error(`${label} must be JSON-compatible`);
98
+ }
99
+ if (seen.has(value)) {
100
+ throw new Error(`${label} must be JSON-compatible`);
101
+ }
102
+ seen.add(value);
103
+ if (Array.isArray(value)) {
104
+ for (const item of value) {
105
+ assertJsonCompatible(item, label, seen);
106
+ }
107
+ }
108
+ else {
109
+ const prototype = Object.getPrototypeOf(value);
110
+ if (prototype !== Object.prototype && prototype !== null) {
111
+ throw new Error(`${label} must be JSON-compatible`);
112
+ }
113
+ for (const item of Object.values(value)) {
114
+ assertJsonCompatible(item, label, seen);
115
+ }
116
+ }
117
+ seen.delete(value);
118
+ }
119
+ function validateObservableDefault(value, type, label) {
120
+ if (value === undefined) {
121
+ throw new Error(`${label} is required`);
122
+ }
123
+ try {
124
+ assertJsonCompatible(value, label);
125
+ }
126
+ catch {
127
+ throw new Error(`${label} must be JSON-compatible`);
128
+ }
129
+ const valid = type === field_type_1.FieldType.any ||
130
+ (type === field_type_1.FieldType.id && typeof value === "string" && (0, utils_1.isid)(value)) ||
131
+ (type === field_type_1.FieldType.string && typeof value === "string") ||
132
+ (type === field_type_1.FieldType.number && typeof value === "number") ||
133
+ (type === field_type_1.FieldType.boolean && typeof value === "boolean") ||
134
+ (type === field_type_1.FieldType.object && value !== null && typeof value === "object");
135
+ if (!valid) {
136
+ throw new Error(`${label} must match observable valueType ${type}`);
137
+ }
138
+ }
139
+ function validatePackageTables(manifest) {
140
+ if (manifest.tables === undefined) {
141
+ manifest.tables = [];
142
+ return;
143
+ }
144
+ if (!Array.isArray(manifest.tables)) {
145
+ throw new Error("manifest.tables must be an array");
146
+ }
147
+ const tableNames = new Set();
148
+ for (const [tableIndex, table] of manifest.tables.entries()) {
149
+ const tableLabel = `manifest.tables[${tableIndex}]`;
150
+ if (!table || typeof table !== "object" || Array.isArray(table)) {
151
+ throw new Error(`${tableLabel} must be an object`);
152
+ }
153
+ const forbiddenIdentity = [
154
+ "tableId",
155
+ "visibility",
156
+ "contractId",
157
+ "contractVersion",
158
+ "packageId",
159
+ "physicalName",
160
+ ].find((key) => key in table);
161
+ if (forbiddenIdentity) {
162
+ throw new Error(`${tableLabel} must not declare ${forbiddenIdentity}`);
163
+ }
164
+ assertOnlyKeys(table, ["name", "description", "primaryKeyName", "fields", "versionNumber"], tableLabel);
165
+ if (typeof table.name !== "string" || table.name.trim() === "") {
166
+ throw new Error(`${tableLabel}.name is required`);
167
+ }
168
+ if (table.description !== undefined && typeof table.description !== "string") {
169
+ throw new Error(`${tableLabel}.description must be a string`);
170
+ }
171
+ if (tableNames.has(table.name)) {
172
+ throw new Error(`manifest declares duplicate table "${table.name}"`);
173
+ }
174
+ tableNames.add(table.name);
175
+ if (typeof table.primaryKeyName !== "string" || table.primaryKeyName.trim() === "") {
176
+ throw new Error(`${tableLabel}.primaryKeyName is required`);
177
+ }
178
+ table.fields = validateFields(table.fields, `${tableLabel}.fields`);
179
+ if (table.fields.length === 0) {
180
+ throw new Error(`${tableLabel}.fields must not be empty`);
181
+ }
182
+ const primaryKey = table.fields.find((field) => field.name === table.primaryKeyName);
183
+ if (!primaryKey) {
184
+ throw new Error(`${tableLabel}.primaryKeyName "${table.primaryKeyName}" is not present in fields`);
185
+ }
186
+ if (primaryKey.type !== field_type_1.FieldType.id) {
187
+ throw new Error(`${tableLabel}.primaryKeyName must refer to a field of type id`);
188
+ }
189
+ if (table.versionNumber !== undefined) {
190
+ if (!Number.isInteger(table.versionNumber) || table.versionNumber < 1) {
191
+ throw new Error(`${tableLabel}.versionNumber must be a positive integer`);
192
+ }
193
+ }
194
+ else {
195
+ table.versionNumber = 1;
196
+ }
197
+ }
198
+ }
199
+ function validateProvidedContract(contract, index, packageTables) {
200
+ const label = `provides[${index}]`;
201
+ if (!contract || typeof contract !== "object" || Array.isArray(contract)) {
202
+ throw new Error(`${label} must be an object`);
203
+ }
204
+ assertOnlyKeys(contract, [
205
+ "contractId",
206
+ "version",
207
+ "name",
208
+ "description",
209
+ "tools",
210
+ "tables",
211
+ "observables",
212
+ "alsoImplements",
213
+ ], label);
214
+ assertPeerId(contract.contractId, `${label}.contractId`);
215
+ assertContractVersion(contract.version, `${label}.version`);
216
+ if (typeof contract.name !== "string" || contract.name.trim() === "") {
217
+ throw new Error(`${label}.name is required`);
218
+ }
219
+ if (contract.description !== undefined && typeof contract.description !== "string") {
220
+ throw new Error(`${label}.description must be a string`);
221
+ }
222
+ if (!Array.isArray(contract.tools)) {
223
+ throw new Error(`${label}.tools must be an array`);
224
+ }
225
+ const names = new Set();
226
+ for (const [toolIndex, tool] of contract.tools.entries()) {
227
+ const toolLabel = `${label}.tools[${toolIndex}]`;
228
+ if (!tool || typeof tool !== "object" || Array.isArray(tool)) {
229
+ throw new Error(`${toolLabel} must be an object`);
230
+ }
231
+ assertOnlyKeys(tool, ["name", "usageDescription", "inputFields", "outputFields", "suggestedAccessLevel"], toolLabel);
232
+ if (typeof tool.name !== "string" || tool.name.trim() === "") {
233
+ throw new Error(`${toolLabel}.name is required`);
234
+ }
235
+ if (names.has(tool.name)) {
236
+ throw new Error(`${label} declares duplicate tool "${tool.name}"`);
237
+ }
238
+ names.add(tool.name);
239
+ if (typeof tool.usageDescription !== "string") {
240
+ throw new Error(`${toolLabel}.usageDescription is required`);
241
+ }
242
+ tool.inputFields = validateFields(tool.inputFields, `${toolLabel}.inputFields`);
243
+ tool.outputFields = validateFields(tool.outputFields, `${toolLabel}.outputFields`);
244
+ if (tool.suggestedAccessLevel !== undefined) {
245
+ if (!Number.isInteger(tool.suggestedAccessLevel) ||
246
+ tool.suggestedAccessLevel < access_level_1.MIN_ACCESS_LEVEL ||
247
+ tool.suggestedAccessLevel > access_level_1.MAX_ACCESS_LEVEL) {
248
+ throw new Error(`${toolLabel}.suggestedAccessLevel must be an integer 0–100`);
249
+ }
250
+ }
251
+ }
252
+ if (contract.tables === undefined) {
253
+ contract.tables = [];
254
+ }
255
+ if (!Array.isArray(contract.tables)) {
256
+ throw new Error(`${label}.tables must be an array`);
257
+ }
258
+ const tableNames = new Set();
259
+ for (const [tableIndex, tableRef] of contract.tables.entries()) {
260
+ const tableLabel = `${label}.tables[${tableIndex}]`;
261
+ if (!tableRef || typeof tableRef !== "object" || Array.isArray(tableRef)) {
262
+ throw new Error(`${tableLabel} must be an object`);
263
+ }
264
+ const keys = Object.keys(tableRef);
265
+ if (keys.length !== 1 || keys[0] !== "name") {
266
+ throw new Error(`${tableLabel} must contain only a package table name`);
267
+ }
268
+ if (typeof tableRef.name !== "string" || tableRef.name.trim() === "") {
269
+ throw new Error(`${tableLabel}.name is required`);
270
+ }
271
+ if (tableNames.has(tableRef.name)) {
272
+ throw new Error(`${label} declares duplicate table "${tableRef.name}"`);
273
+ }
274
+ if (!findPackageTable(packageTables, tableRef.name)) {
275
+ throw new Error(`${tableLabel} references undeclared package table "${tableRef.name}"`);
276
+ }
277
+ tableNames.add(tableRef.name);
278
+ }
279
+ if (contract.observables === undefined) {
280
+ contract.observables = [];
281
+ }
282
+ if (!Array.isArray(contract.observables)) {
283
+ throw new Error(`${label}.observables must be an array`);
284
+ }
285
+ const observableNames = new Set();
286
+ const pvarBindings = new Set();
287
+ for (const [observableIndex, observable] of contract.observables.entries()) {
288
+ const observableLabel = `${label}.observables[${observableIndex}]`;
289
+ if (!observable || typeof observable !== "object" || Array.isArray(observable)) {
290
+ throw new Error(`${observableLabel} must be an object`);
291
+ }
292
+ assertOnlyKeys(observable, ["name", "description", "valueType", "writable", "persistentVar"], observableLabel);
293
+ if (typeof observable.name !== "string" || observable.name.trim() === "") {
294
+ throw new Error(`${observableLabel}.name is required`);
295
+ }
296
+ if (observableNames.has(observable.name)) {
297
+ throw new Error(`${label} declares duplicate observable "${observable.name}"`);
298
+ }
299
+ observableNames.add(observable.name);
300
+ if (typeof observable.description !== "string") {
301
+ throw new Error(`${observableLabel}.description is required`);
302
+ }
303
+ if (typeof observable.valueType !== "string" ||
304
+ !OBSERVABLE_FIELD_TYPES.has(observable.valueType)) {
305
+ throw new Error(`${observableLabel}.valueType must be a JSON-compatible FieldType`);
306
+ }
307
+ if (typeof observable.writable !== "boolean") {
308
+ throw new Error(`${observableLabel}.writable must be a boolean`);
309
+ }
310
+ const binding = observable.persistentVar;
311
+ if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
312
+ throw new Error(`${observableLabel}.persistentVar is required`);
313
+ }
314
+ assertOnlyKeys(binding, ["scope", "name", "defaultValue"], `${observableLabel}.persistentVar`);
315
+ if (typeof binding.scope !== "string" || !PERSISTENT_VAR_SCOPE_SET.has(binding.scope)) {
316
+ throw new Error(`${observableLabel}.persistentVar.scope is invalid`);
317
+ }
318
+ if (typeof binding.name !== "string" || binding.name.trim() === "") {
319
+ throw new Error(`${observableLabel}.persistentVar.name is required`);
320
+ }
321
+ const bindingKey = `${binding.scope}:${binding.name}`;
322
+ if (pvarBindings.has(bindingKey)) {
323
+ throw new Error(`${label} exposes persistent variable "${bindingKey}" more than once`);
324
+ }
325
+ pvarBindings.add(bindingKey);
326
+ validateObservableDefault(binding.defaultValue, observable.valueType, `${observableLabel}.persistentVar.defaultValue`);
327
+ }
328
+ if (contract.tools.length + contract.tables.length + contract.observables.length === 0) {
329
+ throw new Error(`${label} must declare at least one contract member`);
330
+ }
331
+ if (contract.alsoImplements === undefined) {
332
+ contract.alsoImplements = [];
333
+ }
334
+ if (!Array.isArray(contract.alsoImplements)) {
335
+ throw new Error(`${label}.alsoImplements must be an array`);
336
+ }
337
+ for (const [declarationIndex, declaration] of contract.alsoImplements.entries()) {
338
+ const declarationLabel = `${label}.alsoImplements[${declarationIndex}]`;
339
+ if (!declaration || typeof declaration !== "object" || Array.isArray(declaration)) {
340
+ throw new Error(`${declarationLabel} must be an object`);
341
+ }
342
+ assertOnlyKeys(declaration, ["contractId", "version"], declarationLabel);
343
+ assertPeerId(declaration.contractId, `${declarationLabel}.contractId`);
344
+ if (typeof declaration.version === "number") {
345
+ assertContractVersion(declaration.version, `${declarationLabel}.version`);
346
+ continue;
347
+ }
348
+ if (!declaration.version ||
349
+ typeof declaration.version !== "object" ||
350
+ Array.isArray(declaration.version)) {
351
+ throw new Error(`${declarationLabel}.version must be an integer or inclusive range`);
352
+ }
353
+ assertOnlyKeys(declaration.version, ["from", "to"], `${declarationLabel}.version`);
354
+ assertContractVersion(declaration.version.from, `${declarationLabel}.version.from`);
355
+ assertContractVersion(declaration.version.to, `${declarationLabel}.version.to`);
356
+ if (declaration.version.from > declaration.version.to) {
357
+ throw new Error(`${declarationLabel}.version.from must not exceed .to`);
358
+ }
359
+ }
360
+ }
361
+ function validateConsumedContract(dep, index) {
362
+ const label = `consumes[${index}]`;
363
+ if (!dep || typeof dep !== "object" || Array.isArray(dep)) {
364
+ throw new Error(`${label} must be an object`);
365
+ }
366
+ assertOnlyKeys(dep, ["contractId", "version", "optional", "providerPackageIds", "fallbackToDefaultProvider"], label);
367
+ assertPeerId(dep.contractId, `${label}.contractId`);
368
+ assertContractVersion(dep.version, `${label}.version`);
369
+ if (dep.optional !== undefined && typeof dep.optional !== "boolean") {
370
+ throw new Error(`${label}.optional must be a boolean`);
371
+ }
372
+ if (dep.fallbackToDefaultProvider !== undefined &&
373
+ typeof dep.fallbackToDefaultProvider !== "boolean") {
374
+ throw new Error(`${label}.fallbackToDefaultProvider must be a boolean`);
375
+ }
376
+ if (dep.providerPackageIds !== undefined) {
377
+ if (!Array.isArray(dep.providerPackageIds)) {
378
+ throw new Error(`${label}.providerPackageIds must be an array`);
379
+ }
380
+ for (const [providerIndex, providerId] of dep.providerPackageIds.entries()) {
381
+ assertPeerId(providerId, `${label}.providerPackageIds[${providerIndex}]`);
382
+ }
383
+ }
384
+ }
385
+ function validateAppNavs(appNavs) {
386
+ if (appNavs === undefined) {
387
+ return undefined;
388
+ }
389
+ if (!Array.isArray(appNavs)) {
390
+ throw new Error("manifest.appNavs must be an array");
391
+ }
392
+ return appNavs.map((nav, index) => {
393
+ if (!nav || typeof nav !== "object" || Array.isArray(nav)) {
394
+ throw new Error(`manifest.appNavs[${index}] must be an object`);
395
+ }
396
+ const record = nav;
397
+ assertOnlyKeys(record, ["name", "displayName", "iconClassName", "navigationPath"], `manifest.appNavs[${index}]`);
398
+ if (typeof record.name !== "string" || record.name.trim() === "") {
399
+ throw new Error(`manifest.appNavs[${index}].name is required`);
400
+ }
401
+ if (typeof record.iconClassName !== "string") {
402
+ throw new Error(`manifest.appNavs[${index}].iconClassName is required`);
403
+ }
404
+ if (typeof record.navigationPath !== "string") {
405
+ throw new Error(`manifest.appNavs[${index}].navigationPath is required`);
406
+ }
407
+ return {
408
+ name: record.name,
409
+ iconClassName: record.iconClassName,
410
+ navigationPath: record.navigationPath,
411
+ ...(typeof record.displayName === "string" ? { displayName: record.displayName } : {}),
412
+ };
413
+ });
414
+ }
415
+ /**
416
+ * True when `bundle` starts with {@link ISOLATED_PACKAGE_MAGIC}.
417
+ *
418
+ * A recognized header must never fall through to `new Function` evaluation,
419
+ * even if the rest of the envelope is malformed.
420
+ *
421
+ * @param bundle - Package bundle text from Files or `dist/package.bundle.js`.
422
+ */
423
+ function looksLikeIsolatedPackageArtifact(bundle) {
424
+ if (typeof bundle !== "string") {
425
+ return false;
426
+ }
427
+ const trimmed = bundle.trimStart();
428
+ return (trimmed === exports.ISOLATED_PACKAGE_MAGIC ||
429
+ trimmed.startsWith(`${exports.ISOLATED_PACKAGE_MAGIC}\n`) ||
430
+ trimmed.startsWith(`${exports.ISOLATED_PACKAGE_MAGIC}\r\n`));
431
+ }
432
+ /**
433
+ * Validate a wire-safe isolated package artifact. Does not evaluate
434
+ * `providerSource`.
435
+ *
436
+ * @param artifact - Parsed artifact object.
437
+ * @returns The same manifest after in-place field normalization.
438
+ * @throws If identity, contracts, tools, or source are invalid.
439
+ */
440
+ function validateIsolatedPackageArtifact(artifact) {
441
+ if (!artifact || typeof artifact !== "object" || Array.isArray(artifact)) {
442
+ throw new Error("Package artifact must be an object");
443
+ }
444
+ assertOnlyKeys(artifact, ["manifest", "providerSource"], "artifact");
445
+ const { manifest, providerSource } = artifact;
446
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
447
+ throw new Error("Package artifact.manifest is required");
448
+ }
449
+ assertOnlyKeys(manifest, ["packageId", "version", "provides", "consumes", "tables", "appNavs"], "manifest");
450
+ if (typeof providerSource !== "string" || providerSource.trim() === "") {
451
+ throw new Error("Package artifact.providerSource must be a non-empty string");
452
+ }
453
+ assertPeerId(manifest.packageId, "packageId");
454
+ if (typeof manifest.version !== "string" || manifest.version.trim() === "") {
455
+ throw new Error("package version is required");
456
+ }
457
+ if (!Array.isArray(manifest.provides) || manifest.provides.length === 0) {
458
+ throw new Error("package must provide at least one contract");
459
+ }
460
+ if (!Array.isArray(manifest.consumes)) {
461
+ throw new Error("package.consumes must be an array");
462
+ }
463
+ validatePackageTables(manifest);
464
+ const packageTables = manifest.tables ?? [];
465
+ const providedKeys = new Set();
466
+ for (const [index, contract] of manifest.provides.entries()) {
467
+ validateProvidedContract(contract, index, packageTables);
468
+ const key = `${contract.contractId}@${contract.version}`;
469
+ if (providedKeys.has(key)) {
470
+ throw new Error(`Duplicate provided contract ${key}`);
471
+ }
472
+ providedKeys.add(key);
473
+ }
474
+ for (const [index, dep] of manifest.consumes.entries()) {
475
+ validateConsumedContract(dep, index);
476
+ }
477
+ manifest.appNavs = validateAppNavs(manifest.appNavs);
478
+ return manifest;
479
+ }
480
+ /**
481
+ * Parse a magic-header + JSON isolated package bundle.
482
+ *
483
+ * @param bundle - Full package-bundle text.
484
+ * @returns The validated artifact.
485
+ * @throws If the magic header is present but the envelope is malformed.
486
+ */
487
+ function parseIsolatedPackageArtifact(bundle) {
488
+ if (!looksLikeIsolatedPackageArtifact(bundle)) {
489
+ throw new Error("Bundle is not an isolated package artifact");
490
+ }
491
+ const trimmed = bundle.trimStart();
492
+ const newline = trimmed.indexOf("\n");
493
+ if (newline < 0) {
494
+ throw new Error("Isolated package artifact is missing its JSON envelope");
495
+ }
496
+ const jsonText = trimmed.slice(newline + 1).trim();
497
+ let parsed;
498
+ try {
499
+ parsed = JSON.parse(jsonText);
500
+ }
501
+ catch (err) {
502
+ throw new Error(`Isolated package artifact JSON is invalid: ${err instanceof Error ? err.message : String(err)}`);
503
+ }
504
+ const artifact = parsed;
505
+ validateIsolatedPackageArtifact(artifact);
506
+ return artifact;
507
+ }
508
+ /**
509
+ * Serialize an isolated artifact into the package-bundle slot format.
510
+ *
511
+ * @param artifact - Validated or soon-to-be-validated artifact.
512
+ */
513
+ function serializeIsolatedPackageArtifact(artifact) {
514
+ validateIsolatedPackageArtifact(artifact);
515
+ return `${exports.ISOLATED_PACKAGE_MAGIC}\n${JSON.stringify(artifact)}\n`;
516
+ }
517
+ /**
518
+ * Convert a provided-contract declaration into an SDK contract definition.
519
+ *
520
+ * Name-only table references are resolved from the package's schema list.
521
+ * Persistent-variable bindings stay host-only and are omitted from the public
522
+ * observable definition.
523
+ *
524
+ * @param contract - Isolated provided-contract declaration.
525
+ * @param packageTables - Package-owned schemas available for contract exposure.
526
+ */
527
+ function isolatedContractToDefinition(contract, packageTables) {
528
+ const tools = contract.tools.map((tool) => ({
529
+ name: tool.name,
530
+ usageDescription: tool.usageDescription,
531
+ inputFields: tool.inputFields ?? [],
532
+ outputFields: tool.outputFields ?? [],
533
+ }));
534
+ const tables = (contract.tables ?? []).map((tableRef) => {
535
+ const table = findPackageTable(packageTables, tableRef.name);
536
+ if (!table) {
537
+ throw new Error(`Contract references undeclared package table "${tableRef.name}"`);
538
+ }
539
+ return {
540
+ name: table.name,
541
+ description: table.description ?? "",
542
+ primaryKeyName: table.primaryKeyName,
543
+ fields: table.fields,
544
+ };
545
+ });
546
+ const observables = (contract.observables ?? []).map((observable) => ({
547
+ name: observable.name,
548
+ description: observable.description,
549
+ valueType: observable.valueType,
550
+ writable: observable.writable,
551
+ }));
552
+ return {
553
+ contractId: contract.contractId,
554
+ version: contract.version,
555
+ name: contract.name,
556
+ description: contract.description ?? "",
557
+ tables,
558
+ tools,
559
+ observables,
560
+ events: [],
561
+ };
562
+ }
563
+ /**
564
+ * True when `consumes` names this exact contract version.
565
+ *
566
+ * @param consumes - Declared package dependencies.
567
+ * @param contractId - Target contract id.
568
+ * @param version - Target contract version.
569
+ */
570
+ function packageDeclaresConsume(consumes, contractId, version) {
571
+ return consumes.some((dep) => dep.contractId === contractId && dep.version === version);
572
+ }
573
+ /**
574
+ * True when the package advertises this tool on a provided contract.
575
+ *
576
+ * @param provides - Provided contracts from the manifest.
577
+ * @param contractId - Target contract id.
578
+ * @param version - Target contract version.
579
+ * @param toolName - Tool name on that contract.
580
+ */
581
+ function packageProvidesTool(provides, contractId, version, toolName) {
582
+ const contract = provides.find((item) => item.contractId === contractId && item.version === version);
583
+ return Boolean(contract?.tools.some((tool) => tool.name === toolName));
584
+ }
585
+ /**
586
+ * Return a consumer-facing table reference from a provided contract.
587
+ *
588
+ * @param provides Provided contracts from the manifest.
589
+ * @param contractId Target contract ID.
590
+ * @param version Target contract version.
591
+ * @param tableName Public table name.
592
+ */
593
+ function findProvidedContractTable(provides, contractId, version, tableName) {
594
+ return provides
595
+ .find((contract) => contract.contractId === contractId && contract.version === version)
596
+ ?.tables?.find((table) => table.name === tableName);
597
+ }
598
+ /**
599
+ * Return a pvar-backed observable from a provided contract.
600
+ *
601
+ * @param provides Provided contracts from the manifest.
602
+ * @param contractId Target contract ID.
603
+ * @param version Target contract version.
604
+ * @param observableName Public observable name.
605
+ */
606
+ function findProvidedContractObservable(provides, contractId, version, observableName) {
607
+ return provides
608
+ .find((contract) => contract.contractId === contractId && contract.version === version)
609
+ ?.observables?.find((observable) => observable.name === observableName);
610
+ }
611
+ /**
612
+ * True when the package declares a private table.
613
+ *
614
+ * @param tables - Package-private table schemas.
615
+ * @param tableName - Package-local table name.
616
+ */
617
+ function packageProvidesTable(tables, tableName) {
618
+ return tables.some((table) => table.name === tableName);
619
+ }
620
+ /**
621
+ * Return the declared table when this package owns it.
622
+ *
623
+ * @param tables - Package-private table schemas.
624
+ * @param tableName - Package-local table name.
625
+ */
626
+ function findPackageTable(tables, tableName) {
627
+ return tables.find((table) => table.name === tableName);
628
+ }
629
+ /**
630
+ * Map isolated consume declarations onto the registry's
631
+ * {@link IConsumedContract} shape (no deferred handles).
632
+ *
633
+ * @param consumes - Isolated consume list.
634
+ */
635
+ function toConsumedContracts(consumes) {
636
+ return consumes.map((dep) => ({
637
+ contractId: dep.contractId,
638
+ version: dep.version,
639
+ optional: dep.optional,
640
+ providerPackageIds: dep.providerPackageIds,
641
+ fallbackToDefaultProvider: dep.fallbackToDefaultProvider,
642
+ }));
643
+ }
@@ -0,0 +1 @@
1
+ export {};