@peers-app/peers-sdk 0.26.6 → 0.27.1
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 +3 -1
- package/bin/peers-package.js +13 -0
- package/define/package.json +4 -0
- package/dist/contracts/__tests__/action-dispatch.test.js +56 -76
- package/dist/contracts/__tests__/also-implements.test.d.ts +1 -0
- package/dist/contracts/__tests__/also-implements.test.js +110 -0
- package/dist/contracts/__tests__/builder.test.js +45 -0
- package/dist/contracts/__tests__/contract-resolvers.test.js +134 -4
- package/dist/contracts/__tests__/frozen-contracts.test.d.ts +1 -0
- package/dist/contracts/__tests__/frozen-contracts.test.js +191 -0
- package/dist/contracts/__tests__/generic-tool-dispatch.test.js +13 -56
- package/dist/contracts/__tests__/host-contract-client.test.d.ts +1 -0
- package/dist/contracts/__tests__/host-contract-client.test.js +793 -0
- package/dist/contracts/__tests__/host-contract-transport.test.js +15 -0
- package/dist/contracts/__tests__/registry.test.js +68 -0
- package/dist/contracts/__tests__/target-dispatch.d.ts +11 -0
- package/dist/contracts/__tests__/target-dispatch.js +24 -0
- package/dist/contracts/__tests__/tool-catalog-service.test.js +37 -1
- package/dist/contracts/__tests__/tool-catalog.test.js +12 -3
- package/dist/contracts/__tests__/validate.test.js +177 -7
- package/dist/contracts/__tests__/workflow-context.test.js +0 -11
- package/dist/contracts/builder.d.ts +16 -2
- package/dist/contracts/builder.js +44 -3
- package/dist/contracts/contract-definition-schema.d.ts +251 -0
- package/dist/contracts/contract-definition-schema.js +48 -0
- package/dist/contracts/contract-provider-router.d.ts +2 -0
- package/dist/contracts/contract-provider-router.js +5 -2
- package/dist/contracts/contract-proxy.d.ts +52 -18
- package/dist/contracts/contract-proxy.js +144 -203
- package/dist/contracts/contract-resolvers.d.ts +6 -4
- package/dist/contracts/contract-resolvers.js +32 -9
- package/dist/contracts/frozen-contracts.d.ts +59 -0
- package/dist/contracts/frozen-contracts.js +86 -0
- package/dist/contracts/host-contract-client.d.ts +134 -0
- package/dist/contracts/host-contract-client.js +396 -0
- package/dist/contracts/host-contract-transport.d.ts +2 -0
- package/dist/contracts/host-contract-transport.js +8 -0
- package/dist/contracts/index.d.ts +6 -4
- package/dist/contracts/index.js +11 -5
- package/dist/contracts/registry.d.ts +31 -1
- package/dist/contracts/registry.js +100 -6
- package/dist/contracts/system/contract-catalog-contract.d.ts +61 -6
- package/dist/contracts/system/contract-catalog-contract.js +17 -5
- package/dist/contracts/tool-catalog-service.js +26 -6
- package/dist/contracts/tool-catalog.d.ts +1 -1
- package/dist/contracts/tool-catalog.js +4 -1
- package/dist/contracts/types.d.ts +32 -2
- package/dist/contracts/validate.d.ts +16 -1
- package/dist/contracts/validate.js +139 -25
- package/dist/data/change-tracking.d.ts +17 -5
- package/dist/data/change-tracking.js +22 -6
- package/dist/data/change-tracking.test.d.ts +1 -0
- package/dist/data/change-tracking.test.js +62 -0
- package/dist/data/create-group.js +1 -1
- package/dist/data/group-members.d.ts +14 -0
- package/dist/data/group-members.js +36 -0
- package/dist/data/group-members.test.js +49 -0
- package/dist/data/orm/sql.data-source.d.ts +9 -0
- package/dist/data/orm/sql.data-source.js +47 -2
- package/dist/data/orm/sql.data-source.test.js +142 -0
- package/dist/data/orm/table-container.js +9 -9
- package/dist/data/orm/table-container.test.js +44 -0
- package/dist/data/orm/types.d.ts +41 -1
- package/dist/data/orm/types.js +159 -28
- package/dist/data/orm/types.test.js +129 -0
- package/dist/data/peer-types.d.ts +8 -62
- package/dist/data/persistent-vars.d.ts +4 -1
- package/dist/data/persistent-vars.js +7 -2
- package/dist/data/tools.d.ts +29 -221
- package/dist/define/also-implements.test.d.ts +1 -0
- package/dist/define/also-implements.test.js +123 -0
- package/dist/define/context.d.ts +77 -0
- package/dist/define/context.js +2 -0
- package/dist/define/contract-ref.d.ts +177 -0
- package/dist/define/contract-ref.js +67 -0
- package/dist/define/declare-contract.d.ts +167 -0
- package/dist/define/declare-contract.js +220 -0
- package/dist/define/declare-contract.test.d.ts +1 -0
- package/dist/define/declare-contract.test.js +573 -0
- package/dist/define/define-package.d.ts +203 -0
- package/dist/define/define-package.js +289 -0
- package/dist/define/define.test.d.ts +1 -0
- package/dist/define/define.test.js +1010 -0
- package/dist/define/empty-fields.d.ts +15 -0
- package/dist/define/empty-fields.js +134 -0
- package/dist/define/empty-fields.test.d.ts +1 -0
- package/dist/define/empty-fields.test.js +55 -0
- package/dist/define/events-observables.test.d.ts +1 -0
- package/dist/define/events-observables.test.js +1013 -0
- package/dist/define/events.d.ts +81 -0
- package/dist/define/events.js +81 -0
- package/dist/define/generated-field.d.ts +20 -0
- package/dist/define/generated-field.js +25 -0
- package/dist/define/index.d.ts +28 -0
- package/dist/define/index.js +62 -0
- package/dist/define/lazy-members.d.ts +8 -0
- package/dist/define/lazy-members.js +38 -0
- package/dist/define/manifest.d.ts +63 -0
- package/dist/define/manifest.js +653 -0
- package/dist/define/observables.d.ts +146 -0
- package/dist/define/observables.js +187 -0
- package/dist/define/serve-members.d.ts +21 -0
- package/dist/define/serve-members.js +152 -0
- package/dist/define/serve-members.test.d.ts +1 -0
- package/dist/define/serve-members.test.js +630 -0
- package/dist/define/serve.d.ts +22 -0
- package/dist/define/serve.js +58 -0
- package/dist/define/system-contracts.d.ts +41 -0
- package/dist/define/system-contracts.js +21 -0
- package/dist/define/table-handle.d.ts +13 -0
- package/dist/define/table-handle.js +36 -0
- package/dist/define/table.d.ts +128 -0
- package/dist/define/table.js +122 -0
- package/dist/define/tool.d.ts +129 -0
- package/dist/define/tool.js +91 -0
- package/dist/define/vars.d.ts +82 -0
- package/dist/define/vars.js +256 -0
- package/dist/define/vars.test.d.ts +1 -0
- package/dist/define/vars.test.js +39 -0
- package/dist/define/wire-value.d.ts +12 -0
- package/dist/define/wire-value.js +76 -0
- package/dist/define/worker-state.d.ts +15 -0
- package/dist/define/worker-state.js +45 -0
- package/dist/device/tx-encoding.test.js +10 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -1
- package/dist/invites/group-approval.js +4 -2
- package/dist/invites/group-approval.test.js +2 -0
- package/dist/observable.d.ts +42 -2
- package/dist/observable.js +25 -7
- package/dist/package-build/build-package.d.ts +60 -0
- package/dist/package-build/build-package.js +312 -0
- package/dist/package-build/build-package.test.d.ts +1 -0
- package/dist/package-build/build-package.test.js +204 -0
- package/dist/package-build/cli.d.ts +7 -0
- package/dist/package-build/cli.js +218 -0
- package/dist/package-build/contract-add.d.ts +35 -0
- package/dist/package-build/contract-add.js +97 -0
- package/dist/package-build/contract-add.test.d.ts +1 -0
- package/dist/package-build/contract-add.test.js +467 -0
- package/dist/package-build/contract-source.d.ts +59 -0
- package/dist/package-build/contract-source.js +471 -0
- package/dist/package-build/freeze-contracts.d.ts +56 -0
- package/dist/package-build/freeze-contracts.js +198 -0
- package/dist/package-build/freeze-contracts.test.d.ts +1 -0
- package/dist/package-build/freeze-contracts.test.js +355 -0
- package/dist/package-installer/package-creator.d.ts +4 -0
- package/dist/package-installer/package-creator.js +57 -16
- package/dist/package-installer/package-creator.test.js +86 -0
- package/dist/package-loader/contract-members.d.ts +53 -0
- package/dist/package-loader/contract-members.js +31 -0
- package/dist/package-loader/contract-package-loader.js +5 -1
- package/dist/package-loader/contract-package-loader.test.js +11 -0
- package/dist/package-loader/host-authority-loading.test.js +9 -5
- package/dist/package-loader/index.d.ts +1 -0
- package/dist/package-loader/index.js +1 -0
- package/dist/package-loader/isolated-artifact.d.ts +80 -17
- package/dist/package-loader/isolated-artifact.js +257 -51
- package/dist/package-loader/isolated-artifact.test.js +294 -21
- package/dist/package-loader/isolated-runtime.d.ts +22 -7
- package/dist/package-loader/isolated-table-gateway.js +30 -11
- package/dist/package-loader/isolated-table-gateway.test.js +243 -0
- package/dist/package-loader/package-loader.d.ts +8 -1
- package/dist/package-loader/package-loader.js +39 -20
- package/dist/package-loader/package-loader.test.js +47 -0
- package/dist/peer-id.d.ts +25 -0
- package/dist/peer-id.js +99 -0
- package/dist/serial-json.compat.test.d.ts +1 -0
- package/dist/serial-json.compat.test.js +369 -0
- package/dist/serial-json.d.ts +41 -5
- package/dist/serial-json.js +216 -157
- package/dist/serial-json.oracle.d.ts +21 -0
- package/dist/serial-json.oracle.js +222 -0
- package/dist/serial-json.test.js +315 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.js +8 -0
- package/dist/testing/mock-host-contract.d.ts +123 -0
- package/dist/testing/mock-host-contract.js +434 -0
- package/dist/testing/mock-host-contract.test.d.ts +1 -0
- package/dist/testing/mock-host-contract.test.js +782 -0
- package/dist/types/field-type.d.ts +35 -18
- package/dist/types/field-type.js +4 -1
- package/dist/types/zod-types.d.ts +9 -0
- package/dist/types/zod-types.js +8 -2
- package/dist/utils.d.ts +22 -25
- package/dist/utils.js +45 -92
- package/dist/utils.test.js +36 -0
- package/package.json +9 -2
- package/testing/package.json +4 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { PersistentVarScope } from "../data/persistent-var-identity";
|
|
2
|
+
import type { IsolatedContractTableOperation } from "../package-loader/isolated-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Ends a subscription or a watch. Unsubscribing from a consumed contract waits for the provider
|
|
5
|
+
* and rejects if it fails, so await it or catch the rejection.
|
|
6
|
+
*/
|
|
7
|
+
export type PackageUnsubscribe = () => unknown;
|
|
8
|
+
/** A consumed-event callback. `ctx` belongs to that delivery. */
|
|
9
|
+
export type PackageEventHandler = (payload: unknown, ctx: IPackageContext) => unknown;
|
|
10
|
+
/** Picks one provider of a consumed contract. */
|
|
11
|
+
export interface IPackageProviderOptions {
|
|
12
|
+
/** Package that serves the contract. Omit for the group's default provider. */
|
|
13
|
+
providerPackageId?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The host functions the Runtime gives one handler call (guest API 2 and 3).
|
|
17
|
+
*
|
|
18
|
+
* Every tool handler gets its own `ctx`, and host calls made through it act for that call's
|
|
19
|
+
* caller, even while other calls run. The table and contract handles in this module take a
|
|
20
|
+
* `ctx` and make these calls for you, and the package's variables (`deviceVar` and the rest),
|
|
21
|
+
* events, and observables need none; use these directly only for what those don't cover yet.
|
|
22
|
+
*/
|
|
23
|
+
export interface IPackageContext {
|
|
24
|
+
/** Run a tool on a contract this package consumes (or provides), as this call's caller. */
|
|
25
|
+
callConsumedTool(contractId: string, version: number, toolName: string, args?: unknown, options?: IPackageProviderOptions): Promise<unknown>;
|
|
26
|
+
/** Run a tool on another device. The remote device authorizes the call. */
|
|
27
|
+
callRemoteTool(contractId: string, version: number, deviceId: string, toolName: string, args?: unknown): Promise<unknown>;
|
|
28
|
+
/** Subscribe to an event on a consumed or provided contract. */
|
|
29
|
+
subscribeConsumedEvent(contractId: string, version: number, eventName: string, handler: PackageEventHandler): Promise<PackageUnsubscribe>;
|
|
30
|
+
/** Subscribe to `dataChanged` on a table another package exposes through a contract. */
|
|
31
|
+
subscribeConsumedTableEvent(contractId: string, version: number, tableName: string, eventName: "dataChanged", handler: PackageEventHandler): Promise<PackageUnsubscribe>;
|
|
32
|
+
/** Emit an event declared on one of this package's provided contracts. */
|
|
33
|
+
emitContractEvent(contractId: string, version: number, eventName: string, payload: unknown): Promise<unknown>;
|
|
34
|
+
/** Call a standard method on a table another package exposes through a contract. */
|
|
35
|
+
callConsumedTable(contractId: string, version: number, tableName: string, operation: IsolatedContractTableOperation, args?: unknown[]): Promise<unknown>;
|
|
36
|
+
/** Read one row from a consumed contract table. */
|
|
37
|
+
getConsumedTableRecord(contractId: string, version: number, tableName: string, recordId: string): Promise<unknown>;
|
|
38
|
+
/** Save one row to a consumed contract table. */
|
|
39
|
+
saveConsumedTableRecord(contractId: string, version: number, tableName: string, record: unknown): Promise<unknown>;
|
|
40
|
+
/** Read a consumed contract's observable. */
|
|
41
|
+
getConsumedObservable(contractId: string, version: number, observableName: string, options?: IPackageProviderOptions): Promise<unknown>;
|
|
42
|
+
/** Write a consumed contract's writable observable. */
|
|
43
|
+
setConsumedObservable(contractId: string, version: number, observableName: string, value: unknown, options?: IPackageProviderOptions): Promise<unknown>;
|
|
44
|
+
/** Call a standard method on one of this package's own tables, with its arguments. */
|
|
45
|
+
callOwnedTable(tableName: string, operation: IsolatedContractTableOperation, args?: unknown[]): Promise<unknown>;
|
|
46
|
+
/** Read one owned row by id. */
|
|
47
|
+
getOwnedTableRecord(tableName: string, recordId: string): Promise<unknown>;
|
|
48
|
+
/** Insert or replace one owned row. */
|
|
49
|
+
saveOwnedTableRecord(tableName: string, record: unknown): Promise<unknown>;
|
|
50
|
+
/** List owned rows (at most 500; use `callOwnedTable` to filter or page). */
|
|
51
|
+
listOwnedTableRecords(tableName: string): Promise<unknown>;
|
|
52
|
+
/** Delete one owned row by id. */
|
|
53
|
+
deleteOwnedTableRecord(tableName: string, recordId: string): Promise<unknown>;
|
|
54
|
+
/** Read a package-owned persistent variable. The host applies the package prefix. */
|
|
55
|
+
getOwnedPersistentVar(scope: PersistentVarScope, name: string): Promise<unknown>;
|
|
56
|
+
/** Replace a package-owned persistent variable. */
|
|
57
|
+
setOwnedPersistentVar(scope: PersistentVarScope, name: string, value: unknown): Promise<unknown>;
|
|
58
|
+
/**
|
|
59
|
+
* Call `handler` when a package-owned persistent variable changes, including through device
|
|
60
|
+
* sync, until the returned function is called or the worker stops. `handler` gets no value:
|
|
61
|
+
* read it again.
|
|
62
|
+
*/
|
|
63
|
+
watchOwnedPersistentVar(scope: PersistentVarScope, name: string, handler: () => unknown): Promise<PackageUnsubscribe>;
|
|
64
|
+
/**
|
|
65
|
+
* Set the value of an in-memory observable this package provides (one declared without a
|
|
66
|
+
* `persistentVar`). The host checks it against the observable's `valueType`, keeps it for
|
|
67
|
+
* readers, and pushes it to subscribers. Allowed at any time while the worker runs.
|
|
68
|
+
*/
|
|
69
|
+
publishOwnedObservable(contractId: string, version: number, observableName: string, value: unknown): Promise<unknown>;
|
|
70
|
+
/**
|
|
71
|
+
* Take guest API 2 consumers' writes to a writable in-memory observable this package
|
|
72
|
+
* provides. `handler` gets the value with no caller authority, stores and publishes it, and
|
|
73
|
+
* returns what it stored. A later call replaces the handler. Guest API 3 instead routes
|
|
74
|
+
* writes through the contract member handler under the caller's authority.
|
|
75
|
+
*/
|
|
76
|
+
onOwnedObservableSet(contractId: string, version: number, observableName: string, handler: (value: unknown) => unknown): void;
|
|
77
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { IPackageContext, IPackageProviderOptions } from "./context";
|
|
3
|
+
import type { IContractMembers } from "./define-package";
|
|
4
|
+
import type { IPackageEvent } from "./events";
|
|
5
|
+
import type { IPackageObservableDeclaration } from "./observables";
|
|
6
|
+
import type { IPackageTable, IPackageTableHandle } from "./table";
|
|
7
|
+
import type { IPackageToolDeclaration } from "./tool";
|
|
8
|
+
/**
|
|
9
|
+
* One tool on a contract client: a tool declaration, or a function signature. A declared tool's
|
|
10
|
+
* input may be left out when none of its fields is required.
|
|
11
|
+
*/
|
|
12
|
+
export type ContractClientTool<T> = T extends IPackageToolDeclaration<infer I, infer O, any, any> ? (...args: Partial<I> extends I ? [args?: I] : [args: I]) => Promise<O> : T extends (...args: infer A) => infer R ? (...args: A) => Promise<Awaited<R>> : never;
|
|
13
|
+
/** A contract's tools, typed from tool declarations or function signatures. */
|
|
14
|
+
export type ContractClientTools<T> = {
|
|
15
|
+
readonly [K in keyof T]: ContractClientTool<T[K]>;
|
|
16
|
+
};
|
|
17
|
+
/** Untyped tools: any name, any arguments. */
|
|
18
|
+
export type AnyContractTools = Record<string, (args?: unknown) => Promise<unknown>>;
|
|
19
|
+
/** A member's name: from its definition (`defineTool` and the rest) or from Peers IR. */
|
|
20
|
+
type MemberName<M> = M extends {
|
|
21
|
+
definition: {
|
|
22
|
+
name: infer N extends string;
|
|
23
|
+
};
|
|
24
|
+
} ? N : M extends {
|
|
25
|
+
name: infer N extends string;
|
|
26
|
+
} ? N : never;
|
|
27
|
+
/** Untyped rows, for a reference typed by its tools only. */
|
|
28
|
+
type AnyRow = Record<string, any>;
|
|
29
|
+
/** A contract's members, unwrapping a declaration's reference when necessary. */
|
|
30
|
+
export type ContractMembersOf<T> = T extends {
|
|
31
|
+
readonly kind: "contract-ref";
|
|
32
|
+
readonly declaration: infer D;
|
|
33
|
+
} ? D : T;
|
|
34
|
+
/** The contract's id, when `T` is a contract type; otherwise any string. */
|
|
35
|
+
export type ContractIdOf<T> = T extends {
|
|
36
|
+
readonly kind: "contract-ref";
|
|
37
|
+
readonly declaration: infer D;
|
|
38
|
+
} ? ContractIdOf<D> : T extends IContractMembers<infer Id, any, any, any, any, any> ? Id : string;
|
|
39
|
+
/** The contract's version, when `T` is a contract type; otherwise any number. */
|
|
40
|
+
export type ContractVersionOf<T> = T extends {
|
|
41
|
+
readonly kind: "contract-ref";
|
|
42
|
+
readonly declaration: infer D;
|
|
43
|
+
} ? ContractVersionOf<D> : T extends IContractMembers<any, infer V, any, any, any, any> ? V : number;
|
|
44
|
+
/**
|
|
45
|
+
* A contract's tools by name: from a contract type (`typeof News`, or a declaration's), or a
|
|
46
|
+
* map of tool declarations or function signatures.
|
|
47
|
+
*/
|
|
48
|
+
export type ContractToolsOf<T> = T extends {
|
|
49
|
+
readonly kind: "contract-ref";
|
|
50
|
+
readonly declaration: infer D;
|
|
51
|
+
} ? ContractToolsOf<D> : T extends IContractMembers<any, any, any, infer Tools, any, any> ? {
|
|
52
|
+
readonly [K in Tools[number] as MemberName<K>]: ContractClientTool<K>;
|
|
53
|
+
} : ContractClientTools<T>;
|
|
54
|
+
/** A contract's table rows by table name. Untyped for a reference typed by its tools only. */
|
|
55
|
+
export type ContractTableRowsOf<T> = T extends {
|
|
56
|
+
readonly kind: "contract-ref";
|
|
57
|
+
readonly declaration: infer D;
|
|
58
|
+
} ? ContractTableRowsOf<D> : T extends IContractMembers<any, any, infer Tables, any, any, any> ? {
|
|
59
|
+
[K in Tables[number] as MemberName<K>]: K extends IPackageTable<infer S, any> ? z.infer<S> : AnyRow;
|
|
60
|
+
} : Record<string, AnyRow>;
|
|
61
|
+
/**
|
|
62
|
+
* What a contract's tables take on `save`, by table name: the schema's input, so fields with
|
|
63
|
+
* defaults may be left out. The provider's table parses every save, consumers' included.
|
|
64
|
+
* Untyped for a reference typed by its tools only.
|
|
65
|
+
*/
|
|
66
|
+
export type ContractTableInputsOf<T> = T extends {
|
|
67
|
+
readonly kind: "contract-ref";
|
|
68
|
+
readonly declaration: infer D;
|
|
69
|
+
} ? ContractTableInputsOf<D> : T extends IContractMembers<any, any, infer Tables, any, any, any> ? {
|
|
70
|
+
[K in Tables[number] as MemberName<K>]: K extends IPackageTable<infer S, any, any> ? z.input<S> : AnyRow;
|
|
71
|
+
} : Record<string, AnyRow>;
|
|
72
|
+
/** A contract's event payloads by event name. IR events carry `unknown`. */
|
|
73
|
+
export type ContractEventPayloadsOf<T> = T extends {
|
|
74
|
+
readonly kind: "contract-ref";
|
|
75
|
+
readonly declaration: infer D;
|
|
76
|
+
} ? ContractEventPayloadsOf<D> : T extends IContractMembers<any, any, any, any, any, infer Events> ? {
|
|
77
|
+
[K in Events[number] as MemberName<K>]: K extends IPackageEvent<any, any, infer O> ? O : unknown;
|
|
78
|
+
} : Record<string, unknown>;
|
|
79
|
+
/** A contract's observable values by observable name. IR observables carry `unknown`. */
|
|
80
|
+
export type ContractObservableValuesOf<T> = T extends {
|
|
81
|
+
readonly kind: "contract-ref";
|
|
82
|
+
readonly declaration: infer D;
|
|
83
|
+
} ? ContractObservableValuesOf<D> : T extends IContractMembers<any, any, any, any, infer Observables, any> ? {
|
|
84
|
+
[K in Observables[number] as MemberName<K>]: K extends IPackageObservableDeclaration<infer V, any, any> ? V : unknown;
|
|
85
|
+
} : Record<string, unknown>;
|
|
86
|
+
/** A contract's observable writability, preserving literal declarations; unknown for tools-only refs. */
|
|
87
|
+
export type ContractObservableWritabilityOf<T> = T extends {
|
|
88
|
+
readonly kind: "contract-ref";
|
|
89
|
+
readonly declaration: infer D;
|
|
90
|
+
} ? ContractObservableWritabilityOf<D> : T extends IContractMembers<any, any, any, any, infer Observables, any> ? {
|
|
91
|
+
[K in Observables[number] as MemberName<K>]: K extends IPackageObservableDeclaration<any, any, infer W> ? W : K extends {
|
|
92
|
+
writable?: infer W;
|
|
93
|
+
} ? W : false;
|
|
94
|
+
} : Record<string, boolean>;
|
|
95
|
+
/** A table's handle for a row type. */
|
|
96
|
+
type RowHandle<R, I> = R extends AnyRow ? IPackageTableHandle<R, I> : never;
|
|
97
|
+
/** One observable on a contract client: read it, and write it when the contract allows. */
|
|
98
|
+
export type ContractClientObservable<V, W> = true extends W ? {
|
|
99
|
+
/** The provider's current value. An empty whole value crosses the bridge as `null`. */
|
|
100
|
+
get(): Promise<V | null | undefined>;
|
|
101
|
+
/**
|
|
102
|
+
* Write as this call's caller. Whole values can't carry `undefined`; to clear one, its
|
|
103
|
+
* schema must allow `null`. Returns the stored value; an empty whole value is `null`.
|
|
104
|
+
*/
|
|
105
|
+
set(value: Exclude<V, undefined>): Promise<V | null>;
|
|
106
|
+
} : {
|
|
107
|
+
/** The provider's current value. An empty whole value crosses the bridge as `null`. */
|
|
108
|
+
get(): Promise<V | null | undefined>;
|
|
109
|
+
};
|
|
110
|
+
/** Options for a contract client: which provider its calls reach. */
|
|
111
|
+
export interface IContractClientOptions extends IPackageProviderOptions {
|
|
112
|
+
}
|
|
113
|
+
/** A contract's members, bound to one handler call. */
|
|
114
|
+
export interface IContractClient<T> {
|
|
115
|
+
/** Tools, run as this call's caller: `Http(ctx).tools.request({ ... })`. */
|
|
116
|
+
readonly tools: ContractToolsOf<T>;
|
|
117
|
+
/**
|
|
118
|
+
* Tables the contract exposes, used as this call's caller: `News(ctx).tables.Headlines.list()`.
|
|
119
|
+
* Every call goes through the providing package's table (its class, if it has one), so
|
|
120
|
+
* `save` takes the schema's input and the provider's defaults and transforms apply.
|
|
121
|
+
*/
|
|
122
|
+
readonly tables: {
|
|
123
|
+
readonly [K in keyof ContractTableRowsOf<T>]: RowHandle<ContractTableRowsOf<T>[K], K extends keyof ContractTableInputsOf<T> ? ContractTableInputsOf<T>[K] : never>;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Observables the contract exposes, read (and written, when writable) as this call's
|
|
127
|
+
* caller: `await Weather(ctx).observables.forecast.get()`.
|
|
128
|
+
*/
|
|
129
|
+
readonly observables: {
|
|
130
|
+
readonly [K in keyof ContractObservableValuesOf<T>]: ContractClientObservable<ContractObservableValuesOf<T>[K], K extends keyof ContractObservableWritabilityOf<T> ? ContractObservableWritabilityOf<T>[K] : boolean>;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* A reference to a contract: one this package consumes, one it provides when it should call
|
|
135
|
+
* itself through the Runtime so the caller's access is checked again, or (with `hostContract`)
|
|
136
|
+
* the one a UI calls.
|
|
137
|
+
*/
|
|
138
|
+
export interface IContractRef<T = AnyContractTools> {
|
|
139
|
+
/**
|
|
140
|
+
* The contract's members for one handler call. Pass `{ providerPackageId }` to reach one
|
|
141
|
+
* provider rather than the group's default (to call every provider of a contract, say).
|
|
142
|
+
* Table calls can't pick a provider yet.
|
|
143
|
+
*/
|
|
144
|
+
(ctx: IPackageContext, options?: IContractClientOptions): IContractClient<T>;
|
|
145
|
+
/** Marks a contract reference. */
|
|
146
|
+
readonly kind: "contract-ref";
|
|
147
|
+
/** The contract's id. */
|
|
148
|
+
readonly contractId: string;
|
|
149
|
+
/** The contract's version. */
|
|
150
|
+
readonly version: number;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Reference a contract by id and version. Declare it with `pkg.consumes(ref)` when another
|
|
154
|
+
* package provides it, then call it from a handler: `await Http(ctx).tools.request({ ... })`.
|
|
155
|
+
*
|
|
156
|
+
* Type it with the contract (`contractRef<typeof News>(contractId, version)`, where `News` is
|
|
157
|
+
* from `defineContract` or `declareContract`; import it with `import type` so no handler code comes along), which
|
|
158
|
+
* types every member and checks the id and version against it. Or type just the tools, with an
|
|
159
|
+
* interface of function signatures (`IHttpConsumer["tools"]`) or tool declarations
|
|
160
|
+
* (`{ refresh: typeof refreshTool }`). A member named `then`, `toJSON`, `toString`, `valueOf`,
|
|
161
|
+
* or `constructor` needs `ctx.callConsumedTool` (or `callConsumedTable`) instead.
|
|
162
|
+
*
|
|
163
|
+
* @param contractId - The contract's id.
|
|
164
|
+
* @param version - The contract's version.
|
|
165
|
+
*/
|
|
166
|
+
export declare function contractRef<T = AnyContractTools>(contractId: ContractIdOf<T>, version: ContractVersionOf<T>): IContractRef<T>;
|
|
167
|
+
/**
|
|
168
|
+
* Make a contract reference with extra properties (a declaration's members, say). Not part of
|
|
169
|
+
* the public API: use {@link contractRef} or `declareContract`.
|
|
170
|
+
*
|
|
171
|
+
* @internal
|
|
172
|
+
* @param contractId - The contract's id.
|
|
173
|
+
* @param version - The contract's version.
|
|
174
|
+
* @param extra - Properties to add to the reference.
|
|
175
|
+
*/
|
|
176
|
+
export declare function createContractRef<T, X extends object = object>(contractId: string, version: number, extra: X): IContractRef<T> & X;
|
|
177
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.contractRef = contractRef;
|
|
4
|
+
exports.createContractRef = createContractRef;
|
|
5
|
+
const lazy_members_1 = require("./lazy-members");
|
|
6
|
+
const table_handle_1 = require("./table-handle");
|
|
7
|
+
const wire_value_1 = require("./wire-value");
|
|
8
|
+
/**
|
|
9
|
+
* Reference a contract by id and version. Declare it with `pkg.consumes(ref)` when another
|
|
10
|
+
* package provides it, then call it from a handler: `await Http(ctx).tools.request({ ... })`.
|
|
11
|
+
*
|
|
12
|
+
* Type it with the contract (`contractRef<typeof News>(contractId, version)`, where `News` is
|
|
13
|
+
* from `defineContract` or `declareContract`; import it with `import type` so no handler code comes along), which
|
|
14
|
+
* types every member and checks the id and version against it. Or type just the tools, with an
|
|
15
|
+
* interface of function signatures (`IHttpConsumer["tools"]`) or tool declarations
|
|
16
|
+
* (`{ refresh: typeof refreshTool }`). A member named `then`, `toJSON`, `toString`, `valueOf`,
|
|
17
|
+
* or `constructor` needs `ctx.callConsumedTool` (or `callConsumedTable`) instead.
|
|
18
|
+
*
|
|
19
|
+
* @param contractId - The contract's id.
|
|
20
|
+
* @param version - The contract's version.
|
|
21
|
+
*/
|
|
22
|
+
function contractRef(contractId, version) {
|
|
23
|
+
return createContractRef(contractId, version, {});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Make a contract reference with extra properties (a declaration's members, say). Not part of
|
|
27
|
+
* the public API: use {@link contractRef} or `declareContract`.
|
|
28
|
+
*
|
|
29
|
+
* @internal
|
|
30
|
+
* @param contractId - The contract's id.
|
|
31
|
+
* @param version - The contract's version.
|
|
32
|
+
* @param extra - Properties to add to the reference.
|
|
33
|
+
*/
|
|
34
|
+
function createContractRef(contractId, version, extra) {
|
|
35
|
+
if (typeof contractId !== "string" || contractId.trim() === "") {
|
|
36
|
+
throw new Error("contractRef requires a contractId");
|
|
37
|
+
}
|
|
38
|
+
if (!Number.isInteger(version) || version < 1) {
|
|
39
|
+
throw new Error(`contractRef version must be a positive integer, got ${version}`);
|
|
40
|
+
}
|
|
41
|
+
const client = (ctx, options = {}) => {
|
|
42
|
+
const { providerPackageId } = options ?? {};
|
|
43
|
+
if (providerPackageId !== undefined &&
|
|
44
|
+
(typeof providerPackageId !== "string" || providerPackageId.trim() === "")) {
|
|
45
|
+
throw new Error("providerPackageId must be a non-empty package id");
|
|
46
|
+
}
|
|
47
|
+
// Options go only when a provider is chosen: the bridge would carry `undefined` as null.
|
|
48
|
+
const provider = providerPackageId
|
|
49
|
+
? [{ providerPackageId }]
|
|
50
|
+
: [];
|
|
51
|
+
const label = `${contractId}@${version}`;
|
|
52
|
+
// Arguments go as the bridge carries them: a field left undefined is omitted.
|
|
53
|
+
const tools = (0, lazy_members_1.lazyMembers)((toolName) => (args) => ctx.callConsumedTool(contractId, version, toolName, args === undefined ? args : (0, wire_value_1.snapshotPackageValue)(args), ...provider));
|
|
54
|
+
const tables = (0, lazy_members_1.lazyMembers)((tableName) => (0, table_handle_1.packageTableMethods)((operation, args) => {
|
|
55
|
+
if (providerPackageId) {
|
|
56
|
+
throw new Error(`Table calls on ${label} can't pick a provider; call ${tableName} without providerPackageId`);
|
|
57
|
+
}
|
|
58
|
+
return ctx.callConsumedTable(contractId, version, tableName, operation, args);
|
|
59
|
+
}));
|
|
60
|
+
const observables = (0, lazy_members_1.lazyMembers)((observableName) => Object.freeze({
|
|
61
|
+
get: () => ctx.getConsumedObservable(contractId, version, observableName, ...provider),
|
|
62
|
+
set: (value) => ctx.setConsumedObservable(contractId, version, observableName, value === undefined ? value : (0, wire_value_1.snapshotPackageValue)(value), ...provider),
|
|
63
|
+
}));
|
|
64
|
+
return Object.freeze({ tools, tables, observables });
|
|
65
|
+
};
|
|
66
|
+
return Object.freeze(Object.assign(client, { ...extra, kind: "contract-ref", contractId, version }));
|
|
67
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { InvariantObservable, Observable } from "../observable";
|
|
2
|
+
import type { IPackageContext } from "./context";
|
|
3
|
+
import { type ContractMembersOf, type IContractRef } from "./contract-ref";
|
|
4
|
+
import { type AlsoImplementsTarget, type IContractMembers, type IPackageContract, type PackageContractEvent, type PackageContractObservableDeclaration } from "./define-package";
|
|
5
|
+
import { type IPackageObservable, type IPackageObservableDeclaration } from "./observables";
|
|
6
|
+
import type { IPackageTable } from "./table";
|
|
7
|
+
import { type IPackageTool, type IPackageToolDeclaration } from "./tool";
|
|
8
|
+
/** What {@link declareContract} takes. Omitted member lists are empty. */
|
|
9
|
+
export interface IContractDeclarationDefinition<Id extends string = string, V extends number = number, Tables extends readonly IPackageTable<any>[] = readonly IPackageTable<any>[], Tools extends readonly IPackageToolDeclaration<any, any>[] = readonly IPackageToolDeclaration<any, any>[], Observables extends readonly IPackageObservableDeclaration<any>[] = readonly IPackageObservableDeclaration<any>[], Events extends readonly PackageContractEvent[] = readonly PackageContractEvent[]> {
|
|
10
|
+
/** The contract's id, from `newid()`. */
|
|
11
|
+
contractId: Id;
|
|
12
|
+
/** The contract's version, a positive integer. */
|
|
13
|
+
version: V;
|
|
14
|
+
/** Human-readable name. */
|
|
15
|
+
name: string;
|
|
16
|
+
/** What the contract is for. */
|
|
17
|
+
description?: string;
|
|
18
|
+
/** Tables the contract exposes, from `defineTable` (without a `tableClass`). */
|
|
19
|
+
tables?: Tables;
|
|
20
|
+
/** Tools the contract provides, from `declareTool`. */
|
|
21
|
+
tools?: Tools;
|
|
22
|
+
/** Observables the contract exposes, from `declareObservable`. */
|
|
23
|
+
observables?: Observables;
|
|
24
|
+
/** Events the contract may emit, from `defineEvent`. */
|
|
25
|
+
events?: Events;
|
|
26
|
+
}
|
|
27
|
+
/** A declaration's members: a contract's shape with no handlers or sources. */
|
|
28
|
+
export interface IContractDeclarationMembers<Id extends string = string, V extends number = number, Tables extends readonly IPackageTable<any>[] = readonly IPackageTable<any>[], Tools extends readonly IPackageToolDeclaration<any, any>[] = readonly IPackageToolDeclaration<any, any>[], Observables extends readonly PackageContractObservableDeclaration[] = readonly PackageContractObservableDeclaration[], Events extends readonly PackageContractEvent[] = readonly PackageContractEvent[]> extends IContractMembers<Id, V, Tables, Tools, Observables, Events> {
|
|
29
|
+
/** Marks a declaration's members. */
|
|
30
|
+
readonly kind: "contract-declaration";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A contract declared with {@link declareContract}: its shape, with no handlers, and a
|
|
34
|
+
* reference to it. Consumers use it as the reference (`pkg.consumes(VoiceSkill)`,
|
|
35
|
+
* `VoiceSkill(ctx).tools.invoke(...)`, `hostContract(VoiceSkill)`); a provider implements it
|
|
36
|
+
* with {@link implementContract}. It holds no package code, so it's safe to import or copy.
|
|
37
|
+
*/
|
|
38
|
+
export type IContractDeclaration<Id extends string = string, V extends number = number, Tables extends readonly IPackageTable<any>[] = readonly IPackageTable<any>[], Tools extends readonly IPackageToolDeclaration<any, any>[] = readonly IPackageToolDeclaration<any, any>[], Observables extends readonly PackageContractObservableDeclaration[] = readonly PackageContractObservableDeclaration[], Events extends readonly PackageContractEvent[] = readonly PackageContractEvent[]> = IContractRef<IContractDeclarationMembers<Id, V, Tables, Tools, Observables, Events>> & {
|
|
39
|
+
/** The declared contract's id, preserving its literal type. */
|
|
40
|
+
readonly contractId: Id;
|
|
41
|
+
/** The declared contract's version, preserving its literal type. */
|
|
42
|
+
readonly version: V;
|
|
43
|
+
/** The contract's members, as declared. */
|
|
44
|
+
readonly declaration: IContractDeclarationMembers<Id, V, Tables, Tools, Observables, Events>;
|
|
45
|
+
};
|
|
46
|
+
/** A member's name, from its definition. */
|
|
47
|
+
type MemberName<M> = M extends {
|
|
48
|
+
definition: {
|
|
49
|
+
name: infer N extends string;
|
|
50
|
+
};
|
|
51
|
+
} ? N : never;
|
|
52
|
+
/** A tool's handler: the parsed input in, the output (or a promise of it) back. */
|
|
53
|
+
export type ContractToolHandler<T> = T extends IPackageToolDeclaration<any, infer O, any, infer P> ? (args: P, ctx: IPackageContext) => O | Promise<O> : never;
|
|
54
|
+
/**
|
|
55
|
+
* How a provider implements one declared tool: its handler, or the handler with this
|
|
56
|
+
* provider's deadline (`timeoutMs`, at most 120 000).
|
|
57
|
+
*/
|
|
58
|
+
export type ContractToolImplementation<T> = ContractToolHandler<T> | {
|
|
59
|
+
handler: ContractToolHandler<T>;
|
|
60
|
+
timeoutMs?: number;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* What holds a declared observable's value: a package variable (`deviceVar`, `groupVar`, …)
|
|
64
|
+
* or an `observable()`, whose value fits the declared schema. A writable source must also
|
|
65
|
+
* accept every value consumers may write; a read-only source may have a narrower value type.
|
|
66
|
+
*/
|
|
67
|
+
export type ContractObservableSource<T> = T extends IPackageObservableDeclaration<infer V, any, infer W> ? true extends W ? InvariantObservable<V> : Observable<any> & (() => V) : never;
|
|
68
|
+
type ImplementationTools<Tools extends readonly unknown[]> = [Tools[number]] extends [never] ? {
|
|
69
|
+
tools?: Record<string, never>;
|
|
70
|
+
} : {
|
|
71
|
+
tools: {
|
|
72
|
+
[K in Tools[number] as MemberName<K>]: ContractToolImplementation<K>;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
type ImplementationObservables<Observables extends readonly unknown[]> = [
|
|
76
|
+
Observables[number]
|
|
77
|
+
] extends [never] ? {
|
|
78
|
+
observables?: Record<string, never>;
|
|
79
|
+
} : {
|
|
80
|
+
observables: {
|
|
81
|
+
[K in Observables[number] as MemberName<K>]: ContractObservableSource<K>;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* A contract a provider can expose through a hub's contract: one it also provides (from
|
|
86
|
+
* `defineContract` or `implementContract`), or that contract's reference.
|
|
87
|
+
*/
|
|
88
|
+
export interface IExposableContract {
|
|
89
|
+
/** The contract's id. */
|
|
90
|
+
readonly contractId: string;
|
|
91
|
+
/** The contract's version. */
|
|
92
|
+
readonly version: number;
|
|
93
|
+
}
|
|
94
|
+
/** The part of an implementation any declaration accepts: what it exposes and also implements. */
|
|
95
|
+
interface ImplementationExposes {
|
|
96
|
+
/**
|
|
97
|
+
* Other contracts this package provides whose tools the hub contract's consumers may call on
|
|
98
|
+
* this provider, without consuming them. For a hub's contract, such as Voice Skill.
|
|
99
|
+
*/
|
|
100
|
+
exposes?: readonly IExposableContract[];
|
|
101
|
+
/**
|
|
102
|
+
* Older versions of the declared contract (by number) or other contracts (by declaration)
|
|
103
|
+
* whose consumers this implementation also serves; see {@link AlsoImplementsTarget}.
|
|
104
|
+
*/
|
|
105
|
+
alsoImplements?: readonly AlsoImplementsTarget[];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* What {@link implementContract} takes for a declaration: a handler for every declared tool and
|
|
109
|
+
* a source for every declared observable, by name, and optionally the contracts it exposes and
|
|
110
|
+
* the versions it also implements.
|
|
111
|
+
* Tables and events need nothing.
|
|
112
|
+
*/
|
|
113
|
+
export type ContractImplementation<D> = ContractMembersOf<D> extends IContractMembers<any, any, any, infer Tools, infer Observables, any> ? ImplementationTools<Tools> & ImplementationObservables<Observables> & ImplementationExposes : never;
|
|
114
|
+
/** Declared tools, each with its handler. */
|
|
115
|
+
type ImplementedTools<Tools extends readonly unknown[]> = {
|
|
116
|
+
[K in keyof Tools]: Tools[K] extends IPackageToolDeclaration<infer I, infer O, infer N, infer P> ? IPackageTool<I, O, N, P> : never;
|
|
117
|
+
};
|
|
118
|
+
/** Declared observables, each with its source. */
|
|
119
|
+
type ImplementedObservables<Observables extends readonly unknown[]> = {
|
|
120
|
+
[K in keyof Observables]: Observables[K] extends IPackageObservableDeclaration<infer V, infer N, infer W> ? IPackageObservable<V, N, W> : never;
|
|
121
|
+
};
|
|
122
|
+
/** The contract {@link implementContract} makes from a declaration. */
|
|
123
|
+
export type ImplementedContract<D> = ContractMembersOf<D> extends IContractMembers<infer Id, infer V, infer Tables, infer Tools, infer Observables, infer Events> ? IPackageContract<Id, V, Tables, ImplementedTools<Tools> extends readonly IPackageTool<any, any>[] ? ImplementedTools<Tools> : never, ImplementedObservables<Observables> extends readonly IPackageObservable<any>[] ? ImplementedObservables<Observables> : never, Events> : never;
|
|
124
|
+
/**
|
|
125
|
+
* Declare a contract's shape without providing it: its id, version, name, and the tables,
|
|
126
|
+
* tools, observables, and events it exposes, with no handlers. Use it for a contract several
|
|
127
|
+
* packages implement (Voice Skill, which every skill provides). Each provider implements it
|
|
128
|
+
* with {@link implementContract}; consumers use the declaration itself as the reference.
|
|
129
|
+
*
|
|
130
|
+
* Tools come from `declareTool` and observables from `declareObservable`: `defineTool` and
|
|
131
|
+
* `defineObservable` without the handler or source. Events (`defineEvent`) and tables
|
|
132
|
+
* (`defineTable`) are listed as they are; a table can't have a `tableClass` here.
|
|
133
|
+
*
|
|
134
|
+
* @param definition - Id, version, name, description, and members.
|
|
135
|
+
*/
|
|
136
|
+
export declare function declareContract<Id extends string, V extends number, Tables extends readonly IPackageTable<any>[] = [], Tools extends readonly IPackageToolDeclaration<any, any>[] = [], Observables extends readonly IPackageObservableDeclaration<any>[] = [], Events extends readonly PackageContractEvent[] = []>(definition: IContractDeclarationDefinition<Id, V, Tables, Tools, Observables, Events>): IContractDeclaration<Id, V, Tables, Tools, Observables, Events>;
|
|
137
|
+
/**
|
|
138
|
+
* Implement a declared contract: a handler for each of its tools and a source for each of its
|
|
139
|
+
* observables, by name. Returns the contract for `pkg.provides`, as {@link defineContract}
|
|
140
|
+
* would; its manifest shape comes from the declaration, so it can't drift from it.
|
|
141
|
+
*
|
|
142
|
+
* ```ts
|
|
143
|
+
* export const TimersVoiceSkill = implementContract(VoiceSkill, {
|
|
144
|
+
* tools: { invoke: async ({ tool, args }, ctx) => ({ speech: "Done." }) },
|
|
145
|
+
* observables: { skill: deviceVar<VoiceSkillCatalog>("skill", { defaultValue: catalog }) },
|
|
146
|
+
* });
|
|
147
|
+
* pkg.provides(TimersVoiceSkill);
|
|
148
|
+
* ```
|
|
149
|
+
*
|
|
150
|
+
* A tool's entry may also set this provider's deadline: `{ handler, timeoutMs }`. The
|
|
151
|
+
* declaration's events are emitted with their own `emit`.
|
|
152
|
+
*
|
|
153
|
+
* For a hub's contract, `exposes` lists other contracts the package provides whose tools the
|
|
154
|
+
* hub contract's consumers may call on this provider: `exposes: [News]`. Each must also be in
|
|
155
|
+
* `pkg.provides`. The hub reaches only their tools, each at its own access level, as the user.
|
|
156
|
+
*
|
|
157
|
+
* @param declaration - From {@link declareContract}.
|
|
158
|
+
* @param implementation - Handlers and sources, by member name, and the contracts it exposes.
|
|
159
|
+
*/
|
|
160
|
+
export declare function implementContract<D extends IContractDeclaration<any, any, any, any, any, any>>(declaration: D, implementation: ContractImplementation<NoInfer<D>>): ImplementedContract<D>;
|
|
161
|
+
/**
|
|
162
|
+
* True for a declaration from {@link declareContract}.
|
|
163
|
+
*
|
|
164
|
+
* @param value - A contract reference, contract, or anything else.
|
|
165
|
+
*/
|
|
166
|
+
export declare function isContractDeclaration(value: unknown): value is IContractDeclaration;
|
|
167
|
+
export {};
|