@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
/** The value types a Peers IR field can have. */
|
|
2
3
|
export declare enum FieldType {
|
|
3
4
|
id = "id",
|
|
4
5
|
string = "string",
|
|
@@ -9,6 +10,37 @@ export declare enum FieldType {
|
|
|
9
10
|
blob = "blob",
|
|
10
11
|
any = "any"
|
|
11
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* One field in Peers IR: a table column, a tool input or output, an event payload field, or
|
|
15
|
+
* (without `name`) an observable's value.
|
|
16
|
+
*
|
|
17
|
+
* The Runtime checks `type`, `optional`, and `isArray` on table rows, tool arguments, and event
|
|
18
|
+
* payloads (an observable's value, against its `valueType`). `fields` and `enumValues` describe
|
|
19
|
+
* the value further, for the models that call tools and for generated types; the package's own
|
|
20
|
+
* schema enforces them.
|
|
21
|
+
*
|
|
22
|
+
* A field has one empty value: missing. The Runtime removes an optional field that is `null` or
|
|
23
|
+
* `undefined` (a field with a default gets the default instead), stores it as SQL NULL, and
|
|
24
|
+
* reads it back unset.
|
|
25
|
+
*/
|
|
26
|
+
export interface IField {
|
|
27
|
+
name: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
type: FieldType;
|
|
30
|
+
defaultValue?: any;
|
|
31
|
+
/** The value may be empty: missing, `undefined`, or `null`, all read back as missing. */
|
|
32
|
+
optional?: boolean;
|
|
33
|
+
/** The value is an array of `type`; `fields`, `enumValues` describe each element. */
|
|
34
|
+
isArray?: boolean;
|
|
35
|
+
subType?: string;
|
|
36
|
+
/** An object's own fields. Absent: any object. Only on `object`; other keys may be present. */
|
|
37
|
+
fields?: IField[];
|
|
38
|
+
/** The only values allowed: strings, numbers, or booleans matching `type` (any of them on `any`). */
|
|
39
|
+
enumValues?: (string | number | boolean)[];
|
|
40
|
+
}
|
|
41
|
+
/** A field's shape without its name: an observable's value. */
|
|
42
|
+
export type IFieldShape = Omit<IField, "name">;
|
|
43
|
+
/** Zod schema for {@link IField}, used where rows store field lists. */
|
|
12
44
|
export declare const fieldSchema: z.ZodObject<{
|
|
13
45
|
name: z.ZodString;
|
|
14
46
|
description: z.ZodOptional<z.ZodString>;
|
|
@@ -17,21 +49,6 @@ export declare const fieldSchema: z.ZodObject<{
|
|
|
17
49
|
optional: z.ZodOptional<z.ZodBoolean>;
|
|
18
50
|
isArray: z.ZodOptional<z.ZodBoolean>;
|
|
19
51
|
subType: z.ZodOptional<z.ZodString>;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
optional?: boolean | undefined;
|
|
24
|
-
description?: string | undefined;
|
|
25
|
-
defaultValue?: any;
|
|
26
|
-
isArray?: boolean | undefined;
|
|
27
|
-
subType?: string | undefined;
|
|
28
|
-
}, {
|
|
29
|
-
name: string;
|
|
30
|
-
type: FieldType;
|
|
31
|
-
optional?: boolean | undefined;
|
|
32
|
-
description?: string | undefined;
|
|
33
|
-
defaultValue?: any;
|
|
34
|
-
isArray?: boolean | undefined;
|
|
35
|
-
subType?: string | undefined;
|
|
36
|
-
}>;
|
|
37
|
-
export type IField = z.infer<typeof fieldSchema>;
|
|
52
|
+
fields: z.ZodOptional<z.ZodLazy<z.ZodArray<z.ZodType<IField>>>>;
|
|
53
|
+
enumValues: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
54
|
+
}, "strip", z.ZodTypeAny, IField, IField>;
|
package/dist/types/field-type.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.fieldSchema = exports.FieldType = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
5
|
//export type FieldType = 'id' | 'string' | 'number' | 'boolean' | 'Date' | 'object' | 'blob' | 'any';
|
|
6
|
+
/** The value types a Peers IR field can have. */
|
|
6
7
|
var FieldType;
|
|
7
8
|
(function (FieldType) {
|
|
8
9
|
FieldType["id"] = "id";
|
|
@@ -14,6 +15,7 @@ var FieldType;
|
|
|
14
15
|
FieldType["blob"] = "blob";
|
|
15
16
|
FieldType["any"] = "any";
|
|
16
17
|
})(FieldType || (exports.FieldType = FieldType = {}));
|
|
18
|
+
/** Zod schema for {@link IField}, used where rows store field lists. */
|
|
17
19
|
exports.fieldSchema = zod_1.z.object({
|
|
18
20
|
name: zod_1.z.string(),
|
|
19
21
|
description: zod_1.z.string().optional(),
|
|
@@ -21,6 +23,7 @@ exports.fieldSchema = zod_1.z.object({
|
|
|
21
23
|
defaultValue: zod_1.z.any().optional(),
|
|
22
24
|
optional: zod_1.z.boolean().optional(),
|
|
23
25
|
isArray: zod_1.z.boolean().optional(),
|
|
24
|
-
// enumValues: z.array(z.string()).optional(),
|
|
25
26
|
subType: zod_1.z.string().optional(),
|
|
27
|
+
fields: zod_1.z.lazy(() => zod_1.z.array(exports.fieldSchema)).optional(),
|
|
28
|
+
enumValues: zod_1.z.array(zod_1.z.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.boolean()])).optional(),
|
|
26
29
|
});
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export { z } from "zod";
|
|
3
|
+
/** Any plain object, with every key kept. */
|
|
3
4
|
export declare const zodAnyObject: z.ZodObject<{}, "strip", z.ZodAny, z.objectOutputType<{}, z.ZodAny, "strip">, z.objectInputType<{}, z.ZodAny, "strip">>;
|
|
5
|
+
/** Any plain object or any array. */
|
|
4
6
|
export declare const zodAnyObjectOrArray: z.ZodUnion<[z.ZodObject<{}, "strip", z.ZodAny, z.objectOutputType<{}, z.ZodAny, "strip">, z.objectInputType<{}, z.ZodAny, "strip">>, z.ZodArray<z.ZodAny, "many">]>;
|
|
7
|
+
/**
|
|
8
|
+
* A 25-character peer id from `newid()`. Marked as {@link FieldType.id}, so `schemaToFields`
|
|
9
|
+
* records the field as an id.
|
|
10
|
+
*/
|
|
5
11
|
export declare const zodPeerId: z.ZodEffects<z.ZodString, string, string>;
|
|
12
|
+
/** Any zod object schema. */
|
|
6
13
|
export type ZodObjectType = z.ZodObject<any>;
|
|
14
|
+
/** Any zod array schema. */
|
|
7
15
|
export type ZodArrayType = z.ZodArray<any>;
|
|
16
|
+
/** Any zod object or array schema. */
|
|
8
17
|
export type ZodAnyObjectOrArrayType = ZodArrayType | ZodObjectType;
|
package/dist/types/zod-types.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.zodPeerId = exports.zodAnyObjectOrArray = exports.zodAnyObject = exports.z = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
-
const
|
|
5
|
+
const peer_id_1 = require("../peer-id");
|
|
6
6
|
const field_type_1 = require("./field-type");
|
|
7
7
|
// Re-export zod so consumers import it via peers-sdk. This guarantees every
|
|
8
8
|
// schema in the graph is built with the same zod instance that peers-sdk's
|
|
@@ -10,10 +10,16 @@ const field_type_1 = require("./field-type");
|
|
|
10
10
|
// matters because those helpers reach into zod internals (`_def`, shape).
|
|
11
11
|
var zod_2 = require("zod");
|
|
12
12
|
Object.defineProperty(exports, "z", { enumerable: true, get: function () { return zod_2.z; } });
|
|
13
|
+
/** Any plain object, with every key kept. */
|
|
13
14
|
exports.zodAnyObject = zod_1.z.object({}).catchall(zod_1.z.any());
|
|
15
|
+
/** Any plain object or any array. */
|
|
14
16
|
exports.zodAnyObjectOrArray = zod_1.z.union([exports.zodAnyObject, zod_1.z.array(zod_1.z.any())]);
|
|
17
|
+
/**
|
|
18
|
+
* A 25-character peer id from `newid()`. Marked as {@link FieldType.id}, so `schemaToFields`
|
|
19
|
+
* records the field as an id.
|
|
20
|
+
*/
|
|
15
21
|
exports.zodPeerId = zod_1.z.string().refine((value) => {
|
|
16
|
-
return (0,
|
|
22
|
+
return (0, peer_id_1.isid)(value);
|
|
17
23
|
}, {
|
|
18
24
|
message: "Invalid peer id, it must be a 25 character alphanumeric string",
|
|
19
25
|
});
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,30 +1,6 @@
|
|
|
1
1
|
import type { ITableMetaData } from "./data/orm";
|
|
2
2
|
import { type IDeviceConnection } from "./types/peer-device";
|
|
3
|
-
|
|
4
|
-
export declare function cryptoRandomString(length: number): string;
|
|
5
|
-
/**
|
|
6
|
-
* Creates a new 25-character peer id: base-36 time prefix (10 chars) + random suffix (15 chars).
|
|
7
|
-
* Sort order correlates with creation time. Use {@link isid} to validate user-provided values.
|
|
8
|
-
*/
|
|
9
|
-
export declare function newid(): string;
|
|
10
|
-
/** Lexicographic lower bound for valid peer ids (epoch). */
|
|
11
|
-
export declare const MIN_ID: string;
|
|
12
|
-
/** Lexicographic upper bound for valid peer ids (far future). */
|
|
13
|
-
export declare const MAX_ID: string;
|
|
14
|
-
/**
|
|
15
|
-
* Returns whether `id` is a 25-char base-36 string whose embedded time is not in the future (5 min skew allowed).
|
|
16
|
-
* @param id Candidate id (coerced to string)
|
|
17
|
-
*/
|
|
18
|
-
export declare function isid(id: any): boolean;
|
|
19
|
-
/** Returns the `Date.now()`-compatible millisecond time encoded in the first 10 base-36 characters of a peer id. */
|
|
20
|
-
export declare function idTime(id: string): number;
|
|
21
|
-
/** Interprets a peer id’s time prefix as a `Date` (see {@link idTime}). */
|
|
22
|
-
export declare function idDate(id: string): Date;
|
|
23
|
-
/**
|
|
24
|
-
* Returns the 15-character random suffix of a peer id as two base-36 integers so large values stay exact in JS.
|
|
25
|
-
* @param id A 25-character peer id from {@link newid}
|
|
26
|
-
*/
|
|
27
|
-
export declare function idRandNums(id: string): [number, number];
|
|
3
|
+
export { cryptoRandomString, idDate, idRandNums, idTime, isid, MAX_ID, MIN_ID, newid, } from "./peer-id";
|
|
28
4
|
/** Fast non-cryptographic 32-bit string hash (sign-insensitive) for display keys and bucketing. */
|
|
29
5
|
export declare function simpleHash(str: string): number;
|
|
30
6
|
/** Like TypeScript’s `Partial` for specific keys: required keys in `K` become optional. */
|
|
@@ -61,6 +37,23 @@ export declare function deterministicTableId(identity: string): string;
|
|
|
61
37
|
* @param id Peer ID to inspect.
|
|
62
38
|
*/
|
|
63
39
|
export declare function isDeterministicTableId(id: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* The ID of a user's membership row in a group, the same on every device.
|
|
42
|
+
*
|
|
43
|
+
* Several of an admin's devices can approve the same join at once; with this ID they write one
|
|
44
|
+
* row, which merges as an ordinary update, instead of rows that collide on the
|
|
45
|
+
* `(groupId, userId)` unique index.
|
|
46
|
+
*
|
|
47
|
+
* @param groupId The group.
|
|
48
|
+
* @param userId The member.
|
|
49
|
+
*/
|
|
50
|
+
export declare function deterministicGroupMemberId(groupId: string, userId: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Return whether a peer ID was generated by {@link deterministicGroupMemberId}.
|
|
53
|
+
*
|
|
54
|
+
* @param id Peer ID to inspect.
|
|
55
|
+
*/
|
|
56
|
+
export declare function isDeterministicGroupMemberId(id: string): boolean;
|
|
64
57
|
/** Resolves after `ms` milliseconds (0 yields a microtask-yield on most runtimes). */
|
|
65
58
|
export declare function sleep(ms?: number): Promise<void>;
|
|
66
59
|
/** Turns `fooBar` into `Foo Bar` for labels (also replaces `_` with space). */
|
|
@@ -117,6 +110,8 @@ export declare function getTimestamp(): number;
|
|
|
117
110
|
* @param opts.timeout Clamped timeout; defaults to {@link PeerDeviceConsts} min/max
|
|
118
111
|
* @param opts.retriesOnError Max error retries
|
|
119
112
|
* @param opts.retriesOnTimeout Max “timed out” races before giving up
|
|
113
|
+
* @param opts.shouldRetry Return false for a terminal error that must be reported immediately.
|
|
114
|
+
* @param opts.logFailure Log exhausted error retries; defaults to `console.error`.
|
|
120
115
|
* @throws Last error, timeout exhaustion, or if the connection is closed
|
|
121
116
|
*/
|
|
122
117
|
export declare function retryOnErrorOrTimeout<T>({ fn, connection, opts, }: {
|
|
@@ -126,6 +121,8 @@ export declare function retryOnErrorOrTimeout<T>({ fn, connection, opts, }: {
|
|
|
126
121
|
timeout?: number;
|
|
127
122
|
retriesOnError?: number;
|
|
128
123
|
retriesOnTimeout?: number;
|
|
124
|
+
shouldRetry?: (error: unknown) => boolean;
|
|
125
|
+
logFailure?: (message: string, error: unknown) => void;
|
|
129
126
|
};
|
|
130
127
|
}): Promise<T>;
|
|
131
128
|
/**
|
package/dist/utils.js
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TIMESTAMP_REANCHOR_THRESHOLD_MS = exports.moneyFormatter = exports.
|
|
4
|
-
exports.cryptoRandomString = cryptoRandomString;
|
|
5
|
-
exports.newid = newid;
|
|
6
|
-
exports.isid = isid;
|
|
7
|
-
exports.idTime = idTime;
|
|
8
|
-
exports.idDate = idDate;
|
|
9
|
-
exports.idRandNums = idRandNums;
|
|
3
|
+
exports.TIMESTAMP_REANCHOR_THRESHOLD_MS = exports.moneyFormatter = exports.newid = exports.MIN_ID = exports.MAX_ID = exports.isid = exports.idTime = exports.idRandNums = exports.idDate = exports.cryptoRandomString = void 0;
|
|
10
4
|
exports.simpleHash = simpleHash;
|
|
11
5
|
exports.simpleObjectHash = simpleObjectHash;
|
|
12
6
|
exports.deterministicPvarId = deterministicPvarId;
|
|
13
7
|
exports.isDeterministicPvarId = isDeterministicPvarId;
|
|
14
8
|
exports.deterministicTableId = deterministicTableId;
|
|
15
9
|
exports.isDeterministicTableId = isDeterministicTableId;
|
|
10
|
+
exports.deterministicGroupMemberId = deterministicGroupMemberId;
|
|
11
|
+
exports.isDeterministicGroupMemberId = isDeterministicGroupMemberId;
|
|
16
12
|
exports.sleep = sleep;
|
|
17
13
|
exports.camelCaseToSpaces = camelCaseToSpaces;
|
|
18
14
|
exports.camelCaseToHyphens = camelCaseToHyphens;
|
|
@@ -27,89 +23,15 @@ const _ = require("lodash");
|
|
|
27
23
|
const nacl = require("tweetnacl");
|
|
28
24
|
const peer_device_1 = require("./types/peer-device");
|
|
29
25
|
const stableStringify = require("fast-json-stable-stringify");
|
|
30
|
-
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
const n = r[i];
|
|
41
|
-
i++;
|
|
42
|
-
if (n >= 252) {
|
|
43
|
-
// we don't want to bias the characters generated so only consider the range of numbers that spread evenly between factors of 36
|
|
44
|
-
// we exclude 252, 253, 254, and 255 because it would bias towards '0', '1', '2', and '3' respectively
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
s += (n % 36).toString(36);
|
|
48
|
-
}
|
|
49
|
-
return s.substring(0, length);
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Creates a new 25-character peer id: base-36 time prefix (10 chars) + random suffix (15 chars).
|
|
53
|
-
* Sort order correlates with creation time. Use {@link isid} to validate user-provided values.
|
|
54
|
-
*/
|
|
55
|
-
function newid() {
|
|
56
|
-
// modeled after mongo ids, timestamp + counter + random but we're going to skip counter and have bigger random
|
|
57
|
-
// people can switch to using a counter + random later if they want (or do something else with the random part).
|
|
58
|
-
// This id is going to be a 128 bit number so it fits naturally into memory.
|
|
59
|
-
// 2 ** 128 ~= 3.4e38 is the maximum it can be
|
|
60
|
-
// For efficient transmission, storage, presentation, and usage we're going to represent this number with base 36 [0-9a-z]
|
|
61
|
-
// (3.4e38).toString(36) == "f55n5nmuuaw00000000000000", this is our max in base 36, 25 characters
|
|
62
|
-
// Date.now() =~ 1625282091498
|
|
63
|
-
// but we're dealing in base 36 not base 10
|
|
64
|
-
// (1625282091498).toString(36) == "kqn6zrvu" (length 8)
|
|
65
|
-
// Number.parseInt("zzzzzzzz", 36) == 2821109907455 == year 2059. Way too soon so going up to 9 characters
|
|
66
|
-
// Number.parseInt("zzzzzzzzz", 36) == 101559956668415 == year 5188.
|
|
67
|
-
// but the max size of 9 left most can only be "f55n5nmuu" == 42720753566838 == year 3323. That could still work but only ~ 1300 years
|
|
68
|
-
// that leaves 16 characters to represent a random number to prevent collisions each millisecond, 36 ** 16 ~= 8e24
|
|
69
|
-
// Allocating one more character to the time and one less to the random number to avoid hitting max time in any conceivable future
|
|
70
|
-
// now our max time is new Date(Number.parseInt('f55n5nmuua',36)) == +050705-08-09T23:40:06.178Z. 50k years from now.
|
|
71
|
-
// We still have 15 chars for our random number. 36 ** 15 ~= 2e23
|
|
72
|
-
// That is still a very large number and the chance of a collision should be so small as to be effectively globally unique
|
|
73
|
-
// and certainly unique in any group's database.
|
|
74
|
-
const time = Date.now().toString(36).padStart(10, "0"); // e.g: "00kq6xh45f", length == 10
|
|
75
|
-
return time + cryptoRandomString(15);
|
|
76
|
-
}
|
|
77
|
-
/** Lexicographic lower bound for valid peer ids (epoch). */
|
|
78
|
-
exports.MIN_ID = "0".repeat(25); // 1970
|
|
79
|
-
/** Lexicographic upper bound for valid peer ids (far future). */
|
|
80
|
-
exports.MAX_ID = `f55n5nmuua${"z".repeat(15)}`; // 50k years from now
|
|
81
|
-
/**
|
|
82
|
-
* Returns whether `id` is a 25-char base-36 string whose embedded time is not in the future (5 min skew allowed).
|
|
83
|
-
* @param id Candidate id (coerced to string)
|
|
84
|
-
*/
|
|
85
|
-
function isid(id) {
|
|
86
|
-
id = String(id);
|
|
87
|
-
const idMatch = Boolean(/^[0-9a-z]{25}$/i.exec(id));
|
|
88
|
-
if (idMatch) {
|
|
89
|
-
// only consider ids valid if they are less than current time (with 5 min buffer)
|
|
90
|
-
// This is to prevent people from making ids with times in the future to get them to always show up as the "newest" item and/or prematurely claim ids
|
|
91
|
-
return idTime(id) < Date.now() + 300000;
|
|
92
|
-
}
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
/** Returns the `Date.now()`-compatible millisecond time encoded in the first 10 base-36 characters of a peer id. */
|
|
96
|
-
function idTime(id) {
|
|
97
|
-
const time36 = id.substr(0, 10);
|
|
98
|
-
return Number.parseInt(time36, 36);
|
|
99
|
-
}
|
|
100
|
-
/** Interprets a peer id’s time prefix as a `Date` (see {@link idTime}). */
|
|
101
|
-
function idDate(id) {
|
|
102
|
-
return new Date(idTime(id));
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Returns the 15-character random suffix of a peer id as two base-36 integers so large values stay exact in JS.
|
|
106
|
-
* @param id A 25-character peer id from {@link newid}
|
|
107
|
-
*/
|
|
108
|
-
function idRandNums(id) {
|
|
109
|
-
const numPart1 = id.substr(10, 8);
|
|
110
|
-
const numPart2 = id.substr(18, 7);
|
|
111
|
-
return [Number.parseInt(numPart1, 36), Number.parseInt(numPart2, 36)];
|
|
112
|
-
}
|
|
26
|
+
var peer_id_1 = require("./peer-id");
|
|
27
|
+
Object.defineProperty(exports, "cryptoRandomString", { enumerable: true, get: function () { return peer_id_1.cryptoRandomString; } });
|
|
28
|
+
Object.defineProperty(exports, "idDate", { enumerable: true, get: function () { return peer_id_1.idDate; } });
|
|
29
|
+
Object.defineProperty(exports, "idRandNums", { enumerable: true, get: function () { return peer_id_1.idRandNums; } });
|
|
30
|
+
Object.defineProperty(exports, "idTime", { enumerable: true, get: function () { return peer_id_1.idTime; } });
|
|
31
|
+
Object.defineProperty(exports, "isid", { enumerable: true, get: function () { return peer_id_1.isid; } });
|
|
32
|
+
Object.defineProperty(exports, "MAX_ID", { enumerable: true, get: function () { return peer_id_1.MAX_ID; } });
|
|
33
|
+
Object.defineProperty(exports, "MIN_ID", { enumerable: true, get: function () { return peer_id_1.MIN_ID; } });
|
|
34
|
+
Object.defineProperty(exports, "newid", { enumerable: true, get: function () { return peer_id_1.newid; } });
|
|
113
35
|
/** Fast non-cryptographic 32-bit string hash (sign-insensitive) for display keys and bucketing. */
|
|
114
36
|
function simpleHash(str) {
|
|
115
37
|
let hash = 0;
|
|
@@ -128,6 +50,8 @@ function simpleObjectHash(obj) {
|
|
|
128
50
|
const DETERMINISTIC_PVAR_PREFIX = "000pvar0";
|
|
129
51
|
/** Well-known prefix for deterministic package-table IDs. */
|
|
130
52
|
const DETERMINISTIC_TABLE_PREFIX = "000tbl00";
|
|
53
|
+
/** Well-known prefix for deterministic group-membership IDs. */
|
|
54
|
+
const DETERMINISTIC_GROUP_MEMBER_PREFIX = "000gmbr0";
|
|
131
55
|
/**
|
|
132
56
|
* Converts a byte array to a base-36 string of the requested length, using unbiased modular reduction.
|
|
133
57
|
* Bytes >= 252 are skipped to avoid bias (same technique as {@link cryptoRandomString}).
|
|
@@ -185,6 +109,27 @@ function deterministicTableId(identity) {
|
|
|
185
109
|
function isDeterministicTableId(id) {
|
|
186
110
|
return id.startsWith(DETERMINISTIC_TABLE_PREFIX);
|
|
187
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* The ID of a user's membership row in a group, the same on every device.
|
|
114
|
+
*
|
|
115
|
+
* Several of an admin's devices can approve the same join at once; with this ID they write one
|
|
116
|
+
* row, which merges as an ordinary update, instead of rows that collide on the
|
|
117
|
+
* `(groupId, userId)` unique index.
|
|
118
|
+
*
|
|
119
|
+
* @param groupId The group.
|
|
120
|
+
* @param userId The member.
|
|
121
|
+
*/
|
|
122
|
+
function deterministicGroupMemberId(groupId, userId) {
|
|
123
|
+
return deterministicPeerId(DETERMINISTIC_GROUP_MEMBER_PREFIX, `${groupId}:${userId}`);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Return whether a peer ID was generated by {@link deterministicGroupMemberId}.
|
|
127
|
+
*
|
|
128
|
+
* @param id Peer ID to inspect.
|
|
129
|
+
*/
|
|
130
|
+
function isDeterministicGroupMemberId(id) {
|
|
131
|
+
return id.startsWith(DETERMINISTIC_GROUP_MEMBER_PREFIX);
|
|
132
|
+
}
|
|
188
133
|
/** Resolves after `ms` milliseconds (0 yields a microtask-yield on most runtimes). */
|
|
189
134
|
function sleep(ms = 0) {
|
|
190
135
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -323,6 +268,8 @@ function getTimestamp() {
|
|
|
323
268
|
* @param opts.timeout Clamped timeout; defaults to {@link PeerDeviceConsts} min/max
|
|
324
269
|
* @param opts.retriesOnError Max error retries
|
|
325
270
|
* @param opts.retriesOnTimeout Max “timed out” races before giving up
|
|
271
|
+
* @param opts.shouldRetry Return false for a terminal error that must be reported immediately.
|
|
272
|
+
* @param opts.logFailure Log exhausted error retries; defaults to `console.error`.
|
|
326
273
|
* @throws Last error, timeout exhaustion, or if the connection is closed
|
|
327
274
|
*/
|
|
328
275
|
async function retryOnErrorOrTimeout({ fn, connection, opts = {}, }) {
|
|
@@ -338,8 +285,8 @@ async function retryOnErrorOrTimeout({ fn, connection, opts = {}, }) {
|
|
|
338
285
|
let resultSet = false;
|
|
339
286
|
let finalError;
|
|
340
287
|
while (errorCount <= retriesOnError && timeoutCount <= retriesOnTimeout) {
|
|
288
|
+
let sleepTimeout = null;
|
|
341
289
|
try {
|
|
342
|
-
let sleepTimeout = null;
|
|
343
290
|
const sleepPromise = new Promise((resolve) => {
|
|
344
291
|
sleepTimeout = setTimeout(() => {
|
|
345
292
|
resolve();
|
|
@@ -372,14 +319,20 @@ async function retryOnErrorOrTimeout({ fn, connection, opts = {}, }) {
|
|
|
372
319
|
finalError = error;
|
|
373
320
|
connection.errorRate = connection.errorRate * 0.9 + 0.1;
|
|
374
321
|
errorCount++;
|
|
322
|
+
if (opts.shouldRetry?.(error) === false)
|
|
323
|
+
throw error;
|
|
375
324
|
if (connection.closed) {
|
|
376
325
|
throw new Error(`connection has been closed`);
|
|
377
326
|
}
|
|
378
327
|
}
|
|
328
|
+
finally {
|
|
329
|
+
if (sleepTimeout !== null)
|
|
330
|
+
clearTimeout(sleepTimeout);
|
|
331
|
+
}
|
|
379
332
|
console.warn(`Call failed, retrying: timeout is ${timeout}ms, errorCount: ${errorCount} of ${retriesOnError}, timeoutCount: ${timeoutCount} of ${retriesOnTimeout + 1}. Last error:`, finalError);
|
|
380
333
|
}
|
|
381
334
|
if (finalError) {
|
|
382
|
-
console.error(`Retry failed after ${errorCount} errors and ${timeoutCount} timeouts. Last error:`, finalError);
|
|
335
|
+
(opts.logFailure ?? console.error)(`Retry failed after ${errorCount} errors and ${timeoutCount} timeouts. Last error:`, finalError);
|
|
383
336
|
throw finalError;
|
|
384
337
|
}
|
|
385
338
|
else {
|
package/dist/utils.test.js
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const lodash_1 = require("lodash");
|
|
4
4
|
const utils_1 = require("./utils");
|
|
5
|
+
describe("retryOnErrorOrTimeout", () => {
|
|
6
|
+
it("reports a terminal error after one call and clears its timeout", async () => {
|
|
7
|
+
jest.useFakeTimers();
|
|
8
|
+
const error = new Error("No handler registered for event: listChanges_group");
|
|
9
|
+
const fn = jest.fn().mockRejectedValue(error);
|
|
10
|
+
try {
|
|
11
|
+
await expect((0, utils_1.retryOnErrorOrTimeout)({
|
|
12
|
+
fn,
|
|
13
|
+
connection: { deviceId: (0, utils_1.newid)(), latencyMs: 1, errorRate: 0, timestampLastApplied: 0 },
|
|
14
|
+
opts: { shouldRetry: () => false },
|
|
15
|
+
})).rejects.toBe(error);
|
|
16
|
+
expect(fn).toHaveBeenCalledTimes(1);
|
|
17
|
+
expect(jest.getTimerCount()).toBe(0);
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
jest.useRealTimers();
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
});
|
|
5
24
|
describe("readResponseTextCapped", () => {
|
|
6
25
|
it("returns a body within the cap and refuses one over it", async () => {
|
|
7
26
|
await expect((0, utils_1.readResponseTextCapped)(new Response("hello"), 5)).resolves.toBe("hello");
|
|
@@ -132,6 +151,23 @@ describe("newid", () => {
|
|
|
132
151
|
});
|
|
133
152
|
});
|
|
134
153
|
});
|
|
154
|
+
describe("deterministicGroupMemberId", () => {
|
|
155
|
+
it("preserves the membership identity protocol across SDK versions", () => {
|
|
156
|
+
expect((0, utils_1.deterministicGroupMemberId)("00000000000000000000000001", "00000000000000000000000002")).toBe("000gmbr0jzkpw7vuc2ew84u8c");
|
|
157
|
+
});
|
|
158
|
+
it("is a stable peer id per group and user", () => {
|
|
159
|
+
const groupId = (0, utils_1.newid)();
|
|
160
|
+
const userId = (0, utils_1.newid)();
|
|
161
|
+
const id = (0, utils_1.deterministicGroupMemberId)(groupId, userId);
|
|
162
|
+
expect(id).toMatch(/^[a-z0-9]{25}$/);
|
|
163
|
+
expect((0, utils_1.isid)(id)).toBe(true);
|
|
164
|
+
expect((0, utils_1.isDeterministicGroupMemberId)(id)).toBe(true);
|
|
165
|
+
expect((0, utils_1.deterministicGroupMemberId)(groupId, userId)).toBe(id);
|
|
166
|
+
expect((0, utils_1.deterministicGroupMemberId)(groupId, (0, utils_1.newid)())).not.toBe(id);
|
|
167
|
+
expect((0, utils_1.deterministicGroupMemberId)(userId, groupId)).not.toBe(id);
|
|
168
|
+
expect((0, utils_1.isDeterministicGroupMemberId)((0, utils_1.newid)())).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
135
171
|
describe("debounceByArgs", () => {
|
|
136
172
|
beforeEach(() => {
|
|
137
173
|
jest.useFakeTimers();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peers-app/peers-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/peers-app/peers-sdk.git"
|
|
@@ -9,8 +9,14 @@
|
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"main": "dist/index.js",
|
|
11
11
|
"types": "dist/index.d.ts",
|
|
12
|
+
"bin": {
|
|
13
|
+
"peers-package": "bin/peers-package.js"
|
|
14
|
+
},
|
|
12
15
|
"files": [
|
|
13
|
-
"dist"
|
|
16
|
+
"dist",
|
|
17
|
+
"define",
|
|
18
|
+
"testing",
|
|
19
|
+
"bin"
|
|
14
20
|
],
|
|
15
21
|
"publishConfig": {
|
|
16
22
|
"access": "public"
|
|
@@ -61,6 +67,7 @@
|
|
|
61
67
|
"@types/moment-timezone": "^0.5.30",
|
|
62
68
|
"@types/node": "^22.4.0",
|
|
63
69
|
"better-sqlite3": "^11.8.1",
|
|
70
|
+
"esbuild": "^0.28.2",
|
|
64
71
|
"jest": "^29.7.0",
|
|
65
72
|
"ts-jest": "^29.2.5",
|
|
66
73
|
"typescript": "^5.8.3"
|