@peers-app/peers-sdk 0.25.8 → 0.25.10

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.
@@ -426,6 +426,32 @@ describe("contract proxy — error handling", () => {
426
426
  const consumer = consumerVia(contract, provider);
427
427
  await expect(consumer.tables.Widgets.list()).rejects.toThrow(/does not provide table 'Widgets'/);
428
428
  });
429
+ it("forwards callerPackageId when the resolution opts into call context", async () => {
430
+ const seen = [];
431
+ const provider = (0, contract_proxy_1.createContractProvider)({
432
+ contractId: CONTRACT_ID,
433
+ version: 1,
434
+ forwardCallContext: true,
435
+ tools: {
436
+ listGrants: {
437
+ tool: { toolId: (0, utils_1.newid)(), name: "listGrants", accessLevel: 0 },
438
+ toolFn: async (host) => {
439
+ seen.push(host.callerPackageId ?? null);
440
+ return "ok";
441
+ },
442
+ },
443
+ },
444
+ });
445
+ await provider({
446
+ contractId: CONTRACT_ID,
447
+ version: 1,
448
+ member: "tool",
449
+ name: "listGrants",
450
+ operation: "run",
451
+ args: [],
452
+ }, { callerPackageId: "00pkg00000000000000000001" });
453
+ expect(seen).toEqual(["00pkg00000000000000000001"]);
454
+ });
429
455
  it.each([
430
456
  ["tool", "missingTool"],
431
457
  ["observable", "missingObservable"],
@@ -520,6 +546,52 @@ describe("contract proxy — permission checks", () => {
520
546
  expect(denied.table.save).not.toHaveBeenCalled();
521
547
  });
522
548
  });
