@dxos/keys 0.8.3 → 0.8.4-main.16b68245aa

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/src/dxn.ts CHANGED
@@ -2,11 +2,11 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
6
- import type { inspect, InspectOptionsStylized } from 'node:util';
5
+ import * as Schema from 'effect/Schema';
6
+ import type { InspectOptionsStylized, inspect } from 'node:util';
7
7
 
8
- import { devtoolsFormatter, type DevtoolsFormatter, inspectCustom } from '@dxos/debug';
9
- import { invariant } from '@dxos/invariant';
8
+ import { type DevtoolsFormatter, devtoolsFormatter, inspectCustom } from '@dxos/debug';
9
+ import { assertArgument, invariant } from '@dxos/invariant';
10
10
 
11
11
  import { ObjectId } from './object-id';
12
12
  import { SpaceId } from './space-id';
@@ -18,6 +18,8 @@ import { SpaceId } from './space-id';
18
18
  // TODO(dmaretskyi): "@" is a separator character in the URI spec.
19
19
  export const LOCAL_SPACE_TAG = '@';
20
20
 
21
+ export const DXN_ECHO_REGEXP = /@(dxn:[a-zA-Z0-p:@]+)/;
22
+
21
23
  // TODO(burdon): Namespace for.
22
24
  export const QueueSubspaceTags = Object.freeze({
23
25
  DATA: 'data',
@@ -26,6 +28,12 @@ export const QueueSubspaceTags = Object.freeze({
26
28
 
27
29
  export type QueueSubspaceTag = (typeof QueueSubspaceTags)[keyof typeof QueueSubspaceTags];
28
30
 
31
+ // TODO(burdon): Refactor.
32
+ // Consider: https://github.com/multiformats/multiaddr
33
+ // dxn:echo:[<space-id>:[<queue-id>:]]<object-id>
34
+ // dxn:echo:[S/<space-id>:[Q/<queue-id>:]]<object-id>
35
+ // dxn:type:org.dxos.markdown.contact
36
+
29
37
  /**
30
38
  * DXN unambiguously names a resource like an ECHO object, schema definition, plugin, etc.
31
39
  * Each DXN starts with a dxn prefix, followed by a resource kind.
@@ -38,20 +46,21 @@ export type QueueSubspaceTag = (typeof QueueSubspaceTags)[keyof typeof QueueSubs
38
46
  * dxn:echo:<space key>:<echo id>
39
47
  * dxn:echo:BA25QRC2FEWCSAMRP4RZL65LWJ7352CKE:01J00J9B45YHYSGZQTQMSKMGJ6
40
48
  * dxn:echo:@:01J00J9B45YHYSGZQTQMSKMGJ6
41
- * dxn:type:dxos.org/type/Calendar
42
- * dxn:plugin:dxos.org/agent/plugin/functions
49
+ * dxn:type:org.dxos.type.calendar
50
+ * dxn:plugin:org.dxos.agent.plugin.functions
43
51
  * ```
44
52
  */
45
53
  export class DXN {
54
+ // TODO(burdon): Rename to DXN (i.e., DXN.DXN).
46
55
  // TODO(dmaretskyi): Should this be a transformation into the DXN type?
47
56
  static Schema = Schema.NonEmptyString.pipe(
48
57
  Schema.pattern(/^dxn:([^:]+):(?:[^:]+:?)+[^:]$/),
49
58
  // TODO(dmaretskyi): To set the format we need to move the annotation IDs out of the echo-schema package.
50
- // FormatAnnotation.set(FormatEnum.DXN),
59
+ // FormatAnnotation.set(TypeFormat.DXN),
51
60
  Schema.annotations({
52
61
  title: 'DXN',
53
62
  description: 'DXN URI',
54
- examples: ['dxn:type:example.com/type/MyType', 'dxn:echo:@:01J00J9B45YHYSGZQTQMSKMGJ6'],
63
+ examples: ['dxn:type:com.example.type.my-type', 'dxn:echo:@:01J00J9B45YHYSGZQTQMSKMGJ6'],
55
64
  }),
56
65
  );
57
66
 
@@ -64,16 +73,16 @@ export class DXN {
64
73
  */
65
74
  static kind = Object.freeze({
66
75
  /**
67
- * dxn:type:<type name>[:<version>]
76
+ * dxn:type:<type_name>[:<version>]
68
77
  */
69
78
  TYPE: 'type',
70
79
 
71
80
  /**
72
- * dxn:echo:<space id>:<echo id>
73
- * dxn:echo:@:<echo id>
81
+ * dxn:echo:<space_id>:<echo_id>
82
+ * dxn:echo:@:<echo_id>
74
83
  */
75
- // TODO(burdon): Rename to OBJECT? (BREAKING CHANGE).
76
- // TODO(burdon): Add separate Kind for space.
84
+ // TODO(burdon): Rename to OBJECT? (BREAKING CHANGE to update "echo").
85
+ // TODO(burdon): Add separate Kind for space?
77
86
  ECHO: 'echo',
78
87
 
79
88
  /**
@@ -85,14 +94,19 @@ export class DXN {
85
94
  QUEUE: 'queue',
86
95
  });
87
96
 
88
- get kind() {
89
- return this.#kind;
90
- }
91
-
97
+ /**
98
+ * Exactly equals.
99
+ */
92
100
  static equals(a: DXN, b: DXN): boolean {
93
101
  return a.kind === b.kind && a.parts.length === b.parts.length && a.parts.every((part, i) => part === b.parts[i]);
94
102
  }
95
103
 
104
+ static equalsEchoId(a: DXN, b: DXN): boolean {
105
+ const a1 = a.asEchoDXN();
106
+ const b1 = b.asEchoDXN();
107
+ return !!a1 && !!b1 && a1.echoId === b1.echoId;
108
+ }
109
+
96
110
  // TODO(burdon): Rename isValid.
97
111
  static isDXNString(dxn: string): boolean {
98
112
  return dxn.startsWith('dxn:');
@@ -119,37 +133,47 @@ export class DXN {
119
133
  static tryParse(dxn: string): DXN | undefined {
120
134
  try {
121
135
  return DXN.parse(dxn);
122
- } catch (error) {
136
+ } catch {
123
137
  return undefined;
124
138
  }
125
139
  }
126
140
 
127
141
  /**
128
- * @example `dxn:type:example.com/type/Contact`
142
+ * @example `dxn:type:com.example.type.person`
129
143
  */
130
144
  static fromTypename(typename: string): DXN {
131
145
  return new DXN(DXN.kind.TYPE, [typename]);
132
146
  }
133
147
 
134
148
  /**
135
- * @example `dxn:type:example.com/type/Contact:0.1.0`
149
+ * @example `dxn:type:com.example.type.person:0.1.0`
136
150
  */
137
151
  // TODO(dmaretskyi): Consider using @ as the version separator.
138
152
  static fromTypenameAndVersion(typename: string, version: string): DXN {
139
153
  return new DXN(DXN.kind.TYPE, [typename, version]);
140
154
  }
141
155
 
156
+ /**
157
+ * @example `dxn:echo:BA25QRC2FEWCSAMRP4RZL65LWJ7352CKE:01J00J9B45YHYSGZQTQMSKMGJ6`
158
+ */
159
+ static fromSpaceAndObjectId(spaceId: SpaceId, objectId: ObjectId): DXN {
160
+ assertArgument(SpaceId.isValid(spaceId), `Invalid space ID: ${spaceId}`);
161
+ assertArgument(ObjectId.isValid(objectId), 'objectId', `Invalid object ID: ${objectId}`);
162
+ return new DXN(DXN.kind.ECHO, [spaceId, objectId]);
163
+ }
164
+
142
165
  /**
143
166
  * @example `dxn:echo:@:01J00J9B45YHYSGZQTQMSKMGJ6`
144
167
  */
145
168
  static fromLocalObjectId(id: string): DXN {
169
+ assertArgument(ObjectId.isValid(id), 'id', `Invalid object ID: ${id}`);
146
170
  return new DXN(DXN.kind.ECHO, [LOCAL_SPACE_TAG, id]);
147
171
  }
148
172
 
149
173
  static fromQueue(subspaceTag: QueueSubspaceTag, spaceId: SpaceId, queueId: ObjectId, objectId?: ObjectId) {
150
- invariant(SpaceId.isValid(spaceId));
151
- invariant(ObjectId.isValid(queueId));
152
- invariant(!objectId || ObjectId.isValid(objectId));
174
+ assertArgument(SpaceId.isValid(spaceId), `Invalid space ID: ${spaceId}`);
175
+ assertArgument(ObjectId.isValid(queueId), 'queueId', `Invalid queue ID: ${queueId}`);
176
+ assertArgument(!objectId || ObjectId.isValid(objectId), 'objectId', `Invalid object ID: ${objectId}`);
153
177
 
154
178
  return new DXN(DXN.kind.QUEUE, [subspaceTag, spaceId, queueId, ...(objectId ? [objectId] : [])]);
155
179
  }
@@ -158,19 +182,23 @@ export class DXN {
158
182
  #parts: string[];
159
183
 
160
184
  constructor(kind: string, parts: string[]) {
161
- invariant(parts.length > 0);
162
- invariant(parts.every((part) => typeof part === 'string' && part.length > 0 && part.indexOf(':') === -1));
185
+ assertArgument(parts.length > 0, 'parts', `Invalid DXN: ${parts}`);
186
+ assertArgument(
187
+ parts.every((part) => typeof part === 'string' && part.length > 0 && part.indexOf(':') === -1),
188
+ 'parts',
189
+ `Invalid DXN: ${parts}`,
190
+ );
163
191
 
164
192
  // Per-type validation.
165
193
  switch (kind) {
166
194
  case DXN.kind.TYPE:
167
195
  if (parts.length > 2) {
168
- throw new Error('Invalid "type" DXN');
196
+ throw new Error('Invalid DXN.kind.TYPE');
169
197
  }
170
198
  break;
171
199
  case DXN.kind.ECHO:
172
200
  if (parts.length !== 2) {
173
- throw new Error('Invalid "echo" DXN');
201
+ throw new Error('Invalid DXN.kind.ECHO');
174
202
  }
175
203
  break;
176
204
  }
@@ -179,6 +207,39 @@ export class DXN {
179
207
  this.#parts = parts;
180
208
  }
181
209
 
210
+ toString(): DXN.String {
211
+ return `dxn:${this.#kind}:${this.#parts.join(':')}` as DXN.String;
212
+ }
213
+
214
+ toJSON(): string {
215
+ return this.toString();
216
+ }
217
+
218
+ /**
219
+ * Used by Node.js to get textual representation of this object when it's printed with a `console.log` statement.
220
+ */
221
+ [inspectCustom](depth: number, options: InspectOptionsStylized, inspectFn: typeof inspect): string {
222
+ const printControlCode = (code: number) => {
223
+ return `\x1b[${code}m`;
224
+ };
225
+
226
+ return (
227
+ printControlCode(inspectFn.colors.blueBright![0]) + this.toString() + printControlCode(inspectFn.colors.reset![0])
228
+ );
229
+ }
230
+
231
+ get [devtoolsFormatter](): DevtoolsFormatter {
232
+ return {
233
+ header: () => {
234
+ return ['span', { style: 'font-weight: bold;' }, this.toString()];
235
+ },
236
+ };
237
+ }
238
+
239
+ get kind() {
240
+ return this.#kind;
241
+ }
242
+
182
243
  get parts() {
183
244
  return this.#parts;
184
245
  }
@@ -189,6 +250,10 @@ export class DXN {
189
250
  return this.#parts[0];
190
251
  }
191
252
 
253
+ equals(other: DXN): boolean {
254
+ return DXN.equals(this, other);
255
+ }
256
+
192
257
  hasTypenameOf(typename: string): boolean {
193
258
  return this.#kind === DXN.kind.TYPE && this.#parts.length === 1 && this.#parts[0] === typename;
194
259
  }
@@ -204,6 +269,7 @@ export class DXN {
204
269
 
205
270
  const [type, version] = this.#parts;
206
271
  return {
272
+ // TODO(wittjosiah): Should be `typename` for consistency.
207
273
  type,
208
274
  version: version as string | undefined,
209
275
  };
@@ -217,6 +283,7 @@ export class DXN {
217
283
  const [spaceId, echoId] = this.#parts;
218
284
  return {
219
285
  spaceId: spaceId === LOCAL_SPACE_TAG ? undefined : (spaceId as SpaceId | undefined),
286
+ // TODO(burdon): objectId.
220
287
  echoId,
221
288
  };
222
289
  }
@@ -232,66 +299,33 @@ export class DXN {
232
299
  }
233
300
 
234
301
  return {
235
- subspaceTag,
302
+ subspaceTag: subspaceTag as QueueSubspaceTag,
236
303
  spaceId: spaceId as SpaceId,
237
304
  queueId,
238
305
  objectId: objectId as string | undefined,
239
306
  };
240
307
  }
241
308
 
242
- toString(): DXN.String {
243
- return `dxn:${this.#kind}:${this.#parts.join(':')}` as DXN.String;
244
- }
245
-
246
309
  /**
247
- * Used by Node.js to get textual representation of this object when it's printed with a `console.log` statement.
310
+ * Produces a new DXN with the given parts appended.
248
311
  */
249
- [inspectCustom](depth: number, options: InspectOptionsStylized, inspectFn: typeof inspect): string {
250
- const printControlCode = (code: number) => {
251
- return `\x1b[${code}m`;
252
- };
253
-
254
- return (
255
- printControlCode(inspectFn.colors.blueBright![0]) + this.toString() + printControlCode(inspectFn.colors.reset![0])
256
- );
257
- }
258
-
259
- get [devtoolsFormatter](): DevtoolsFormatter {
260
- return {
261
- header: () => {
262
- return ['span', { style: 'font-weight: bold;' }, this.toString()];
263
- },
264
- };
312
+ extend(parts: string[]): DXN {
313
+ return new DXN(this.#kind, [...this.#parts, ...parts]);
265
314
  }
266
315
  }
267
316
 
268
- // TODO(dmaretskyi): Fluent API:
269
- /*
270
- class DXN {
271
- ...
272
- isEchoDXN(): this is EchoDXN {
273
- return this.#kind === DXN.kind.ECHO;
274
- }
275
- ...
276
- }
277
-
278
- interface EchoDXN extends DXN {
279
- objectId: ObjectId;
280
- }
281
-
282
- declare const dxn: DXN;
283
-
284
- dxn.objectId
285
-
286
- if(dxn.isEchoDXN()) {
287
- dxn.objectId
288
- }
289
- ```
290
-
291
317
  /**
292
318
  * API namespace.
293
319
  */
294
320
  export declare namespace DXN {
321
+ /**
322
+ * DXN represented as a javascript string.
323
+ */
324
+ // TODO(burdon): Use Effect branded string?
325
+ // export const String = S.String.pipe(S.brand('DXN'));
326
+ // export type String = S.To(typoeof String);
327
+ export type String = string & { __DXNString: never };
328
+
295
329
  export type TypeDXN = {
296
330
  type: string;
297
331
  version?: string;
@@ -299,22 +333,13 @@ export declare namespace DXN {
299
333
 
300
334
  export type EchoDXN = {
301
335
  spaceId?: SpaceId;
302
- // TODO(burdon): Rename objectId.
303
- echoId: string; // TODO(dmaretskyi): ObjectId.
336
+ echoId: string; // TODO(dmaretskyi): Rename to `objectId` and use `ObjectId` for the type.
304
337
  };
305
338
 
306
339
  export type QueueDXN = {
307
- subspaceTag: string;
340
+ subspaceTag: QueueSubspaceTag;
308
341
  spaceId: SpaceId;
309
342
  queueId: string; // TODO(dmaretskyi): ObjectId.
310
343
  objectId?: string; // TODO(dmaretskyi): ObjectId.
311
344
  };
312
-
313
- /**
314
- * DXN represented as a javascript string.
315
- */
316
- export type String = string & { __DXNString: never };
317
- // TODO(burdon): Make brand.
318
- // export const String = S.String.pipe(S.brand('DXN'));
319
- // export type String = S.To(typoeof String);
320
345
  }
package/src/index.ts CHANGED
@@ -7,4 +7,4 @@ export * from './identity-did';
7
7
  export * from './object-id';
8
8
  export * from './public-key';
9
9
  export * from './space-id';
10
- export * from './types';
10
+ export type * from './types';
package/src/object-id.ts CHANGED
@@ -2,23 +2,53 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
6
- import { ulid } from 'ulidx';
5
+ import * as Schema from 'effect/Schema';
6
+ import { type PRNG, type ULIDFactory, monotonicFactory } from 'ulidx';
7
7
 
8
8
  // TODO(dmaretskyi): Make brand.
9
9
  // export const ObjectIdBrand: unique symbol = Symbol('@dxos/echo/ObjectId');
10
10
  // export const ObjectIdSchema = Schema.ULID.pipe(S.brand(ObjectIdBrand));
11
11
  const ObjectIdSchema = Schema.String.pipe(Schema.pattern(/^[0-7][0-9A-HJKMNP-TV-Z]{25}$/i)).annotations({
12
- description: 'a Universally Unique Lexicographically Sortable Identifier',
12
+ description: 'A Universally Unique Lexicographically Sortable Identifier',
13
13
  pattern: '^[0-7][0-9A-HJKMNP-TV-Z]{25}$',
14
14
  });
15
15
 
16
16
  export type ObjectId = typeof ObjectIdSchema.Type;
17
17
 
18
18
  export interface ObjectIdClass extends Schema.SchemaClass<ObjectId, string> {
19
+ /**
20
+ * @returns true if the string is a valid ObjectId.
21
+ */
19
22
  isValid(id: string): id is ObjectId;
23
+
24
+ /**
25
+ * Creates an ObjectId from a string validating the format.
26
+ */
20
27
  make(id: string): ObjectId;
28
+
29
+ /**
30
+ * Generates a random ObjectId.
31
+ */
21
32
  random(): ObjectId;
33
+
34
+ /**
35
+ * WARNING: To be used only within tests.
36
+ *
37
+ * Disables randomness in ObjectId generation, causing the same sequence of IDs to be generated.
38
+ * Do not use in production code as this will cause data collisions.
39
+ * Place this at the top of the test file to ensure that the same sequence of IDs is generated.
40
+ *
41
+ * ```ts
42
+ * ObjectId.dangerouslyDisableRandomness();
43
+ *
44
+ * describe('suite', () => {
45
+ * // ...
46
+ * });
47
+ * ```
48
+ *
49
+ * NOTE: The generated IDs depend on the order of ObjectId.random() calls, which might be affected by test order, scheduling, etc.
50
+ */
51
+ dangerouslyDisableRandomness(): void;
22
52
  }
23
53
 
24
54
  /**
@@ -27,16 +57,75 @@ export interface ObjectIdClass extends Schema.SchemaClass<ObjectId, string> {
27
57
  * Follows ULID spec.
28
58
  */
29
59
  export const ObjectId: ObjectIdClass = class extends ObjectIdSchema {
60
+ static #factory: ULIDFactory = monotonicFactory();
61
+ static #seedTime: number | undefined = undefined;
62
+
30
63
  static isValid(id: string): id is ObjectId {
31
64
  try {
32
65
  Schema.decodeSync(ObjectId)(id);
33
66
  return true;
34
- } catch (err) {
67
+ } catch {
35
68
  return false;
36
69
  }
37
70
  }
38
71
 
39
72
  static random(): ObjectId {
40
- return ulid() as ObjectId;
73
+ return this.#factory(this.#seedTime) as ObjectId;
74
+ }
75
+
76
+ static dangerouslyDisableRandomness() {
77
+ this.#factory = monotonicFactory(makeTestPRNG());
78
+ this.#seedTime = new Date('2025-01-01').getTime();
41
79
  }
42
80
  };
81
+
82
+ /**
83
+ * Test PRNG that always starts with the same seed and produces the same sequence.
84
+ */
85
+ const makeTestPRNG = (): PRNG => {
86
+ const rng = new SimplePRNG();
87
+ return () => {
88
+ return rng.next();
89
+ };
90
+ };
91
+
92
+ /**
93
+ * Simple Linear Congruential Generator (LCG) for pseudo-random number generation.
94
+ * Returns numbers in the range [0, 1) (0 inclusive, 1 exclusive).
95
+ */
96
+ export class SimplePRNG {
97
+ #seed: number;
98
+
99
+ // LCG parameters (from Numerical Recipes)
100
+ static readonly #a = 1664525;
101
+ static readonly #c = 1013904223;
102
+ static readonly #m = Math.pow(2, 32);
103
+
104
+ /**
105
+ * Creates a new PRNG instance.
106
+ * @param seed - Initial seed value. If not provided, uses 0.
107
+ */
108
+ constructor(seed: number = 0) {
109
+ this.#seed = seed;
110
+ }
111
+
112
+ /**
113
+ * Generates the next pseudo-random number in the range [0, 1).
114
+ * @returns A pseudo-random number between 0 (inclusive) and 1 (exclusive).
115
+ */
116
+ next(): number {
117
+ // Update seed using LCG formula: (a * seed + c) mod m
118
+ this.#seed = (SimplePRNG.#a * this.#seed + SimplePRNG.#c) % SimplePRNG.#m;
119
+
120
+ // Normalize to [0, 1) range
121
+ return this.#seed / SimplePRNG.#m;
122
+ }
123
+
124
+ /**
125
+ * Resets the generator with a new seed.
126
+ * @param seed - New seed value.
127
+ */
128
+ reset(seed: number): void {
129
+ this.#seed = seed;
130
+ }
131
+ }
package/src/prng.ts ADDED
@@ -0,0 +1,54 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ /**
6
+ * Simple Linear Congruential Generator (LCG) for pseudo-random number generation.
7
+ * Returns numbers in the range [0, 1) (0 inclusive, 1 exclusive).
8
+ */
9
+ export class SimplePRNG {
10
+ private _seed: number;
11
+
12
+ // LCG parameters (from Numerical Recipes)
13
+ private static readonly _a = 1664525;
14
+ private static readonly _c = 1013904223;
15
+ private static readonly _m = Math.pow(2, 32);
16
+
17
+ /**
18
+ * Creates a new PRNG instance.
19
+ * @param seed - Initial seed value. If not provided, uses current timestamp.
20
+ */
21
+ constructor(seed?: number) {
22
+ this._seed = seed ?? Date.now();
23
+ }
24
+
25
+ /**
26
+ * Generates the next pseudo-random number in the range [0, 1).
27
+ * @returns A pseudo-random number between 0 (inclusive) and 1 (exclusive).
28
+ */
29
+ next(): number {
30
+ // Update seed using LCG formula: (a * seed + c) mod m
31
+ this._seed = (SimplePRNG._a * this._seed + SimplePRNG._c) % SimplePRNG._m;
32
+
33
+ // Normalize to [0, 1) range
34
+ return this._seed / SimplePRNG._m;
35
+ }
36
+
37
+ /**
38
+ * Resets the generator with a new seed.
39
+ * @param seed - New seed value.
40
+ */
41
+ reset(seed: number): void {
42
+ this._seed = seed;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Creates a simple PRNG function with optional seed.
48
+ * @param seed - Optional seed value.
49
+ * @returns A function that returns pseudo-random numbers in [0, 1).
50
+ */
51
+ export const createPRNG = (seed?: number): (() => number) => {
52
+ const prng = new SimplePRNG(seed);
53
+ return () => prng.next();
54
+ };
package/src/public-key.ts CHANGED
@@ -4,13 +4,13 @@
4
4
 
5
5
  import base32Decode from 'base32-decode';
6
6
  import base32Encode from 'base32-encode';
7
- import { type inspect, type InspectOptionsStylized } from 'node:util';
7
+ import { type InspectOptionsStylized, type inspect } from 'node:util';
8
8
 
9
9
  import {
10
- devtoolsFormatter,
11
10
  type DevtoolsFormatter,
12
- equalsSymbol,
13
11
  type Equatable,
12
+ devtoolsFormatter,
13
+ equalsSymbol,
14
14
  inspectCustom,
15
15
  truncateKey,
16
16
  } from '@dxos/debug';
@@ -216,7 +216,7 @@ export class PublicKey implements Equatable {
216
216
  return 'B' + base32Encode(this._value, 'RFC4648');
217
217
  }
218
218
 
219
- truncate(length = undefined): string {
219
+ truncate(length?: number): string {
220
220
  return truncateKey(this, length);
221
221
  }
222
222
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  export const randomBytes = (length: number) => {
6
6
  // globalThis.crypto is not available in Node.js when running in vitest even though the documentation says it should be.
7
- // eslint-disable-next-line @typescript-eslint/no-var-requires
7
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
8
8
  const webCrypto = globalThis.crypto ?? require('node:crypto').webcrypto;
9
9
 
10
10
  const bytes = new Uint8Array(length);
package/src/space-id.ts CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  import base32Decode from 'base32-decode';
6
6
  import base32Encode from 'base32-encode';
7
- import { Schema } from 'effect';
7
+ import * as Schema from 'effect/Schema';
8
8
 
9
9
  import { invariant } from '@dxos/invariant';
10
10
 
@@ -17,7 +17,7 @@ const MULTIBASE_PREFIX = 'B';
17
17
 
18
18
  const ENCODED_LENGTH = 33;
19
19
 
20
- const isValid = (value: string): value is SpaceId => {
20
+ const isValid = (value: unknown): value is SpaceId => {
21
21
  return typeof value === 'string' && value.startsWith(MULTIBASE_PREFIX) && value.length === ENCODED_LENGTH;
22
22
  };
23
23
 
@@ -33,7 +33,7 @@ export const SpaceId: Schema.Schema<SpaceId, string> & {
33
33
  byteLength: number;
34
34
  encode: (value: Uint8Array) => SpaceId;
35
35
  decode: (value: SpaceId) => Uint8Array;
36
- isValid: (value: string) => value is SpaceId;
36
+ isValid: (value: unknown) => value is SpaceId;
37
37
  make: (value: string) => SpaceId;
38
38
  random: () => SpaceId;
39
39
  } = class extends Schema.String.pipe(Schema.filter(isValid)) {