@dxos/keys 0.8.2-main.30e4dbb → 0.8.2-main.4c6cf53

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.
@@ -1 +1 @@
1
- {"version":3,"file":"space-id.d.ts","sourceRoot":"","sources":["../../../src/space-id.ts"],"names":[],"mappings":"AAWA;;;;GAIG;AAEH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC;AAEnD,eAAO,MAAM,OAAO;;oBAGF,UAAU,KAAG,OAAO;oBAMpB,OAAO,KAAG,UAAU;qBAKnB,MAAM,KAAG,KAAK,IAAI,OAAO;kBAI9B,OAAO;EAGnB,CAAC"}
1
+ {"version":3,"file":"space-id.d.ts","sourceRoot":"","sources":["../../../src/space-id.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAiBhC;;;;GAIG;AAEH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC;AAEnD,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG;IACrD,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC;IACvC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,UAAU,CAAC;IACvC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,IAAI,OAAO,CAAC;IAC7C,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACjC,MAAM,EAAE,MAAM,OAAO,CAAC;CAoBvB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/keys",
3
- "version": "0.8.2-main.30e4dbb",
3
+ "version": "0.8.2-main.4c6cf53",
4
4
  "description": "Key utils and definitions.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -27,9 +27,11 @@
27
27
  "src"
28
28
  ],
29
29
  "dependencies": {
30
- "@dxos/node-std": "0.8.2-main.30e4dbb",
31
- "@dxos/debug": "0.8.2-main.30e4dbb",
32
- "@dxos/invariant": "0.8.2-main.30e4dbb"
30
+ "effect": "3.14.21",
31
+ "ulidx": "^2.3.0",
32
+ "@dxos/debug": "0.8.2-main.4c6cf53",
33
+ "@dxos/node-std": "0.8.2-main.4c6cf53",
34
+ "@dxos/invariant": "0.8.2-main.4c6cf53"
33
35
  },
34
36
  "devDependencies": {
35
37
  "base32-decode": "^1.0.0",
package/src/dxn.ts CHANGED
@@ -2,16 +2,20 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
+ import { Schema } from 'effect';
5
6
  import type { inspect, InspectOptionsStylized } from 'node:util';
6
7
 
7
8
  import { inspectCustom } from '@dxos/debug';
8
9
  import { invariant } from '@dxos/invariant';
9
10
 
10
- import type { SpaceId } from './space-id';
11
+ import { ObjectId } from './object-id';
12
+ import { SpaceId } from './space-id';
11
13
 
12
14
  /**
13
15
  * Tags for ECHO DXNs that should resolve the object ID in the local space.
14
16
  */
17
+ // TODO(dmaretskyi): Rebrand this as "unknown location" to specify objects in the same space or queue. Essentially making the DXN it a URI not URL
18
+ // TODO(dmaretskyi): "@" is a separator character in the URI spec.
15
19
  export const LOCAL_SPACE_TAG = '@';
16
20
 
17
21
  // TODO(burdon): Namespace for.
@@ -20,6 +24,8 @@ export const QueueSubspaceTags = Object.freeze({
20
24
  TRACE: 'trace',
21
25
  });
22
26
 
27
+ export type QueueSubspaceTag = (typeof QueueSubspaceTags)[keyof typeof QueueSubspaceTags];
28
+
23
29
  /**
24
30
  * DXN unambiguously names a resource like an ECHO object, schema definition, plugin, etc.
25
31
  * Each DXN starts with a dxn prefix, followed by a resource kind.
@@ -37,6 +43,18 @@ export const QueueSubspaceTags = Object.freeze({
37
43
  * ```
38
44
  */
39
45
  export class DXN {
46
+ // TODO(dmaretskyi): Should this be a transformation into the DXN type?
47
+ static Schema = Schema.NonEmptyString.pipe(
48
+ Schema.pattern(/^dxn:([^:]+):(?:[^:]+:?)+[^:]$/),
49
+ // TODO(dmaretskyi): To set the format we need to move the annotation IDs out of the echo-schema package.
50
+ // FormatAnnotation.set(FormatEnum.DXN),
51
+ Schema.annotations({
52
+ title: 'DXN',
53
+ description: 'DXN URI',
54
+ examples: ['dxn:type:example.com/type/MyType', 'dxn:echo:@:01J00J9B45YHYSGZQTQMSKMGJ6'],
55
+ }),
56
+ );
57
+
40
58
  /**
41
59
  * Kind constants.
42
60
  */
@@ -63,12 +81,16 @@ export class DXN {
63
81
  QUEUE: 'queue',
64
82
  });
65
83
 
66
- static equals(a: DXN, b: DXN) {
84
+ get kind() {
85
+ return this.#kind;
86
+ }
87
+
88
+ static equals(a: DXN, b: DXN): boolean {
67
89
  return a.kind === b.kind && a.parts.length === b.parts.length && a.parts.every((part, i) => part === b.parts[i]);
68
90
  }
69
91
 
70
92
  // TODO(burdon): Rename isValid.
71
- static isDXNString(dxn: string) {
93
+ static isDXNString(dxn: string): boolean {
72
94
  return dxn.startsWith('dxn:');
73
95
  }
74
96
 
@@ -101,7 +123,7 @@ export class DXN {
101
123
  /**
102
124
  * @example `dxn:type:example.com/type/Contact`
103
125
  */
104
- static fromTypename(typename: string) {
126
+ static fromTypename(typename: string): DXN {
105
127
  return new DXN(DXN.kind.TYPE, [typename]);
106
128
  }
107
129
 
@@ -109,17 +131,25 @@ export class DXN {
109
131
  * @example `dxn:type:example.com/type/Contact:0.1.0`
110
132
  */
111
133
  // TODO(dmaretskyi): Consider using @ as the version separator.
112
- static fromTypenameAndVersion(typename: string, version: string) {
134
+ static fromTypenameAndVersion(typename: string, version: string): DXN {
113
135
  return new DXN(DXN.kind.TYPE, [typename, version]);
114
136
  }
115
137
 
116
138
  /**
117
139
  * @example `dxn:echo:@:01J00J9B45YHYSGZQTQMSKMGJ6`
118
140
  */
119
- static fromLocalObjectId(id: string) {
141
+ static fromLocalObjectId(id: string): DXN {
120
142
  return new DXN(DXN.kind.ECHO, [LOCAL_SPACE_TAG, id]);
121
143
  }
122
144
 
145
+ static fromQueue(subspaceTag: QueueSubspaceTag, spaceId: SpaceId, queueId: ObjectId, objectId?: ObjectId) {
146
+ invariant(SpaceId.isValid(spaceId));
147
+ invariant(ObjectId.isValid(queueId));
148
+ invariant(!objectId || ObjectId.isValid(objectId));
149
+
150
+ return new DXN(DXN.kind.QUEUE, [subspaceTag, spaceId, queueId, ...(objectId ? [objectId] : [])]);
151
+ }
152
+
123
153
  #kind: string;
124
154
  #parts: string[];
125
155
 
@@ -145,10 +175,6 @@ export class DXN {
145
175
  this.#parts = parts;
146
176
  }
147
177
 
148
- get kind() {
149
- return this.#kind;
150
- }
151
-
152
178
  get parts() {
153
179
  return this.#parts;
154
180
  }
@@ -159,11 +185,11 @@ export class DXN {
159
185
  return this.#parts[0];
160
186
  }
161
187
 
162
- hasTypenameOf(typename: string) {
188
+ hasTypenameOf(typename: string): boolean {
163
189
  return this.#kind === DXN.kind.TYPE && this.#parts.length === 1 && this.#parts[0] === typename;
164
190
  }
165
191
 
166
- isLocalObjectId() {
192
+ isLocalObjectId(): boolean {
167
193
  return this.#kind === DXN.kind.ECHO && this.#parts[0] === LOCAL_SPACE_TAG && this.#parts.length === 2;
168
194
  }
169
195
 
@@ -216,7 +242,7 @@ export class DXN {
216
242
  /**
217
243
  * Used by Node.js to get textual representation of this object when it's printed with a `console.log` statement.
218
244
  */
219
- [inspectCustom](depth: number, options: InspectOptionsStylized, inspectFn: typeof inspect) {
245
+ [inspectCustom](depth: number, options: InspectOptionsStylized, inspectFn: typeof inspect): string {
220
246
  const printControlCode = (code: number) => {
221
247
  return `\x1b[${code}m`;
222
248
  };
@@ -227,6 +253,29 @@ export class DXN {
227
253
  }
228
254
  }
229
255
 
256
+ // TODO(dmaretskyi): Fluent API:
257
+ /*
258
+ class DXN {
259
+ ...
260
+ isEchoDXN(): this is EchoDXN {
261
+ return this.#kind === DXN.kind.ECHO;
262
+ }
263
+ ...
264
+ }
265
+
266
+ interface EchoDXN extends DXN {
267
+ objectId: ObjectId;
268
+ }
269
+
270
+ declare const dxn: DXN;
271
+
272
+ dxn.objectId
273
+
274
+ if(dxn.isEchoDXN()) {
275
+ dxn.objectId
276
+ }
277
+ ```
278
+
230
279
  /**
231
280
  * API namespace.
232
281
  */
package/src/index.ts CHANGED
@@ -7,3 +7,4 @@ export * from './public-key';
7
7
  export * from './types';
8
8
  export * from './space-id';
9
9
  export * from './dxn';
10
+ export * from './object-id';
@@ -0,0 +1,42 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Schema } from 'effect';
6
+ import { ulid } from 'ulidx';
7
+
8
+ // TODO(dmaretskyi): Make brand.
9
+ // export const ObjectIdBrand: unique symbol = Symbol('@dxos/echo/ObjectId');
10
+ // export const ObjectIdSchema = Schema.ULID.pipe(S.brand(ObjectIdBrand));
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',
13
+ pattern: '^[0-7][0-9A-HJKMNP-TV-Z]{25}$',
14
+ });
15
+
16
+ export type ObjectId = typeof ObjectIdSchema.Type;
17
+
18
+ export interface ObjectIdClass extends Schema.SchemaClass<ObjectId, string> {
19
+ isValid(id: string): id is ObjectId;
20
+ make(id: string): ObjectId;
21
+ random(): ObjectId;
22
+ }
23
+
24
+ /**
25
+ * Randomly generated unique identifier for an object.
26
+ *
27
+ * Follows ULID spec.
28
+ */
29
+ export const ObjectId: ObjectIdClass = class extends ObjectIdSchema {
30
+ static isValid(id: string): id is ObjectId {
31
+ try {
32
+ Schema.decodeSync(ObjectId)(id);
33
+ return true;
34
+ } catch (err) {
35
+ return false;
36
+ }
37
+ }
38
+
39
+ static random(): ObjectId {
40
+ return ulid() as ObjectId;
41
+ }
42
+ };
package/src/public-key.ts CHANGED
@@ -90,7 +90,7 @@ export class PublicKey implements Equatable {
90
90
  /**
91
91
  * Creates new instance of PublicKey from hex string.
92
92
  */
93
- static fromHex(hex: string) {
93
+ static fromHex(hex: string): PublicKey {
94
94
  if (hex.startsWith('0x')) {
95
95
  hex = hex.slice(2);
96
96
  }
@@ -139,7 +139,7 @@ export class PublicKey implements Equatable {
139
139
  /**
140
140
  * Tests two keys for equality.
141
141
  */
142
- static equals(left: PublicKeyLike, right: PublicKeyLike) {
142
+ static equals(left: PublicKeyLike, right: PublicKeyLike): boolean {
143
143
  return PublicKey.from(left).equals(right);
144
144
  }
145
145
 
@@ -196,7 +196,7 @@ export class PublicKey implements Equatable {
196
196
  return this.toHex();
197
197
  }
198
198
 
199
- toJSON() {
199
+ toJSON(): string {
200
200
  return this.toHex();
201
201
  }
202
202
 
@@ -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) {
219
+ truncate(length = undefined): string {
220
220
  return truncateKey(this, length);
221
221
  }
222
222
 
@@ -228,14 +228,14 @@ export class PublicKey implements Equatable {
228
228
  return this._value;
229
229
  }
230
230
 
231
- getInsecureHash(modulo: number) {
231
+ getInsecureHash(modulo: number): number {
232
232
  return Math.abs(this._value.reduce((acc, val) => (acc ^ val) | 0, 0)) % modulo;
233
233
  }
234
234
 
235
235
  /**
236
236
  * Used by Node.js to get textual representation of this object when it's printed with a `console.log` statement.
237
237
  */
238
- [inspectCustom](depth: number, options: InspectOptionsStylized, inspectFn: typeof inspect) {
238
+ [inspectCustom](depth: number, options: InspectOptionsStylized, inspectFn: typeof inspect): string {
239
239
  if (!options.colors || typeof process.stdout.hasColors !== 'function' || !process.stdout.hasColors()) {
240
240
  return `<PublicKey ${this.truncate()}>`;
241
241
  }
@@ -302,7 +302,7 @@ export class PublicKey implements Equatable {
302
302
  /**
303
303
  * Test this key for equality with some other key.
304
304
  */
305
- equals(other: PublicKeyLike) {
305
+ equals(other: PublicKeyLike): boolean {
306
306
  const otherConverted = PublicKey.from(other);
307
307
  if (this._value.length !== otherConverted._value.length) {
308
308
  return false;
@@ -316,7 +316,7 @@ export class PublicKey implements Equatable {
316
316
  return equal;
317
317
  }
318
318
 
319
- [equalsSymbol](other: any) {
319
+ [equalsSymbol](other: any): boolean {
320
320
  if (!PublicKey.isPublicKey(other)) {
321
321
  return false;
322
322
  }
package/src/space-id.ts CHANGED
@@ -4,11 +4,23 @@
4
4
 
5
5
  import base32Decode from 'base32-decode';
6
6
  import base32Encode from 'base32-encode';
7
+ import { Schema } from 'effect';
7
8
 
8
9
  import { invariant } from '@dxos/invariant';
9
10
 
10
11
  import { randomBytes } from './random-bytes';
11
12
 
13
+ /**
14
+ * Denotes RFC4648 base-32 format.
15
+ */
16
+ const MULTIBASE_PREFIX = 'B';
17
+
18
+ const ENCODED_LENGTH = 33;
19
+
20
+ const isValid = (value: string): value is SpaceId => {
21
+ return typeof value === 'string' && value.startsWith(MULTIBASE_PREFIX) && value.length === ENCODED_LENGTH;
22
+ };
23
+
12
24
  /**
13
25
  * A unique identifier for a space.
14
26
  * Space keys are generated by creating a keypair, and then taking the first 20 bytes of the SHA-256 hash of the public key and encoding them to multibase RFC4648 base-32 format (prefixed with B, see Multibase Table).
@@ -17,32 +29,30 @@ import { randomBytes } from './random-bytes';
17
29
  // TODO(burdon): Use effect branded type?
18
30
  export type SpaceId = string & { __SpaceId: true };
19
31
 
20
- export const SpaceId = Object.freeze({
21
- byteLength: 20,
22
-
23
- encode: (value: Uint8Array): SpaceId => {
32
+ export const SpaceId: Schema.Schema<SpaceId, string> & {
33
+ byteLength: number;
34
+ encode: (value: Uint8Array) => SpaceId;
35
+ decode: (value: SpaceId) => Uint8Array;
36
+ isValid: (value: string) => value is SpaceId;
37
+ make: (value: string) => SpaceId;
38
+ random: () => SpaceId;
39
+ } = class extends Schema.String.pipe(Schema.filter(isValid)) {
40
+ static byteLength = 20;
41
+
42
+ static encode = (value: Uint8Array): SpaceId => {
24
43
  invariant(value instanceof Uint8Array, 'Invalid type');
25
44
  invariant(value.length === SpaceId.byteLength, 'Invalid length');
26
45
  return (MULTIBASE_PREFIX + base32Encode(value, 'RFC4648')) as SpaceId;
27
- },
46
+ };
28
47
 
29
- decode: (value: SpaceId): Uint8Array => {
48
+ static decode = (value: SpaceId): Uint8Array => {
30
49
  invariant(value.startsWith(MULTIBASE_PREFIX), 'Invalid multibase32 encoding');
31
50
  return new Uint8Array(base32Decode(value.slice(1), 'RFC4648'));
32
- },
51
+ };
33
52
 
34
- isValid: (value: string): value is SpaceId => {
35
- return typeof value === 'string' && value.startsWith(MULTIBASE_PREFIX) && value.length === ENCODED_LENGTH;
36
- },
53
+ static isValid = isValid;
37
54
 
38
- random: (): SpaceId => {
55
+ static random = (): SpaceId => {
39
56
  return SpaceId.encode(randomBytes(SpaceId.byteLength));
40
- },
41
- });
42
-
43
- /**
44
- * Denotes RFC4648 base-32 format.
45
- */
46
- const MULTIBASE_PREFIX = 'B';
47
-
48
- const ENCODED_LENGTH = 33;
57
+ };
58
+ };