549
+ describe("contract provider endpoint event filter", () => {
550
+ it("delivers a generic event only to subscribers the filter allows", async () => {
551
+ const handlers = new Set();
552
+ const changed = {
553
+ eventName: () => "changed",
554
+ subscribe: (handler) => {
555
+ handlers.add(handler);
556
+ return {
557
+ unsubscribe: () => {
558
+ handlers.delete(handler);
559
+ return true;
560
+ },
561
+ };
562
+ },
563
+ };
564
+ const resolution = {
565
+ contractId: CONTRACT_ID,
566
+ version: 1,
567
+ events: { changed: changed },
568
+ eventFilter: (_eventName, payload, context) => payload.owner === context.callerPackageId,
569
+ };
570
+ const delivered = [];
571
+ const endpoint = (0, contract_proxy_1.createContractProviderEndpoint)(resolution, {
572
+ notify: async (notification) => {
573
+ delivered.push(notification.subscriptionId);
574
+ },
575
+ });
576
+ const subscribe = (subscriptionId, callerPackageId) => endpoint.handleCall({
577
+ contractId: CONTRACT_ID,
578
+ version: 1,
579
+ member: "event",
580
+ name: "changed",
581
+ operation: "subscribe",
582
+ subscriptionId,
583
+ args: [],
584
+ }, { callerPackageId });
585
+ await subscribe("allowed", "pkg-a");
586
+ await subscribe("denied", "pkg-b");
587
+ for (const handler of handlers) {
588
+ handler({ owner: "pkg-a" });
589
+ }
590
+ await new Promise((resolve) => setImmediate(resolve));
591
+ expect(delivered).toEqual(["allowed"]);
592
+ endpoint.dispose();
593
+ });
594
+ });
523
595
  describe("connectionContractTransport", () => {
524
596
  it("emits on the request channel with the call and returns the response", async () => {
525
597
  const emit = jest.fn(async (_event, _call) => ["ok"]);
@@ -220,6 +220,11 @@ export interface IContractToolContext {
220
220
  * by the host and returned only to the provider-side tool implementation.
221
221
  */
222
222
  getVariable(variableName: string, requestingToolId: string): Promise<string>;
223
+ /**
224
+ * Isolated package that originated this call, when the host runtime supplied one.
225
+ * Absent for trusted local callers (the signed-in UI session and the CLI).
226
+ */
227
+ callerPackageId?: string;
223
228
  }
224
229
  /**
225
230
  * Build the **consumer** side of a contract proxy from a contract definition and a
@@ -293,6 +298,13 @@ export interface IContractResolution {
293
298
  * Omit this for remote or otherwise untrusted provider routes.
294
299
  */
295
300
  toolContext?: IContractToolContext;
301
+ /**
302
+ * When true, provider tool functions receive an {@link IContractToolContext} as their
303
+ * final argument even if {@link toolContext} was not set, including
304
+ * {@link IContractToolContext.callerPackageId} from the current call. Host contracts
305
+ * that authorize by calling package set this.
306
+ */
307
+ forwardCallContext?: boolean;
296
308
  observables?: Record<string, Observable<any>>;
297
309
  /**
298
310
  * Provider-owned write policy for live observables. A `set` is allowed only when the
@@ -306,6 +318,20 @@ export interface IContractResolution {
306
318
  * their payloads to its configured notification sink.
307
319
  */
308
320
  events?: Record<string, Event<any>>;
321
+ /**
322
+ * Optional per-subscriber gate for generic contract events.
323
+ *
324
+ * Table and observable streams are not filtered. The endpoint invokes this once
325
+ * per subscriber with the {@link IContractCallContext} captured at subscribe time.
326
+ * Return false, or reject, to drop that notification for that subscriber only.
327
+ * A shared endpoint can therefore hide one caller's events from another.
328
+ *
329
+ * @param eventName Contract event name, such as `changed`.
330
+ * @param payload Payload about to be delivered.
331
+ * @param context Caller identity from the subscribe call.
332
+ * @returns Whether this subscriber may receive the payload.
333
+ */
334
+ eventFilter?: (eventName: string, payload: unknown, context: IContractCallContext) => boolean | Promise<boolean>;
309
335
  }
310
336
  /** Identity of the caller, supplied by the transport layer for permission checks. */
311
337
  export interface IContractCallContext {
@@ -319,6 +345,11 @@ export interface IContractCallContext {
319
345
  * host router before dispatch.
320
346
  */
321
347
  callerDeviceId?: string;
348
+ /**
349
+ * Isolated package that originated this call. Set by the isolate runtime from host
350
+ * state, never from a guest payload. Absent for trusted local UI and CLI callers.
351
+ */
352
+ callerPackageId?: string;
322
353
  /**
323
354
  * Provider-side data context used to resolve the caller's group/personal role.
324
355
  * In-process only — never serialized across a connection.
@@ -418,7 +449,9 @@ export interface IContractProviderEndpoint {
418
449
  * or an {@link Observable} (`resolution.observables[name]`) — is only subscribed to while at
419
450
  * least one consumer holds a subscription (refcounted by a resolved key), and each payload is
420
451
  * fanned out through the supplied {@link ContractNotificationSink} as an
421
- * {@link IContractNotification}. The endpoint owns no request listener, which lets a
452
+ * {@link IContractNotification}. Generic events pass through
453
+ * {@link IContractResolution.eventFilter} once per subscriber, using the caller context
454
+ * captured when that subscriber subscribed. The endpoint owns no request listener, which lets a
422
455
  * connection-wide router multiplex many endpoints through one request channel and later
423
456
  * substitute sandbox-backed endpoint implementations.
424
457
  *
@@ -534,9 +534,19 @@ function createContractProvider(resolution, opts = {}) {
534
534
  throw new Error(`Contract ${call.contractId} does not provide tool '${call.name}'`);
535
535
  }
536
536
  await assertContractToolAccess(tool, call, context);
537
- return resolution.toolContext
538
- ? tool.toolFn(...call.args, resolution.toolContext)
539
- : tool.toolFn(...call.args);
537
+ if (resolution.toolContext || resolution.forwardCallContext) {
538
+ const base = resolution.toolContext ?? {
539
+ dataContextId: context.dataContext?.dataContextId ?? "",
540
+ getVariable: async () => {
541
+ throw new Error("Persistent variables are not available to this contract tool");
542
+ },
543
+ };
544
+ return tool.toolFn(...call.args, {
545
+ ...base,
546
+ callerPackageId: context.callerPackageId,
547
+ });
548
+ }
549
+ return tool.toolFn(...call.args);
540
550
  }
541
551
  case "observable": {
542
552
  const obs = resolution.observables?.[call.name];
@@ -571,7 +581,9 @@ function createContractProvider(resolution, opts = {}) {
571
581
  * or an {@link Observable} (`resolution.observables[name]`) — is only subscribed to while at
572
582
  * least one consumer holds a subscription (refcounted by a resolved key), and each payload is
573
583
  * fanned out through the supplied {@link ContractNotificationSink} as an
574
- * {@link IContractNotification}. The endpoint owns no request listener, which lets a
584
+ * {@link IContractNotification}. Generic events pass through
585
+ * {@link IContractResolution.eventFilter} once per subscriber, using the caller context
586
+ * captured when that subscriber subscribed. The endpoint owns no request listener, which lets a
575
587
  * connection-wide router multiplex many endpoints through one request channel and later
576
588
  * substitute sandbox-backed endpoint implementations.
577
589
  *
@@ -581,9 +593,10 @@ function createContractProvider(resolution, opts = {}) {
581
593
  function createContractProviderEndpoint(resolution, opts) {
582
594
  const { notify, permissionCheck, context: defaultContext } = opts;
583
595
  const dispatch = createContractProvider(resolution, { permissionCheck });
584
- // One live subscription per resolved subscription key, refcounted by the set of
585
- // consumer subscription ids currently attached to it, plus a reverse index for
586
- // unsubscribe. Keys namespace generic events (`event:<name>`), table events
596
+ // One live subscription per resolved subscription key, refcounted by the subscribers
597
+ // currently attached to it, plus a reverse index for unsubscribe. Each subscriber keeps
598
+ // the caller context from its subscribe call so event filters can differ per consumer.
599
+ // Keys namespace generic events (`event:<name>`), table events
587
600
  // (`table:<name>:<event>`), and observable streams (`observable:<name>`) so the same
588
601
  // machinery serves all three.
589
602
  const byKey = new Map();
@@ -651,29 +664,51 @@ function createContractProviderEndpoint(resolution, opts) {
651
664
  },
652
665
  };
653
666
  };
654
- const subscribe = (key, attach, subscriptionId) => {
667
+ const reportNotifyFailure = (subscriptionId, err) => {
668
+ console.error(`Contract notify failed for subscription '${subscriptionId}'`, err);
669
+ };
670
+ const deliver = (key, subscriptionId, payload, context) => {
671
+ // Start each notification independently. A slow or rejected destination must not
672
+ // block sibling subscribers, and every rejection is handled because Observable
673
+ // callbacks do not await returned promises. Without a filter, attach the catch
674
+ // directly so a rejected notify is logged on the same turn as before.
675
+ if (!(key.startsWith("event:") && resolution.eventFilter)) {
676
+ if (!subIdToKey.has(subscriptionId)) {
677
+ return;
678
+ }
679
+ void notifySink(subscriptionId, payload, key).catch((err) => {
680
+ reportNotifyFailure(subscriptionId, err);
681
+ });
682
+ return;
683
+ }
684
+ void (async () => {
685
+ const allowed = await resolution.eventFilter?.(key.slice("event:".length), payload, context);
686
+ if (!allowed || !subIdToKey.has(subscriptionId)) {
687
+ return;
688
+ }
689
+ await notifySink(subscriptionId, payload, key);
690
+ })().catch((err) => {
691
+ reportNotifyFailure(subscriptionId, err);
692
+ });
693
+ };
694
+ const subscribe = (key, attach, subscriptionId, context) => {
655
695
  if (subIdToKey.has(subscriptionId)) {
656
696
  throw new Error(`Duplicate contract subscriptionId '${subscriptionId}'`);
657
697
  }
658
698
  let entry = byKey.get(key);
659
699
  if (!entry) {
660
700
  // First subscriber for this key: attach the single live subscription.
661
- const subIds = new Set();
701
+ const subscribers = new Map();
662
702
  const detach = attach((payload) => {
663
- // Start each notification independently. A slow or rejected destination must not
664
- // block sibling subscribers, and every rejection is handled because Observable
665
- // callbacks do not await returned promises.
666
- for (const sid of [...subIds]) {
667
- void notifySink(sid, payload, key).catch((err) => {
668
- console.error(`Contract notify failed for subscription '${sid}'`, err);
669
- });
703
+ for (const [sid, subscriberContext] of [...subscribers]) {
704
+ deliver(key, sid, payload, subscriberContext);
670
705
  }
671
706
  });
672
- entry = { liveUnsub: detach, subIds };
707
+ entry = { liveUnsub: detach, subscribers };
673
708
  byKey.set(key, entry);
674
709
  }
675
710
  subIdToKey.set(subscriptionId, key);
676
- entry.subIds.add(subscriptionId);
711
+ entry.subscribers.set(subscriptionId, context);
677
712
  };
678
713
  const unsubscribe = (subscriptionId) => {
679
714
  const key = subIdToKey.get(subscriptionId);
@@ -685,8 +720,8 @@ function createContractProviderEndpoint(resolution, opts) {
685
720
  if (!entry) {
686
721
  return;
687
722
  }
688
- entry.subIds.delete(subscriptionId);
689
- if (entry.subIds.size === 0) {
723
+ entry.subscribers.delete(subscriptionId);
724
+ if (entry.subscribers.size === 0) {
690
725
  // Last subscriber left: detach the live subscription (nothing more is pushed).
691
726
  entry.liveUnsub();
692
727
  byKey.delete(key);
@@ -731,7 +766,7 @@ function createContractProviderEndpoint(resolution, opts) {
731
766
  throw new Error(`Contract provider endpoint ${resolution.contractId} v${resolution.version} is disposed`);
732
767
  }
733
768
  const { key, attach } = resolveSubscription(call);
734
- subscribe(key, attach, call.subscriptionId);
769
+ subscribe(key, attach, call.subscriptionId, context);
735
770
  }
736
771
  finally {
737
772
  pendingSubIds.delete(call.subscriptionId);
@@ -149,6 +149,7 @@ const permissionDenied = (call, reason) => new Error(`Permission denied: ${call.
149
149
  const localOnlyContractIds = new Set([
150
150
  system_ids_1.identityContractId,
151
151
  system_ids_1.invitesContractId,
152
+ system_ids_1.localFoldersContractId,
152
153
  system_ids_1.toolAccessControlContractId,
153
154
  ]);
154
155
  /**
@@ -15,6 +15,7 @@ export { createRemoteContractEndpoint, getRemoteContractTransportFactory, type I
15
15
  export { DEVICE_OPERATIONS_CONTRACT_VERSION, DEVICE_OPERATIONS_MAX_QUERY_LENGTH, DEVICE_OPERATIONS_MAX_RESULT_BYTES, DEVICE_OPERATIONS_MAX_ROWS, DEVICE_OPERATIONS_MAX_TOOLS, type DeviceOperationCapability, deviceOperationsProviderPackageId, deviceOperationsToolIds, getDeviceOperationsContractDefinition, type IDeviceDatabaseQueryInput, type IDeviceDatabaseQueryResult, type IDeviceOperationStatus, type IDeviceToolListInput, type IDeviceToolLookupInput, type IDeviceToolLookupResult, type IDeviceToolRunInput, } from "./system/device-operations-contract";
16
16
  export { getIdentityContractDefinition, IDENTITY_CONTRACT_VERSION, type IIdentityConsumer, type IIdentitySessionInfo, } from "./system/identity-contract";
17
17
  export { getInvitesContractDefinition, type IAcceptInviteResult, type ICreatedInvite, type IInvitePreview, type IInvitesConsumer, INVITES_CONTRACT_VERSION, } from "./system/invites-contract";
18
+ export { getLocalFoldersContractDefinition, type ILocalFolderChange, type ILocalFolderChangedEvent, type ILocalFolderEntry, type ILocalFolderFileBlocks, type ILocalFolderFileBlocksInput, type ILocalFolderGrantInfo, type ILocalFolderIndexInput, type ILocalFolderJobFinishedEvent, type ILocalFolderJobHandle, type ILocalFolderJobInfo, type ILocalFolderJobProgressEvent, type ILocalFolderListInput, type ILocalFolderMoveInput, type ILocalFolderPathInput, type ILocalFolderPurgeTrashInput, type ILocalFolderReadRangeInput, type ILocalFolderReadRangeResult, type ILocalFolderRequestGrantInput, type ILocalFolderStatInput, LOCAL_FOLDERS_CHUNK_AVG, LOCAL_FOLDERS_CHUNK_MAX, LOCAL_FOLDERS_CHUNK_MIN, LOCAL_FOLDERS_CONTRACT_VERSION, LOCAL_FOLDERS_JOB_TTL_MS, LOCAL_FOLDERS_READ_RANGE_MAX, LOCAL_FOLDERS_RESERVED_DIR, type LocalFolderChangeKind, type LocalFolderEntryKind, type LocalFolderMode, localFoldersProviderPackageId, } from "./system/local-folders-contract";
18
19
  export { getLogsContractDefinition, type ILogsConsumer, LOGS_CONTRACT_VERSION, resolveLogsContract, } from "./system/logs-contract";
19
20
  export * from "./system/network-contracts";
20
21
  export { getToolAccessControlContractDefinition, type IToolAccessControlConsumer, TOOL_ACCESS_CONTROL_CONTRACT_VERSION, } from "./system/tool-access-control-contract";
@@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.DEVICE_OPERATIONS_MAX_ROWS = exports.DEVICE_OPERATIONS_MAX_RESULT_BYTES = exports.DEVICE_OPERATIONS_MAX_QUERY_LENGTH = exports.DEVICE_OPERATIONS_CONTRACT_VERSION = exports.setRemoteContractTransportFactory = exports.resolveRemoteTargetEndpoint = exports.getRemoteContractTransportFactory = exports.createRemoteContractEndpoint = exports.ContractRegistry = exports.PersistentContractRegistry = exports.waitForHostContractTransport = exports.subscribeHostContractTransport = exports.registerHostContractTransport = exports.getHostContractTransport = exports.hostContractConsumer = exports.setSchemaToFieldsFn = exports.extractToolShape = exports.extractTableShape = exports.createLocalContractConsumer = exports.contractsSchema = exports.contractsMetaData = exports.ContractsTable = exports.Contracts = exports.inProcessTransportPair = exports.connectionContractTransport = exports.CONTRACT_REQUEST_CHANNEL = exports.CONTRACT_NOTIFY_CHANNEL = exports.verifyRemoteContractIdentity = exports.registerContractResolver = exports.isSelfTrustedContractCaller = exports.handleContractCall = exports.getContractResolver = exports.createUserContextContractProviderRouter = exports.createLocalUserContextContractProviderRouter = exports.authorizeRemoteContractCall = exports.sameUserContractPermissionCheck = exports.createRemoteContractConsumer = exports.createContractProviderSession = exports.createContractProviderEndpoint = exports.createContractProvider = exports.createContractConsumer = exports.CONTRACT_CALL_RPC = exports.contractProvidersSchema = exports.contractProvidersMetaData = exports.ContractProvidersTable = exports.ContractProviders = exports.createContractProviderRouter = exports.PackageBuilder = exports.definePackage = exports.ContractBuilder = void 0;
18
- exports.validateProviderSatisfiesContract = exports.validateImmutability = exports.validateAlsoImplements = exports.contractKey = exports.registerSystemContract = exports.listSystemContracts = exports.installSystemContracts = exports.TOOL_ACCESS_CONTROL_CONTRACT_VERSION = exports.getToolAccessControlContractDefinition = exports.resolveLogsContract = exports.LOGS_CONTRACT_VERSION = exports.getLogsContractDefinition = exports.INVITES_CONTRACT_VERSION = exports.getInvitesContractDefinition = exports.IDENTITY_CONTRACT_VERSION = exports.getIdentityContractDefinition = exports.getDeviceOperationsContractDefinition = exports.deviceOperationsToolIds = exports.deviceOperationsProviderPackageId = exports.DEVICE_OPERATIONS_MAX_TOOLS = void 0;
18
+ exports.validateProviderSatisfiesContract = exports.validateImmutability = exports.validateAlsoImplements = exports.contractKey = exports.registerSystemContract = exports.listSystemContracts = exports.installSystemContracts = exports.TOOL_ACCESS_CONTROL_CONTRACT_VERSION = exports.getToolAccessControlContractDefinition = exports.resolveLogsContract = exports.LOGS_CONTRACT_VERSION = exports.getLogsContractDefinition = exports.localFoldersProviderPackageId = exports.LOCAL_FOLDERS_RESERVED_DIR = exports.LOCAL_FOLDERS_READ_RANGE_MAX = exports.LOCAL_FOLDERS_JOB_TTL_MS = exports.LOCAL_FOLDERS_CONTRACT_VERSION = exports.LOCAL_FOLDERS_CHUNK_MIN = exports.LOCAL_FOLDERS_CHUNK_MAX = exports.LOCAL_FOLDERS_CHUNK_AVG = exports.getLocalFoldersContractDefinition = exports.INVITES_CONTRACT_VERSION = exports.getInvitesContractDefinition = exports.IDENTITY_CONTRACT_VERSION = exports.getIdentityContractDefinition = exports.getDeviceOperationsContractDefinition = exports.deviceOperationsToolIds = exports.deviceOperationsProviderPackageId = exports.DEVICE_OPERATIONS_MAX_TOOLS = void 0;
19
19
  var builder_1 = require("./builder");
20
20
  Object.defineProperty(exports, "ContractBuilder", { enumerable: true, get: function () { return builder_1.ContractBuilder; } });
21
21
  Object.defineProperty(exports, "definePackage", { enumerable: true, get: function () { return builder_1.definePackage; } });
@@ -91,6 +91,16 @@ Object.defineProperty(exports, "IDENTITY_CONTRACT_VERSION", { enumerable: true,
91
91
  var invites_contract_1 = require("./system/invites-contract");
92
92
  Object.defineProperty(exports, "getInvitesContractDefinition", { enumerable: true, get: function () { return invites_contract_1.getInvitesContractDefinition; } });
93
93
  Object.defineProperty(exports, "INVITES_CONTRACT_VERSION", { enumerable: true, get: function () { return invites_contract_1.INVITES_CONTRACT_VERSION; } });
94
+ var local_folders_contract_1 = require("./system/local-folders-contract");
95
+ Object.defineProperty(exports, "getLocalFoldersContractDefinition", { enumerable: true, get: function () { return local_folders_contract_1.getLocalFoldersContractDefinition; } });
96
+ Object.defineProperty(exports, "LOCAL_FOLDERS_CHUNK_AVG", { enumerable: true, get: function () { return local_folders_contract_1.LOCAL_FOLDERS_CHUNK_AVG; } });
97
+ Object.defineProperty(exports, "LOCAL_FOLDERS_CHUNK_MAX", { enumerable: true, get: function () { return local_folders_contract_1.LOCAL_FOLDERS_CHUNK_MAX; } });
98
+ Object.defineProperty(exports, "LOCAL_FOLDERS_CHUNK_MIN", { enumerable: true, get: function () { return local_folders_contract_1.LOCAL_FOLDERS_CHUNK_MIN; } });
99
+ Object.defineProperty(exports, "LOCAL_FOLDERS_CONTRACT_VERSION", { enumerable: true, get: function () { return local_folders_contract_1.LOCAL_FOLDERS_CONTRACT_VERSION; } });
100
+ Object.defineProperty(exports, "LOCAL_FOLDERS_JOB_TTL_MS", { enumerable: true, get: function () { return local_folders_contract_1.LOCAL_FOLDERS_JOB_TTL_MS; } });
101
+ Object.defineProperty(exports, "LOCAL_FOLDERS_READ_RANGE_MAX", { enumerable: true, get: function () { return local_folders_contract_1.LOCAL_FOLDERS_READ_RANGE_MAX; } });
102
+ Object.defineProperty(exports, "LOCAL_FOLDERS_RESERVED_DIR", { enumerable: true, get: function () { return local_folders_contract_1.LOCAL_FOLDERS_RESERVED_DIR; } });
103
+ Object.defineProperty(exports, "localFoldersProviderPackageId", { enumerable: true, get: function () { return local_folders_contract_1.localFoldersProviderPackageId; } });
94
104
  var logs_contract_1 = require("./system/logs-contract");
95
105
  Object.defineProperty(exports, "getLogsContractDefinition", { enumerable: true, get: function () { return logs_contract_1.getLogsContractDefinition; } });
96
106
  Object.defineProperty(exports, "LOGS_CONTRACT_VERSION", { enumerable: true, get: function () { return logs_contract_1.LOGS_CONTRACT_VERSION; } });
@@ -0,0 +1,163 @@
1
+ import type { IContractDefinition } from "../types";
2
+ /** Version of the built-in System Local Folders contract. */
3
+ export declare const LOCAL_FOLDERS_CONTRACT_VERSION = 1;
4
+ /** Maximum byte length accepted by `readRange` in one call. */
5
+ export declare const LOCAL_FOLDERS_READ_RANGE_MAX: number;
6
+ /** FastCDC minimum block size. Files at or below this size are one block. */
7
+ export declare const LOCAL_FOLDERS_CHUNK_MIN: number;
8
+ /** FastCDC target average block size. */
9
+ export declare const LOCAL_FOLDERS_CHUNK_AVG: number;
10
+ /** FastCDC maximum block size. */
11
+ export declare const LOCAL_FOLDERS_CHUNK_MAX: number;
12
+ /** Directory name reserved inside every grant for trash and in-flight files. */
13
+ export declare const LOCAL_FOLDERS_RESERVED_DIR = ".peers";
14
+ /** How long finished index jobs stay readable via `getJob`. */
15
+ export declare const LOCAL_FOLDERS_JOB_TTL_MS: number;
16
+ /** Package ID used by live System Local Folders tool instances. */
17
+ export declare const localFoldersProviderPackageId = "000peers0system0000000001";
18
+ /** Grant mode accepted by `requestGrant`. */
19
+ export type LocalFolderMode = "read" | "readwrite";
20
+ /** Filesystem entry kind returned by `list` and `stat`. */
21
+ export type LocalFolderEntryKind = "file" | "dir" | "symlink";
22
+ /** Watcher change kind delivered on the `changed` event. */
23
+ export type LocalFolderChangeKind = "created" | "modified" | "deleted" | "renamed";
24
+ /** Input for {@link ILocalFoldersConsumer tools.requestGrant}. */
25
+ export interface ILocalFolderRequestGrantInput {
26
+ groupId: string;
27
+ mode: LocalFolderMode;
28
+ /** Operator-only. Ignored when an isolated package is the caller. */
29
+ packageId?: string;
30
+ /**
31
+ * Operator-only absolute path. Isolated callers cannot set this.
32
+ * Omit it on a desktop host to open the folder picker.
33
+ */
34
+ absolutePath?: string;
35
+ }
36
+ /** One active grant returned by `requestGrant` and `listGrants`. */
37
+ export interface ILocalFolderGrantInfo {
38
+ grantId: string;
39
+ groupId: string;
40
+ absolutePath: string;
41
+ mode: LocalFolderMode;
42
+ packageId?: string;
43
+ createdAt: number;
44
+ }
45
+ /** Input for listing a directory inside a grant. */
46
+ export interface ILocalFolderListInput {
47
+ grantId: string;
48
+ /** Relative path. Empty lists the grant root. */
49
+ relPath?: string;
50
+ recursive?: boolean;
51
+ }
52
+ /** One directory entry. */
53
+ export interface ILocalFolderEntry {
54
+ relPath: string;
55
+ name: string;
56
+ kind: LocalFolderEntryKind;
57
+ size: number;
58
+ mtimeMs: number;
59
+ inode: string;
60
+ }
61
+ /** Input for `stat`. */
62
+ export interface ILocalFolderStatInput {
63
+ grantId: string;
64
+ relPath: string;
65
+ }
66
+ /** Input for a bounded read. `length` is capped at {@link LOCAL_FOLDERS_READ_RANGE_MAX}. */
67
+ export interface ILocalFolderReadRangeInput {
68
+ grantId: string;
69
+ relPath: string;
70
+ offset: number;
71
+ length: number;
72
+ }
73
+ /** Bytes returned by `readRange`. `data` is a base64 string so it survives JSON tool results. */
74
+ export interface ILocalFolderReadRangeResult {
75
+ dataBase64: string;
76
+ bytesRead: number;
77
+ }
78
+ /** Input for starting an index job. */
79
+ export interface ILocalFolderIndexInput {
80
+ grantId: string;
81
+ /** Relative directory or file. Empty indexes the whole grant. */
82
+ relPath?: string;
83
+ }
84
+ /** Handle returned by `index`. */
85
+ export interface ILocalFolderJobHandle {
86
+ jobId: string;
87
+ }
88
+ /** Input for `getFileBlocks`. */
89
+ export interface ILocalFolderFileBlocksInput {
90
+ grantId: string;
91
+ /** Relative file path. */
92
+ relPath: string;
93
+ }
94
+ /** Indexed blocks for one file. `indexed` is false when the path has no index row. */
95
+ export interface ILocalFolderFileBlocks {
96
+ indexed: boolean;
97
+ size?: number;
98
+ mtimeMs?: number;
99
+ blocks: {
100
+ hash: string;
101
+ size: number;
102
+ }[];
103
+ }
104
+ /** Index job snapshot returned by `getJob`. */
105
+ export interface ILocalFolderJobInfo {
106
+ jobId: string;
107
+ kind: "index";
108
+ status: "running" | "done" | "error" | "cancelled";
109
+ done: number;
110
+ total: number;
111
+ error?: string;
112
+ result?: {
113
+ files: number;
114
+ rehashed: number;
115
+ skipped: number;
116
+ removed: number;
117
+ };
118
+ }
119
+ /** Input for `move`. */
120
+ export interface ILocalFolderMoveInput {
121
+ grantId: string;
122
+ from: string;
123
+ to: string;
124
+ }
125
+ /** Input for `trash` and single-path tools. */
126
+ export interface ILocalFolderPathInput {
127
+ grantId: string;
128
+ relPath: string;
129
+ }
130
+ /** Input for `purgeTrash`. */
131
+ export interface ILocalFolderPurgeTrashInput {
132
+ grantId: string;
133
+ olderThanDays: number;
134
+ }
135
+ /** One path change inside a grant. */
136
+ export interface ILocalFolderChange {
137
+ relPath: string;
138
+ kind: LocalFolderChangeKind;
139
+ }
140
+ /** Payload of the `changed` event. */
141
+ export interface ILocalFolderChangedEvent {
142
+ grantId: string;
143
+ changes: ILocalFolderChange[];
144
+ }
145
+ /** Payload of the `jobProgress` event. */
146
+ export interface ILocalFolderJobProgressEvent {
147
+ jobId: string;
148
+ done: number;
149
+ total: number;
150
+ }
151
+ /** Payload of the `jobFinished` event. */
152
+ export interface ILocalFolderJobFinishedEvent {
153
+ jobId: string;
154
+ status: "done" | "error" | "cancelled";
155
+ error?: string;
156
+ }
157
+ /**
158
+ * Return the local-only System Local Folders contract shape.
159
+ *
160
+ * The host checks every path against an operator grant. Remote devices cannot call
161
+ * this contract; bytes between devices move through block exchange, not these tools.
162
+ */
163
+ export declare function getLocalFoldersContractDefinition(): IContractDefinition;