@peers-app/peers-sdk 0.28.5 → 0.28.7
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 +2 -2
- package/dist/contracts/builder.d.ts +0 -6
- package/dist/contracts/builder.js +0 -11
- package/dist/contracts/contract-proxy.d.ts +7 -6
- package/dist/contracts/extract.d.ts +1 -1
- package/dist/data/package-version-resolver.js +1 -19
- package/dist/data/packages.utils.d.ts +4 -0
- package/dist/data/packages.utils.js +5 -1
- package/dist/data/tools.d.ts +0 -10
- package/dist/define/context.d.ts +5 -7
- package/dist/define/observables.js +1 -5
- package/dist/package-loader/isolated-artifact.d.ts +16 -29
- package/dist/package-loader/isolated-artifact.js +20 -33
- package/dist/package-loader/package-loader.d.ts +5 -5
- package/dist/package-loader/package-loader.js +14 -43
- package/dist/package-loader/package-runtime-state.d.ts +1 -2
- package/dist/types/peers-package.d.ts +1 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ NaCl-based key management, message signing and verification, box encryption, and
|
|
|
32
32
|
|
|
33
33
|
### Tools Framework
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Declare AI-callable tools with `definePackage()` and provide their implementations through contract resolvers. Use `IContractToolImplementation` for provider implementations.
|
|
36
36
|
|
|
37
37
|
### Identity and Groups
|
|
38
38
|
|
|
@@ -58,7 +58,7 @@ The fastest way to build a Peers package is to start from the template:
|
|
|
58
58
|
| `DataQuery` / `DataFilter` | Mongo-style query filters translated to SQL |
|
|
59
59
|
| `observable()` / `computed()` | Reactive state primitives |
|
|
60
60
|
| `deviceVar` / `userVar` / `groupVar` | Persistent variables — observables backed by the database |
|
|
61
|
-
| `ITool` / `
|
|
61
|
+
| `ITool` / `IContractToolImplementation` | AI-callable tool definitions |
|
|
62
62
|
| `IPeersUI` / `IPeersUIRoute` | Screen and route declarations for package UIs |
|
|
63
63
|
| `newid()` | Generate 25-character time-sortable peer IDs |
|
|
64
64
|
| `newKeys()` / `hydrateKeys()` | Cryptographic key pair generation and hydration |
|
|
@@ -18,7 +18,6 @@ export declare class ContractBuilder {
|
|
|
18
18
|
private _events;
|
|
19
19
|
private _alsoImplements;
|
|
20
20
|
private _exposes;
|
|
21
|
-
private warnedAboutToolInstances;
|
|
22
21
|
/** Full table definitions for this contract (used by the install flow). */
|
|
23
22
|
private _tableDefinitions;
|
|
24
23
|
constructor(contractId: string, version: number, name: string, description?: string | undefined);
|
|
@@ -35,11 +34,6 @@ export declare class ContractBuilder {
|
|
|
35
34
|
*/
|
|
36
35
|
set events(events: IContractEvent[]);
|
|
37
36
|
get events(): IContractEvent[];
|
|
38
|
-
/**
|
|
39
|
-
* Discard executable assignments from bundles compiled against an older SDK.
|
|
40
|
-
* @deprecated Implement tools with registerContractResolver instead.
|
|
41
|
-
*/
|
|
42
|
-
set toolInstances(_instances: never);
|
|
43
37
|
/** Full table definitions for runtime table registration. */
|
|
44
38
|
set tableDefinitions(defs: ITableDefinition[]);
|
|
45
39
|
get tableDefinitions(): ITableDefinition[];
|
|
@@ -26,7 +26,6 @@ class ContractBuilder {
|
|
|
26
26
|
_events = [];
|
|
27
27
|
_alsoImplements = [];
|
|
28
28
|
_exposes = [];
|
|
29
|
-
warnedAboutToolInstances = false;
|
|
30
29
|
/** Full table definitions for this contract (used by the install flow). */
|
|
31
30
|
_tableDefinitions = [];
|
|
32
31
|
constructor(contractId, version, name, description) {
|
|
@@ -64,16 +63,6 @@ class ContractBuilder {
|
|
|
64
63
|
get events() {
|
|
65
64
|
return this._events;
|
|
66
65
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Discard executable assignments from bundles compiled against an older SDK.
|
|
69
|
-
* @deprecated Implement tools with registerContractResolver instead.
|
|
70
|
-
*/
|
|
71
|
-
set toolInstances(_instances) {
|
|
72
|
-
if (this.warnedAboutToolInstances)
|
|
73
|
-
return;
|
|
74
|
-
this.warnedAboutToolInstances = true;
|
|
75
|
-
console.warn(`Contract ${this.name} (${this.contractId}@${this.version}) assigned toolInstances, which no longer run; implement these tools with registerContractResolver (docs/Packages/contracts.md#migrating-from-toolinstances)`);
|
|
76
|
-
}
|
|
77
66
|
/** Full table definitions for runtime table registration. */
|
|
78
67
|
set tableDefinitions(defs) {
|
|
79
68
|
this._tableDefinitions = defs;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
1
2
|
import type { DataContext } from "../context/data-context";
|
|
2
3
|
import type { Table } from "../data/orm/table";
|
|
3
|
-
import type { ITool
|
|
4
|
+
import type { ITool } from "../data/tools";
|
|
4
5
|
import type { Event, ISubscriptionResult } from "../events";
|
|
5
6
|
import { type Observable } from "../observable";
|
|
6
7
|
import { type IIsolatedCallPrincipal } from "../package-loader/isolated-runtime";
|
|
@@ -359,15 +360,15 @@ export declare function createRemoteContractConsumer(contract: IContractDefiniti
|
|
|
359
360
|
dataContextId?: string;
|
|
360
361
|
}): IRemoteContractConsumer<IContractConsumer>;
|
|
361
362
|
/**
|
|
362
|
-
* Provider-local executable metadata
|
|
363
|
-
*
|
|
363
|
+
* Provider-local executable metadata for contract tools.
|
|
364
|
+
* Implementations need no catalog ID, persisted row, or executable registration.
|
|
364
365
|
*/
|
|
365
366
|
export interface IContractToolImplementation {
|
|
366
367
|
/** Provider metadata; the contract member name supplies identity, without a Tools row. */
|
|
367
368
|
tool: Pick<ITool, "name" | "usageDescription"> & Partial<Pick<ITool, "toolId" | "detailedDescription" | "inputSchema" | "outputSchema" | "accessLevel" | "authorityPropagation" | "remoteAccessLevel">>;
|
|
368
|
-
toolFn:
|
|
369
|
-
inputSchema?:
|
|
370
|
-
outputSchema?:
|
|
369
|
+
toolFn: (...args: any[]) => any;
|
|
370
|
+
inputSchema?: z.ZodObject<any>;
|
|
371
|
+
outputSchema?: z.ZodObject<any>;
|
|
371
372
|
}
|
|
372
373
|
/** Context-bound live members of a provider; executable ownership stays with the contract. */
|
|
373
374
|
export interface IContractResolution {
|
|
@@ -18,7 +18,7 @@ export interface IExtractableTableDef {
|
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Minimal tool-instance shape accepted by extractToolShape.
|
|
21
|
-
* Mirrors the subset of
|
|
21
|
+
* Mirrors the subset of IContractToolImplementation we need.
|
|
22
22
|
*/
|
|
23
23
|
export interface IExtractableToolInstance {
|
|
24
24
|
/** Host-only workflow policy carried into the contract definition. */
|
|
@@ -18,14 +18,12 @@ exports.hasDeviceFollowOverride = hasDeviceFollowOverride;
|
|
|
18
18
|
exports.readStoredPackageLocalPath = readStoredPackageLocalPath;
|
|
19
19
|
const user_context_singleton_1 = require("../context/user-context-singleton");
|
|
20
20
|
const keys_1 = require("../keys");
|
|
21
|
-
const assistants_1 = require("./assistants");
|
|
22
21
|
const file_types_1 = require("./files/file.types");
|
|
23
22
|
const group_member_roles_1 = require("./group-member-roles");
|
|
24
23
|
const group_permissions_1 = require("./group-permissions");
|
|
25
24
|
const package_versions_1 = require("./package-versions");
|
|
26
25
|
const packages_1 = require("./packages");
|
|
27
26
|
const persistent_vars_1 = require("./persistent-vars");
|
|
28
|
-
const workflows_1 = require("./workflows");
|
|
29
27
|
/**
|
|
30
28
|
* Reactive, cached observable holding device-local preferences for a single
|
|
31
29
|
* package. Backed by a `groupDeviceVar` so the UI can subscribe via
|
|
@@ -344,9 +342,7 @@ async function resolveDevicePackageVersion(pkg, dataContext, opts) {
|
|
|
344
342
|
}
|
|
345
343
|
return { loaded: false, upgraded: false };
|
|
346
344
|
}
|
|
347
|
-
// Load succeeded
|
|
348
|
-
// either as a group-level active version or as device-local state.
|
|
349
|
-
await installPackageContents(dataContext, pkg, instance);
|
|
345
|
+
// Load succeeded; persist activation as a group default or device-local state.
|
|
350
346
|
// When the device is following group settings (no device-level follow
|
|
351
347
|
// override) and the upgraded version is non-dev, also advance the group's
|
|
352
348
|
// activePackageVersionId so other devices stay in sync. Only admin+ users
|
|
@@ -436,20 +432,6 @@ async function loadOlderCompatiblePackageVersion(pkg, dataContext, unavailableVe
|
|
|
436
432
|
}
|
|
437
433
|
return undefined;
|
|
438
434
|
}
|
|
439
|
-
/**
|
|
440
|
-
* Save a loaded package's assistants and workflows into the data context.
|
|
441
|
-
* Contract-installed packages are skipped because their content is already
|
|
442
|
-
* managed by the contract loader.
|
|
443
|
-
*/
|
|
444
|
-
async function installPackageContents(dataContext, pkg, instance) {
|
|
445
|
-
if (dataContext.packageLoader.isContractInstalled(pkg.packageId))
|
|
446
|
-
return;
|
|
447
|
-
const saves = [
|
|
448
|
-
...(instance.assistants?.map((a) => (0, assistants_1.Assistants)(dataContext).save(a)) ?? []),
|
|
449
|
-
...(instance.workflows?.map((w) => (0, workflows_1.Workflows)(dataContext).save(w)) ?? []),
|
|
450
|
-
];
|
|
451
|
-
await Promise.all(saves);
|
|
452
|
-
}
|
|
453
435
|
/**
|
|
454
436
|
* Returns true when the device has an explicit follow-policy override
|
|
455
437
|
* (`followRange`, `followTags`, or `pinned`). Used by the UI to drive the
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import type { DataContext } from "../context/data-context";
|
|
2
2
|
import { type IPackage } from "./packages";
|
|
3
|
+
/**
|
|
4
|
+
* Copy a package and its active version's bundles into another context.
|
|
5
|
+
* Restores an uninstalled package; uninstall keeps version and file rows.
|
|
6
|
+
*/
|
|
3
7
|
export declare function copyPackageToAnotherDataContext(packageId: string, fromDataContext: DataContext, toDataContext: DataContext): Promise<IPackage>;
|
|
@@ -15,6 +15,10 @@ async function copyBundleFile(fileId, fromFiles, toFiles, label) {
|
|
|
15
15
|
console.warn(`${label} file ${fileId} not found in source context`);
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Copy a package and its active version's bundles into another context.
|
|
20
|
+
* Restores an uninstalled package; uninstall keeps version and file rows.
|
|
21
|
+
*/
|
|
18
22
|
async function copyPackageToAnotherDataContext(packageId, fromDataContext, toDataContext) {
|
|
19
23
|
const fromPackages = (0, packages_1.Packages)(fromDataContext);
|
|
20
24
|
const toPackages = (0, packages_1.Packages)(toDataContext);
|
|
@@ -35,5 +39,5 @@ async function copyPackageToAnotherDataContext(packageId, fromDataContext, toDat
|
|
|
35
39
|
await toPvTable.signAndSave(pv, { saveAsSnapshot: true });
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
|
-
return toPackages.signAndSave(pkg);
|
|
42
|
+
return toPackages.signAndSave(pkg, { restoreIfDeleted: true, saveAsSnapshot: true });
|
|
39
43
|
}
|
package/dist/data/tools.d.ts
CHANGED
|
@@ -172,16 +172,6 @@ export declare const toolSchema: z.ZodObject<{
|
|
|
172
172
|
}>;
|
|
173
173
|
/** A tool definition stored in the Tools table. */
|
|
174
174
|
export type ITool = z.infer<typeof toolSchema>;
|
|
175
|
-
/**
|
|
176
|
-
* Historical tool instance shape used by older packages.
|
|
177
|
-
* @deprecated Use IContractToolImplementation with a contract resolver.
|
|
178
|
-
*/
|
|
179
|
-
export interface IToolInstance {
|
|
180
|
-
tool: ITool;
|
|
181
|
-
toolFn: (...args: any[]) => any;
|
|
182
|
-
inputSchema?: z.ZodObject<any>;
|
|
183
|
-
outputSchema?: z.ZodObject<any>;
|
|
184
|
-
}
|
|
185
175
|
/**
|
|
186
176
|
* Accessor for the Tools table.
|
|
187
177
|
*
|
package/dist/define/context.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface IPackageProviderOptions {
|
|
|
14
14
|
providerPackageId?: string;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* The host functions the Runtime gives one
|
|
17
|
+
* The host functions the Runtime gives one guest API 3 handler call.
|
|
18
18
|
*
|
|
19
19
|
* Every tool handler gets its own `ctx`, and host calls made through it act for that call's
|
|
20
20
|
* caller, even while other calls run. The table and contract handles in this module take a
|
|
@@ -75,15 +75,13 @@ export interface IPackageContext {
|
|
|
75
75
|
watchOwnedTable(tableName: string, handler: (event: IDataChangedEvent<unknown>) => unknown): Promise<PackageUnsubscribe>;
|
|
76
76
|
/**
|
|
77
77
|
* Set the value of an in-memory observable this package provides (one declared without a
|
|
78
|
-
* `persistentVar`). The host checks it against the observable's `valueType
|
|
79
|
-
*
|
|
78
|
+
* `persistentVar`). The host checks it against the observable's `valueType` and pushes it
|
|
79
|
+
* to subscribers. Allowed at any time while the worker runs.
|
|
80
80
|
*/
|
|
81
81
|
publishOwnedObservable(contractId: string, version: number, observableName: string, value: unknown): Promise<unknown>;
|
|
82
82
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* returns what it stored. A later call replaces the handler. Guest API 3 instead routes
|
|
86
|
-
* writes through the contract member handler under the caller's authority.
|
|
83
|
+
* Compatibility no-op retained for built guest API 3 bundles. Consumer writes are
|
|
84
|
+
* served through the contract member handler under the caller's authority.
|
|
87
85
|
*/
|
|
88
86
|
onOwnedObservableSet(contractId: string, version: number, observableName: string, handler: (value: unknown) => unknown): void;
|
|
89
87
|
}
|
|
@@ -162,8 +162,7 @@ async function serveObservable(observable, targets, ctx) {
|
|
|
162
162
|
});
|
|
163
163
|
serving.set(observable, () => subscription.dispose());
|
|
164
164
|
if (definition.writable) {
|
|
165
|
-
//
|
|
166
|
-
// API 2 uses onOwnedObservableSet without a caller frame. Both parse before writing.
|
|
165
|
+
// The member handler runs this under the caller's authority and parses before writing.
|
|
167
166
|
const write = async (value) => {
|
|
168
167
|
const next = schema ? schema.parse((0, empty_fields_1.withoutEmptySchemaFields)(schema, value)) : value;
|
|
169
168
|
applying = true;
|
|
@@ -177,9 +176,6 @@ async function serveObservable(observable, targets, ctx) {
|
|
|
177
176
|
return publish(source());
|
|
178
177
|
};
|
|
179
178
|
writers.set(observable, write);
|
|
180
|
-
for (const target of targets) {
|
|
181
|
-
ctx.onOwnedObservableSet(target.contractId, target.version, name, write);
|
|
182
|
-
}
|
|
183
179
|
}
|
|
184
180
|
// A successful start guarantees readers have a published value. Later background failures
|
|
185
181
|
// are logged, but a failed initial publish must fail startup rather than silently serve null.
|
|
@@ -48,34 +48,17 @@ export interface IIsolatedContractTool extends Omit<IContractTool, "inputFields"
|
|
|
48
48
|
* {@link ISOLATED_TOOL_TIMEOUT_MAX_MS}. Host calls made during the invoke,
|
|
49
49
|
* including System HTTP, count against this deadline, as does time a handler spends
|
|
50
50
|
* waiting on the package's own locks. A call that times out is rejected; its handler keeps
|
|
51
|
-
* running unless the worker is stuck
|
|
51
|
+
* running unless the worker is stuck.
|
|
52
52
|
*/
|
|
53
53
|
timeoutMs?: number;
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
|
-
* The
|
|
57
|
-
*
|
|
58
|
-
* - `3`: as `2`, and the package serves its own contract members. The host sends consumers'
|
|
59
|
-
* table calls, observable reads and writes, and subscriptions to the provider's worker
|
|
60
|
-
* (`contractMemberHandlerName`), and passes every table change, event, and observable
|
|
61
|
-
* value bound for a subscriber through it first (`contractDeliveryHandlerName`).
|
|
62
|
-
* - `2`: handlers receive `(args, ctx)` and calls run concurrently; the package orders any
|
|
63
|
-
* state it shares between calls itself. The host serves contract tables and stored
|
|
64
|
-
* observables from storage, without the worker.
|
|
65
|
-
* - Omitted: the legacy global helpers, with one call at a time.
|
|
66
|
-
*
|
|
67
|
-
* Hosts that predate a version reject it.
|
|
56
|
+
* The guest API version built and run by Peers. Consumers' contract member calls and
|
|
57
|
+
* notifications pass through the provider's worker under the caller's authority.
|
|
68
58
|
*/
|
|
69
59
|
export declare const ISOLATED_GUEST_API_VERSION = 3;
|
|
70
|
-
/** Guest API
|
|
71
|
-
export type IsolatedGuestApi =
|
|
72
|
-
/**
|
|
73
|
-
* True when the package serves its contract members itself (guest API 3): every consumer call
|
|
74
|
-
* and outgoing message goes through its worker.
|
|
75
|
-
*
|
|
76
|
-
* @param manifest - The package's manifest.
|
|
77
|
-
*/
|
|
78
|
-
export declare function servesContractMembers(manifest: Pick<IIsolatedPackageManifest, "guestApi">): boolean;
|
|
60
|
+
/** Guest API version for packages built with peers-package. */
|
|
61
|
+
export type IsolatedGuestApi = 3;
|
|
79
62
|
/** Longest per-tool invoke deadline an isolated manifest may declare. */
|
|
80
63
|
export declare const ISOLATED_TOOL_TIMEOUT_MAX_MS = 120000;
|
|
81
64
|
/**
|
|
@@ -152,9 +135,9 @@ export interface IIsolatedContractPersistentVarBinding {
|
|
|
152
135
|
}
|
|
153
136
|
/**
|
|
154
137
|
* Observable contract member. With `persistentVar`, a package-owned persistent variable holds
|
|
155
|
-
* the value. Without it
|
|
156
|
-
* publishes it with `publishOwnedObservable`.
|
|
157
|
-
*
|
|
138
|
+
* the value. Without it, the worker holds the value in memory and
|
|
139
|
+
* publishes it with `publishOwnedObservable`. All consumer calls and deliveries pass
|
|
140
|
+
* through the worker.
|
|
158
141
|
*/
|
|
159
142
|
export interface IIsolatedContractObservable extends IContractObservable {
|
|
160
143
|
persistentVar?: IIsolatedContractPersistentVarBinding;
|
|
@@ -224,9 +207,8 @@ export interface IIsolatedPackageManifest {
|
|
|
224
207
|
network?: IIsolatedPackageNetwork;
|
|
225
208
|
appNavs?: IAppNav[];
|
|
226
209
|
/**
|
|
227
|
-
* Guest API version ({@link ISOLATED_GUEST_API_VERSION}).
|
|
228
|
-
*
|
|
229
|
-
* legacy global helpers, which run one call at a time.
|
|
210
|
+
* Guest API version ({@link ISOLATED_GUEST_API_VERSION}). Required by the runtime;
|
|
211
|
+
* optional here so build tools can inspect historical bundles without running them.
|
|
230
212
|
*/
|
|
231
213
|
guestApi?: IsolatedGuestApi;
|
|
232
214
|
}
|
|
@@ -311,15 +293,20 @@ export declare function looksLikeIsolatedPackageArtifact(bundle: string): boolea
|
|
|
311
293
|
*/
|
|
312
294
|
export declare function validateIsolatedPackageArtifact(artifact: IIsolatedPackageArtifact, options?: {
|
|
313
295
|
strict?: boolean;
|
|
296
|
+
/** Refuse unsupported guest APIs before validating their contents for execution. */
|
|
297
|
+
requireCurrentGuestApi?: boolean;
|
|
298
|
+
/** Display name to include alongside the package ID in a runtime refusal. */
|
|
299
|
+
packageName?: string;
|
|
314
300
|
}): IIsolatedPackageManifest;
|
|
315
301
|
/**
|
|
316
302
|
* Parse a magic-header + JSON isolated package bundle.
|
|
317
303
|
*
|
|
318
304
|
* @param bundle - Full package-bundle text.
|
|
305
|
+
* @param options - Runtime loaders require the current guest API; build tools may inspect older bundles.
|
|
319
306
|
* @returns The validated artifact.
|
|
320
307
|
* @throws If the magic header is present but the envelope is malformed.
|
|
321
308
|
*/
|
|
322
|
-
export declare function parseIsolatedPackageArtifact(bundle: string): IIsolatedPackageArtifact;
|
|
309
|
+
export declare function parseIsolatedPackageArtifact(bundle: string, options?: Parameters<typeof validateIsolatedPackageArtifact>[1]): IIsolatedPackageArtifact;
|
|
323
310
|
/**
|
|
324
311
|
* Serialize an isolated artifact into the package-bundle slot format.
|
|
325
312
|
*
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ISOLATED_MAX_FIELD_DEPTH = exports.ISOLATED_OBSERVABLE_HOST_MEMBER = exports.ISOLATED_EVENT_HOST_MEMBER = exports.ISOLATED_PERSISTENT_VAR_HOST_MEMBER = exports.ISOLATED_TABLE_HOST_MEMBER = exports.ISOLATED_CONSUME_HOST_MEMBER = exports.ISOLATED_TOOL_TIMEOUT_MAX_MS = exports.ISOLATED_GUEST_API_VERSION = exports.ISOLATED_PACKAGE_MAGIC = void 0;
|
|
4
|
-
exports.servesContractMembers = servesContractMembers;
|
|
5
4
|
exports.enumValueFits = enumValueFits;
|
|
6
5
|
exports.looksLikeIsolatedPackageArtifact = looksLikeIsolatedPackageArtifact;
|
|
7
6
|
exports.validateIsolatedPackageArtifact = validateIsolatedPackageArtifact;
|
|
@@ -30,29 +29,10 @@ const utils_1 = require("../utils");
|
|
|
30
29
|
*/
|
|
31
30
|
exports.ISOLATED_PACKAGE_MAGIC = "PEERS_ISOLATED_PACKAGE_V1";
|
|
32
31
|
/**
|
|
33
|
-
* The
|
|
34
|
-
*
|
|
35
|
-
* - `3`: as `2`, and the package serves its own contract members. The host sends consumers'
|
|
36
|
-
* table calls, observable reads and writes, and subscriptions to the provider's worker
|
|
37
|
-
* (`contractMemberHandlerName`), and passes every table change, event, and observable
|
|
38
|
-
* value bound for a subscriber through it first (`contractDeliveryHandlerName`).
|
|
39
|
-
* - `2`: handlers receive `(args, ctx)` and calls run concurrently; the package orders any
|
|
40
|
-
* state it shares between calls itself. The host serves contract tables and stored
|
|
41
|
-
* observables from storage, without the worker.
|
|
42
|
-
* - Omitted: the legacy global helpers, with one call at a time.
|
|
43
|
-
*
|
|
44
|
-
* Hosts that predate a version reject it.
|
|
32
|
+
* The guest API version built and run by Peers. Consumers' contract member calls and
|
|
33
|
+
* notifications pass through the provider's worker under the caller's authority.
|
|
45
34
|
*/
|
|
46
35
|
exports.ISOLATED_GUEST_API_VERSION = 3;
|
|
47
|
-
/**
|
|
48
|
-
* True when the package serves its contract members itself (guest API 3): every consumer call
|
|
49
|
-
* and outgoing message goes through its worker.
|
|
50
|
-
*
|
|
51
|
-
* @param manifest - The package's manifest.
|
|
52
|
-
*/
|
|
53
|
-
function servesContractMembers(manifest) {
|
|
54
|
-
return manifest.guestApi === 3;
|
|
55
|
-
}
|
|
56
36
|
/** Longest per-tool invoke deadline an isolated manifest may declare. */
|
|
57
37
|
exports.ISOLATED_TOOL_TIMEOUT_MAX_MS = 120_000;
|
|
58
38
|
/** Host-contract member guests use to request a consumed-contract tool call. */
|
|
@@ -340,7 +320,7 @@ function validatePackageTables(manifest, strict) {
|
|
|
340
320
|
(0, package_table_identity_1.validateTableIndexes)(table.indexes, new Set(table.fields.map((field) => field.name)), `${tableLabel}.indexes`, table.localOnly);
|
|
341
321
|
}
|
|
342
322
|
}
|
|
343
|
-
function validateProvidedContract(contract, index, packageTables,
|
|
323
|
+
function validateProvidedContract(contract, index, packageTables, strict) {
|
|
344
324
|
const label = `provides[${index}]`;
|
|
345
325
|
if (!contract || typeof contract !== "object" || Array.isArray(contract)) {
|
|
346
326
|
throw new Error(`${label} must be an object`);
|
|
@@ -488,13 +468,8 @@ function validateProvidedContract(contract, index, packageTables, guestApi, stri
|
|
|
488
468
|
throw new Error(`${observableLabel}.writable must be a boolean`);
|
|
489
469
|
}
|
|
490
470
|
const binding = observable.persistentVar;
|
|
491
|
-
if (binding === undefined)
|
|
492
|
-
// Held by the worker, which publishes it: that needs guest API 2 or later.
|
|
493
|
-
if (guestApi === undefined) {
|
|
494
|
-
throw new Error(`${observableLabel}.persistentVar is required unless the package uses guestApi 2 or later`);
|
|
495
|
-
}
|
|
471
|
+
if (binding === undefined)
|
|
496
472
|
continue;
|
|
497
|
-
}
|
|
498
473
|
if (!binding || typeof binding !== "object" || Array.isArray(binding)) {
|
|
499
474
|
throw new Error(`${observableLabel}.persistentVar must be an object`);
|
|
500
475
|
}
|
|
@@ -777,6 +752,15 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
|
|
|
777
752
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
778
753
|
throw new Error("Package artifact.manifest is required");
|
|
779
754
|
}
|
|
755
|
+
if (options.requireCurrentGuestApi && manifest.guestApi !== exports.ISOLATED_GUEST_API_VERSION) {
|
|
756
|
+
const identity = options.packageName
|
|
757
|
+
? `'${options.packageName}' (${manifest.packageId})`
|
|
758
|
+
: manifest.packageId;
|
|
759
|
+
if (typeof manifest.guestApi === "number" && manifest.guestApi > exports.ISOLATED_GUEST_API_VERSION) {
|
|
760
|
+
throw new Error(`Package ${identity} needs a newer version of Peers; update Peers to use it.`);
|
|
761
|
+
}
|
|
762
|
+
throw new Error(`Package ${identity} has an unsupported guest API. Rebuild it with peers-package; guest API 2 and packages without guestApi are no longer supported.`);
|
|
763
|
+
}
|
|
780
764
|
assertOnlyKeys(manifest, [
|
|
781
765
|
"packageId",
|
|
782
766
|
"version",
|
|
@@ -802,7 +786,9 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
|
|
|
802
786
|
manifest.executionMode !== "privileged") {
|
|
803
787
|
throw new Error('manifest.executionMode must be "sandbox" or "privileged"');
|
|
804
788
|
}
|
|
805
|
-
|
|
789
|
+
const guestApi = manifest.guestApi;
|
|
790
|
+
// Contract import/freezing tools still inspect historical bundles without executing them.
|
|
791
|
+
if (guestApi !== undefined && guestApi !== 2 && guestApi !== 3) {
|
|
806
792
|
throw new Error("manifest.guestApi must be 2 or 3 when set");
|
|
807
793
|
}
|
|
808
794
|
assertPeerId(manifest.packageId, "packageId");
|
|
@@ -820,7 +806,7 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
|
|
|
820
806
|
const packageTables = manifest.tables ?? [];
|
|
821
807
|
const providedKeys = new Set();
|
|
822
808
|
for (const [index, contract] of manifest.provides.entries()) {
|
|
823
|
-
validateProvidedContract(contract, index, packageTables,
|
|
809
|
+
validateProvidedContract(contract, index, packageTables, strict);
|
|
824
810
|
const key = `${contract.contractId}@${contract.version}`;
|
|
825
811
|
if (providedKeys.has(key)) {
|
|
826
812
|
throw new Error(`Duplicate provided contract ${key}`);
|
|
@@ -857,10 +843,11 @@ function validateIsolatedPackageArtifact(artifact, options = {}) {
|
|
|
857
843
|
* Parse a magic-header + JSON isolated package bundle.
|
|
858
844
|
*
|
|
859
845
|
* @param bundle - Full package-bundle text.
|
|
846
|
+
* @param options - Runtime loaders require the current guest API; build tools may inspect older bundles.
|
|
860
847
|
* @returns The validated artifact.
|
|
861
848
|
* @throws If the magic header is present but the envelope is malformed.
|
|
862
849
|
*/
|
|
863
|
-
function parseIsolatedPackageArtifact(bundle) {
|
|
850
|
+
function parseIsolatedPackageArtifact(bundle, options = {}) {
|
|
864
851
|
if (!looksLikeIsolatedPackageArtifact(bundle)) {
|
|
865
852
|
throw new Error("Bundle is not an isolated package artifact");
|
|
866
853
|
}
|
|
@@ -878,7 +865,7 @@ function parseIsolatedPackageArtifact(bundle) {
|
|
|
878
865
|
throw new Error(`Isolated package artifact JSON is invalid: ${err instanceof Error ? err.message : String(err)}`);
|
|
879
866
|
}
|
|
880
867
|
const artifact = parsed;
|
|
881
|
-
validateIsolatedPackageArtifact(artifact);
|
|
868
|
+
validateIsolatedPackageArtifact(artifact, options);
|
|
882
869
|
return artifact;
|
|
883
870
|
}
|
|
884
871
|
/**
|
|
@@ -30,7 +30,7 @@ export interface IPackageContractEndpointOptions {
|
|
|
30
30
|
* Loads package versions for one {@link DataContext}.
|
|
31
31
|
*
|
|
32
32
|
* Isolated artifacts (magic-header JSON) are installed without host
|
|
33
|
-
* evaluation.
|
|
33
|
+
* evaluation. Host-backed `definePackage()` bundles evaluate in-process.
|
|
34
34
|
*/
|
|
35
35
|
export declare class PackageLoader {
|
|
36
36
|
readonly dataContext: DataContext;
|
|
@@ -75,7 +75,7 @@ export declare class PackageLoader {
|
|
|
75
75
|
get catalogReady(): Promise<void>;
|
|
76
76
|
/** Optional isolated-provider supervisor created from the host factory. */
|
|
77
77
|
readonly isolatedRuntime?: IIsolatedPackageRuntime;
|
|
78
|
-
/** Optional host `require` used when reading local bundles or evaluating
|
|
78
|
+
/** Optional host `require` used when reading local bundles or evaluating host-backed packages. */
|
|
79
79
|
require: (<T>(module: string) => T) | undefined;
|
|
80
80
|
/**
|
|
81
81
|
* @param dataContext - Owning personal or group data context. Its
|
|
@@ -116,7 +116,7 @@ export declare class PackageLoader {
|
|
|
116
116
|
*/
|
|
117
117
|
private recordLoadFailure;
|
|
118
118
|
private dispatchToolFromProvider;
|
|
119
|
-
/** Project a
|
|
119
|
+
/** Project a host caller's declared tool reach and authority for advisory catalog listings. */
|
|
120
120
|
hostToolPrincipal(principal: IIsolatedCallPrincipal, packageId: string, target: {
|
|
121
121
|
contractId: string;
|
|
122
122
|
version: number;
|
|
@@ -146,7 +146,7 @@ export declare class PackageLoader {
|
|
|
146
146
|
resolveContractEndpoint(request: IContractProviderEndpointRequest, opts?: IPackageContractEndpointOptions): Promise<IContractProviderEndpoint>;
|
|
147
147
|
private guardResolutionTools;
|
|
148
148
|
/**
|
|
149
|
-
* Bind a
|
|
149
|
+
* Bind a host-backed package's declared consume to its selected provider.
|
|
150
150
|
*
|
|
151
151
|
* The returned proxy can target either a resolver-backed or isolated provider.
|
|
152
152
|
* Its provider choice is captured from {@link ContractRegistry.selectProvider}
|
|
@@ -158,7 +158,7 @@ export declare class PackageLoader {
|
|
|
158
158
|
*/
|
|
159
159
|
resolveConsumedContract(dependency: IConsumedContract, callerPackageId: string): Promise<IContractConsumer | undefined>;
|
|
160
160
|
/**
|
|
161
|
-
* Bind a
|
|
161
|
+
* Bind a host-backed package's `remote()` declaration to a per-device consumer factory.
|
|
162
162
|
*
|
|
163
163
|
* No local provider is selected: every device consumer stamps `targetDeviceId` and
|
|
164
164
|
* this loader's own router forwards the call through
|
|
@@ -46,7 +46,7 @@ function isolatedContractLifecycle(packageVersion) {
|
|
|
46
46
|
* Loads package versions for one {@link DataContext}.
|
|
47
47
|
*
|
|
48
48
|
* Isolated artifacts (magic-header JSON) are installed without host
|
|
49
|
-
* evaluation.
|
|
49
|
+
* evaluation. Host-backed `definePackage()` bundles evaluate in-process.
|
|
50
50
|
*/
|
|
51
51
|
class PackageLoader {
|
|
52
52
|
dataContext;
|
|
@@ -93,7 +93,7 @@ class PackageLoader {
|
|
|
93
93
|
}
|
|
94
94
|
/** Optional isolated-provider supervisor created from the host factory. */
|
|
95
95
|
isolatedRuntime;
|
|
96
|
-
/** Optional host `require` used when reading local bundles or evaluating
|
|
96
|
+
/** Optional host `require` used when reading local bundles or evaluating host-backed packages. */
|
|
97
97
|
require = undefined;
|
|
98
98
|
/**
|
|
99
99
|
* @param dataContext - Owning personal or group data context. Its
|
|
@@ -264,7 +264,7 @@ class PackageLoader {
|
|
|
264
264
|
endpoint.dispose();
|
|
265
265
|
}
|
|
266
266
|
}
|
|
267
|
-
/** Project a
|
|
267
|
+
/** Project a host caller's declared tool reach and authority for advisory catalog listings. */
|
|
268
268
|
hostToolPrincipal(principal, packageId, target) {
|
|
269
269
|
if (!principal.userId || this.isolatedRuntime?.isInstalled(packageId))
|
|
270
270
|
return undefined;
|
|
@@ -473,7 +473,7 @@ class PackageLoader {
|
|
|
473
473
|
};
|
|
474
474
|
}
|
|
475
475
|
/**
|
|
476
|
-
* Bind a
|
|
476
|
+
* Bind a host-backed package's declared consume to its selected provider.
|
|
477
477
|
*
|
|
478
478
|
* The returned proxy can target either a resolver-backed or isolated provider.
|
|
479
479
|
* Its provider choice is captured from {@link ContractRegistry.selectProvider}
|
|
@@ -514,7 +514,7 @@ class PackageLoader {
|
|
|
514
514
|
return consumer;
|
|
515
515
|
}
|
|
516
516
|
/**
|
|
517
|
-
* Bind a
|
|
517
|
+
* Bind a host-backed package's `remote()` declaration to a per-device consumer factory.
|
|
518
518
|
*
|
|
519
519
|
* No local provider is selected: every device consumer stamps `targetDeviceId` and
|
|
520
520
|
* this loader's own router forwards the call through
|
|
@@ -910,7 +910,10 @@ class PackageLoader {
|
|
|
910
910
|
assertCurrent();
|
|
911
911
|
if ((0, isolated_artifact_1.looksLikeIsolatedPackageArtifact)(bundleCode)) {
|
|
912
912
|
try {
|
|
913
|
-
const artifact = (0, isolated_artifact_1.parseIsolatedPackageArtifact)(bundleCode
|
|
913
|
+
const artifact = (0, isolated_artifact_1.parseIsolatedPackageArtifact)(bundleCode, {
|
|
914
|
+
requireCurrentGuestApi: true,
|
|
915
|
+
packageName: pkg.name,
|
|
916
|
+
});
|
|
914
917
|
return await this._installIsolated(pkg, artifact, selectedPackageVersion, usingLocalBundle, force, assertCurrent);
|
|
915
918
|
}
|
|
916
919
|
catch (err) {
|
|
@@ -920,7 +923,7 @@ class PackageLoader {
|
|
|
920
923
|
await this.catalogReady;
|
|
921
924
|
assertCurrent();
|
|
922
925
|
const replacingIsolated = this.isolatedRuntime?.isInstalled(pkg.packageId) ?? false;
|
|
923
|
-
const instance = await this._evaluateBundle(pkg, bundleCode, selectedPackageVersion,
|
|
926
|
+
const instance = await this._evaluateBundle(pkg, bundleCode, selectedPackageVersion, assertCurrent);
|
|
924
927
|
if (!instance) {
|
|
925
928
|
return undefined;
|
|
926
929
|
}
|
|
@@ -995,7 +998,7 @@ class PackageLoader {
|
|
|
995
998
|
});
|
|
996
999
|
return instance;
|
|
997
1000
|
}
|
|
998
|
-
async _evaluateBundle(pkg, bundleCode, selectedPackageVersion,
|
|
1001
|
+
async _evaluateBundle(pkg, bundleCode, selectedPackageVersion, assertCurrent = this.guardLoad(pkg.packageId)) {
|
|
999
1002
|
// Node.js built-in modules that do not exist in React Native (and would cause
|
|
1000
1003
|
// a fatal native error if passed through to RN's require). We block them here
|
|
1001
1004
|
// and throw a descriptive JS Error instead, which the surrounding try/catch
|
|
@@ -1063,19 +1066,16 @@ class PackageLoader {
|
|
|
1063
1066
|
const bundleFunction = new Function("module", "exports", "require", bundleCode);
|
|
1064
1067
|
const activateResolvers = (0, contract_resolvers_1.stagePackageContractResolvers)(this.dataContext, pkg.packageId, () => bundleFunction(module, moduleExports, customRequire));
|
|
1065
1068
|
const bundleExports = module.exports;
|
|
1066
|
-
//
|
|
1069
|
+
// Host bundles must export a definePackage() definition.
|
|
1067
1070
|
const packageDefinition = (0, contract_package_loader_1.extractPackageDefinition)(bundleExports);
|
|
1068
1071
|
if (packageDefinition) {
|
|
1069
1072
|
if (packageDefinition.packageId !== pkg.packageId) {
|
|
1070
1073
|
console.warn(`[PackageLoader] packageId mismatch: definition has "${packageDefinition.packageId}" but DB record has "${pkg.packageId}"`);
|
|
1071
1074
|
}
|
|
1072
|
-
//
|
|
1073
|
-
// (e.g. package-installer) can read appNavs, assistants, etc.
|
|
1075
|
+
// Expose loaded identity and navigation to callers such as package-installer.
|
|
1074
1076
|
const contractPackageInstance = {
|
|
1075
1077
|
packageId: packageDefinition.packageId,
|
|
1076
1078
|
appNavs: packageDefinition.appNavs,
|
|
1077
|
-
assistants: packageDefinition.assistants,
|
|
1078
|
-
tableDefinitions: packageDefinition.tableDefinitions,
|
|
1079
1079
|
};
|
|
1080
1080
|
await (0, contract_package_loader_1.installContractPackage)(this.dataContext, packageDefinition, {
|
|
1081
1081
|
registry: this.contractRegistry,
|
|
@@ -1100,36 +1100,7 @@ class PackageLoader {
|
|
|
1100
1100
|
assertCurrent();
|
|
1101
1101
|
return contractPackageInstance;
|
|
1102
1102
|
}
|
|
1103
|
-
|
|
1104
|
-
const packageInstance = bundleExports?.exports || bundleExports?.package || bundleExports?.default || bundleExports;
|
|
1105
|
-
if (packageInstance &&
|
|
1106
|
-
typeof packageInstance === "object" &&
|
|
1107
|
-
typeof packageInstance.packageId === "string" &&
|
|
1108
|
-
packageInstance.packageId.length > 0) {
|
|
1109
|
-
const tools = packageInstance.toolInstances;
|
|
1110
|
-
if (Array.isArray(tools) && tools.length)
|
|
1111
|
-
throw new Error(`Package '${pkg.name}' (${pkg.packageId}) ships legacy executable tools, which this runtime no longer runs; upgrade it to definePackage() contract tools (docs/Packages/contracts.md#migrating-from-toolinstances)`);
|
|
1112
|
-
await this.toolCatalog.publish(pkg.packageId, [], async () => assertCurrent(), () => {
|
|
1113
|
-
assertCurrent();
|
|
1114
|
-
// Final catalog writes must succeed before changing active host tables.
|
|
1115
|
-
if (packageInstance.tableDefinitions?.length) {
|
|
1116
|
-
this.dataContext.tableContainer.registerTableDefinitions(packageInstance.tableDefinitions, { overwrite: true });
|
|
1117
|
-
}
|
|
1118
|
-
activateResolvers();
|
|
1119
|
-
this.retireHostEndpoints(pkg.packageId);
|
|
1120
|
-
if (force && this.contractInstalledPackages.has(pkg.packageId)) {
|
|
1121
|
-
this.contractRegistry.unregister(pkg.packageId);
|
|
1122
|
-
this.contractInstalledPackages.delete(pkg.packageId);
|
|
1123
|
-
}
|
|
1124
|
-
this.packageInstances[pkg.packageId] = packageInstance;
|
|
1125
|
-
this.recordRunning(pkg.packageId, {
|
|
1126
|
-
...runningVersionFields(selectedPackageVersion),
|
|
1127
|
-
kind: "legacy",
|
|
1128
|
-
});
|
|
1129
|
-
});
|
|
1130
|
-
return packageInstance;
|
|
1131
|
-
}
|
|
1132
|
-
throw new Error("Package bundle exports nothing usable");
|
|
1103
|
+
throw new Error(`Package '${pkg.name}' (${pkg.packageId}) has an unsupported bundle format. Rebuild it with definePackage() and peers-package.`);
|
|
1133
1104
|
}
|
|
1134
1105
|
catch (err) {
|
|
1135
1106
|
this.recordLoadFailure(pkg.packageId, selectedPackageVersion, err, assertCurrent);
|
|
@@ -24,9 +24,8 @@ export type PackageRuntimeStatus = "running" | "failed" | "unavailable" | "unsup
|
|
|
24
24
|
*
|
|
25
25
|
* - `isolated` — an isolated artifact served by the host's isolated runtime.
|
|
26
26
|
* - `contract` — an in-process `definePackage()` bundle.
|
|
27
|
-
* - `legacy` — an in-process legacy bundle without a package definition.
|
|
28
27
|
*/
|
|
29
|
-
export type PackageRuntimeKind = "isolated" | "contract"
|
|
28
|
+
export type PackageRuntimeKind = "isolated" | "contract";
|
|
30
29
|
/** A contract a running package provides. */
|
|
31
30
|
export interface IPackageRuntimeProvidedContract {
|
|
32
31
|
/** Provided contract identity. */
|
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
import type { IAssistant } from "../data";
|
|
2
|
-
import type { ITableDefinition } from "../data/orm/table-definitions.type";
|
|
3
1
|
import type { IPeersUI } from "../peers-ui/peers-ui";
|
|
4
2
|
import type { IPeersUIRoute } from "../peers-ui/peers-ui.types";
|
|
5
3
|
import type { IAppNav } from "./app-nav";
|
|
6
|
-
|
|
7
|
-
/** Host package tables, assistants and UI; tools are provided by contracts. */
|
|
4
|
+
/** Identity and navigation of a loaded package; content is installed from its definition. */
|
|
8
5
|
export interface IPeersPackage {
|
|
9
6
|
packageId: string;
|
|
10
|
-
assistants?: IAssistant[];
|
|
11
|
-
workflows?: IWorkflow[];
|
|
12
7
|
appNavs?: IAppNav[];
|
|
13
|
-
tableDefinitions?: ITableDefinition[];
|
|
14
8
|
}
|
|
15
9
|
/** Routes exported by a host package. */
|
|
16
10
|
export interface IPeersPackageRoutes {
|