jazz-tools 0.7.0-alpha.4 → 0.7.0-alpha.42

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. package/.eslintrc.cjs +3 -10
  2. package/.prettierrc.js +9 -0
  3. package/.turbo/turbo-build.log +3 -19
  4. package/.turbo/turbo-lint.log +4 -0
  5. package/.turbo/turbo-test.log +140 -0
  6. package/CHANGELOG.md +252 -0
  7. package/README.md +10 -2
  8. package/dist/coValues/account.js +104 -50
  9. package/dist/coValues/account.js.map +1 -1
  10. package/dist/coValues/coList.js +165 -112
  11. package/dist/coValues/coList.js.map +1 -1
  12. package/dist/coValues/coMap.js +243 -163
  13. package/dist/coValues/coMap.js.map +1 -1
  14. package/dist/coValues/coStream.js +256 -73
  15. package/dist/coValues/coStream.js.map +1 -1
  16. package/dist/coValues/deepLoading.js +57 -0
  17. package/dist/coValues/deepLoading.js.map +1 -0
  18. package/dist/coValues/extensions/imageDef.js +14 -8
  19. package/dist/coValues/extensions/imageDef.js.map +1 -1
  20. package/dist/coValues/group.js +49 -38
  21. package/dist/coValues/group.js.map +1 -1
  22. package/dist/coValues/interfaces.js +66 -26
  23. package/dist/coValues/interfaces.js.map +1 -1
  24. package/dist/implementation/devtoolsFormatters.js +114 -0
  25. package/dist/implementation/devtoolsFormatters.js.map +1 -0
  26. package/dist/implementation/refs.js +60 -19
  27. package/dist/implementation/refs.js.map +1 -1
  28. package/dist/implementation/schema.js +44 -1
  29. package/dist/implementation/schema.js.map +1 -1
  30. package/dist/implementation/subscriptionScope.js +19 -1
  31. package/dist/implementation/subscriptionScope.js.map +1 -1
  32. package/dist/implementation/symbols.js +5 -0
  33. package/dist/implementation/symbols.js.map +1 -0
  34. package/dist/index.js +4 -5
  35. package/dist/index.js.map +1 -1
  36. package/dist/internal.js +4 -1
  37. package/dist/internal.js.map +1 -1
  38. package/dist/tests/coList.test.js +51 -52
  39. package/dist/tests/coList.test.js.map +1 -1
  40. package/dist/tests/coMap.test.js +196 -75
  41. package/dist/tests/coMap.test.js.map +1 -1
  42. package/dist/tests/coStream.test.js +95 -85
  43. package/dist/tests/coStream.test.js.map +1 -1
  44. package/dist/tests/deepLoading.test.js +188 -0
  45. package/dist/tests/deepLoading.test.js.map +1 -0
  46. package/dist/tests/groupsAndAccounts.test.js +83 -0
  47. package/dist/tests/groupsAndAccounts.test.js.map +1 -0
  48. package/package.json +17 -9
  49. package/src/coValues/account.ts +184 -153
  50. package/src/coValues/coList.ts +220 -173
  51. package/src/coValues/coMap.ts +322 -312
  52. package/src/coValues/coStream.ts +397 -135
  53. package/src/coValues/deepLoading.ts +215 -0
  54. package/src/coValues/extensions/imageDef.ts +16 -17
  55. package/src/coValues/group.ts +95 -111
  56. package/src/coValues/interfaces.ts +217 -115
  57. package/src/implementation/devtoolsFormatters.ts +110 -0
  58. package/src/implementation/inspect.ts +1 -1
  59. package/src/implementation/refs.ts +91 -38
  60. package/src/implementation/schema.ts +87 -46
  61. package/src/implementation/subscriptionScope.ts +44 -12
  62. package/src/implementation/symbols.ts +11 -0
  63. package/src/index.ts +13 -9
  64. package/src/internal.ts +6 -2
  65. package/src/tests/coList.test.ts +67 -66
  66. package/src/tests/coMap.test.ts +226 -123
  67. package/src/tests/coStream.test.ts +141 -131
  68. package/src/tests/deepLoading.test.ts +301 -0
  69. package/src/tests/groupsAndAccounts.test.ts +91 -0
@@ -1,207 +1,299 @@
1
- import { Effect, Sink, Stream } from "effect";
1
+ import { Effect, Option, Sink, Stream } from "effect";
2
2
  import type { CojsonInternalTypes, RawCoValue } from "cojson";
3
3
  import { RawAccount } from "cojson";
4
- import type { Me, UnavailableError } from "../internal.js";
4
+ import type {
5
+ DeeplyLoaded,
6
+ DepthsIn,
7
+ UnavailableError,
8
+ } from "../internal.js";
5
9
  import {
6
10
  Account,
7
11
  AccountCtx,
8
12
  Group,
9
13
  SubscriptionScope,
10
- ValueRef,
14
+ Ref,
11
15
  inspect,
16
+ subscriptionsScopes,
12
17
  } from "../internal.js";
18
+ import { fulfillsDepth } from "./deepLoading.js";
13
19
 
14
- export type SubclassedConstructor<T> = {
20
+ export type ClassOf<T> = {
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
22
  new (...args: any[]): T;
16
23
  };
17
24
 
18
- export interface CoValueClass<
19
- Value extends CoValue = CoValue,
20
- Init = any,
21
- > {
22
- /** @category Construction and loading */
25
+ /** @category Abstract interfaces */
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
+ export interface CoValueClass<Value extends CoValue = CoValue, Init = any> {
23
28
  new (init: Init, options: { owner: Account | Group }): Value;
24
29
 
25
30
  /** @ignore */
26
31
  fromRaw(raw: Value["_raw"]): Value;
27
32
 
28
- /** @category Construction and loading */
29
- load<V extends Value>(
30
- this: SubclassedConstructor<V>,
33
+ /** @category Subscription & Loading */
34
+ load<V extends Value, Depth>(
35
+ this: ClassOf<V>,
31
36
  id: ID<V>,
32
- options: {
33
- as: Account & Me;
34
- onProgress?: (progress: number) => void;
35
- }
36
- ): Promise<V | undefined>;
37
+ as: Account,
38
+ depth: Depth & DepthsIn<V>,
39
+ ): Promise<DeeplyLoaded<V, Depth> | undefined>;
40
+ /** @category Subscription & Loading */
41
+ load<V extends Value, Depth>(
42
+ this: ClassOf<V>,
43
+ existing: V,
44
+ depth: Depth & DepthsIn<V>,
45
+ ): Promise<DeeplyLoaded<V, Depth> | undefined>;
37
46
 
38
- /** @category Construction and loading */
39
- loadEf<V extends Value>(
40
- this: SubclassedConstructor<V>,
41
- id: ID<V>
42
- ): Effect.Effect<V, UnavailableError, AccountCtx>;
47
+ /** @category Subscription & Loading */
48
+ loadEf<V extends Value, Depth>(
49
+ this: ClassOf<V>,
50
+ id: ID<V>,
51
+ depth: Depth & DepthsIn<V>,
52
+ ): Effect.Effect<DeeplyLoaded<V, Depth>, UnavailableError, AccountCtx>;
43
53
 
44
- /** @category Subscription */
45
- subscribe<V extends Value, Acc extends Account>(
46
- this: SubclassedConstructor<V>,
54
+ /** @category Subscription & Loading */
55
+ subscribe<V extends Value, Depth>(
56
+ this: ClassOf<V>,
47
57
  id: ID<V>,
48
- options: { as: Acc & Me },
49
- onUpdate: (value: V) => void
58
+ as: Account,
59
+ depth: Depth & DepthsIn<V>,
60
+ listener: (value: DeeplyLoaded<V, Depth>) => void,
61
+ ): () => void;
62
+ subscribe<V extends Value, Depth>(
63
+ this: ClassOf<V>,
64
+ existing: V,
65
+ depth: Depth & DepthsIn<V>,
66
+ listener: (value: DeeplyLoaded<V, Depth>) => void,
50
67
  ): () => void;
51
68
 
52
- /** @category Subscription */
53
- subscribeEf<V extends Value>(
54
- this: SubclassedConstructor<V>,
55
- id: ID<V>
56
- ): Stream.Stream<V, UnavailableError, AccountCtx>;
69
+ /** @category Subscription & Loading */
70
+ subscribeEf<V extends Value, Depth>(
71
+ this: ClassOf<V>,
72
+ id: ID<V>,
73
+ depth: Depth & DepthsIn<V>,
74
+ ): Stream.Stream<DeeplyLoaded<V, Depth>, UnavailableError, AccountCtx>;
57
75
  }
58
76
 
59
- /** @category Schemas & CoValues - Abstract interfaces */
77
+ /** @category Abstract interfaces */
78
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
79
  export interface CoValue<Type extends string = string, Raw = any> {
61
- /** @category Value identity */
80
+ /** @category Content */
62
81
  readonly id: ID<this>;
63
- /** @category Value identity */
82
+ /** @category Type Helpers */
64
83
  _type: Type;
65
84
  /** @category Collaboration */
66
85
  _owner: Account | Group;
67
- /** @category Subscription */
68
- subscribe(listener: (update: this) => void): () => void;
69
- /** @category Subscription */
70
- subscribeEf(): Stream.Stream<this, UnavailableError, never>;
71
86
  /** @category Internals */
72
87
  _raw: Raw;
73
- /** @category Internals */
74
- readonly _loadedAs: Account & Me;
75
- /** @category Stringifying & inspection */
88
+ /** @internal */
89
+ readonly _loadedAs: Account;
90
+ /** @category Stringifying & Inspection */
91
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
76
92
  toJSON(): any[] | object;
77
- /** @category Stringifying & inspection */
93
+ /** @category Stringifying & Inspection */
94
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
95
  [inspect](): any;
79
96
  }
80
97
 
98
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
99
  export function isCoValue(value: any): value is CoValue {
82
100
  return value && value._type !== undefined;
83
101
  }
84
102
 
85
- /** @category Schemas & CoValues - Abstract interfaces */
86
- export type ID<T> = CojsonInternalTypes.RawCoID & {
87
- readonly __type: T;
88
- };
103
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
104
+ export function isCoValueClass(value: any): value is CoValueClass {
105
+ return typeof value === "function" && value.fromRaw !== undefined;
106
+ }
107
+
108
+ /** @category CoValues */
109
+ export type ID<T> = CojsonInternalTypes.RawCoID & IDMarker<T>;
89
110
 
111
+ type IDMarker<out T> = { __type(_: never): T };
112
+
113
+ /** @internal */
90
114
  export class CoValueBase implements CoValue {
91
115
  id!: ID<this>;
92
116
  _type!: string;
93
117
  _raw!: RawCoValue;
118
+ _instanceID!: string;
94
119
 
95
120
  get _owner(): Account | Group {
96
- return this._raw.group instanceof RawAccount
97
- ? Account.fromRaw(this._raw.group)
98
- : Group.fromRaw(this._raw.group);
121
+ const owner =
122
+ this._raw.group instanceof RawAccount
123
+ ? Account.fromRaw(this._raw.group)
124
+ : Group.fromRaw(this._raw.group);
125
+
126
+ const subScope = subscriptionsScopes.get(this);
127
+ if (subScope) {
128
+ subScope.onRefAccessedOrSet(this.id, owner.id);
129
+ subscriptionsScopes.set(owner, subScope);
130
+ }
131
+
132
+ return owner;
99
133
  }
100
134
 
135
+ /** @private */
101
136
  get _loadedAs() {
102
137
  return Account.fromNode(this._raw.core.node);
103
138
  }
104
139
 
105
- constructor(..._args: any) {}
140
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
+ constructor(..._args: any) {
142
+ Object.defineProperty(this, "_instanceID", {
143
+ value: `instance-${Math.random().toString(36).slice(2)}`,
144
+ enumerable: false,
145
+ });
146
+ }
106
147
 
107
- static fromRaw<V extends CoValue>(
108
- this: SubclassedConstructor<V>,
109
- raw: RawCoValue
110
- ): V {
111
- return new this(undefined, { fromRaw: raw });
148
+ /** @category Internals */
149
+ static fromRaw<V extends CoValue>(this: ClassOf<V>, raw: RawCoValue): V {
150
+ return new this({ fromRaw: raw });
112
151
  }
113
152
 
114
- static loadEf<V extends CoValue>(
115
- this: SubclassedConstructor<V> & typeof CoValueBase,
116
- id: ID<V>
117
- ): Effect.Effect<V, UnavailableError, AccountCtx> {
118
- return Effect.gen(this, function* (_) {
119
- const account = yield* _(AccountCtx);
120
- return yield* _(new ValueRef(id as ID<V>, account, this).loadEf());
121
- });
153
+ /** @category Subscription & Loading */
154
+ static load<V extends CoValue, Depth>(
155
+ this: ClassOf<V> & typeof CoValueBase,
156
+ id: ID<V>,
157
+ as: Account,
158
+ depth: Depth & DepthsIn<V>,
159
+ ): Promise<DeeplyLoaded<V, Depth> | undefined>;
160
+ /** @category Subscription & Loading */
161
+ static load<V extends CoValue, Depth>(
162
+ this: ClassOf<V> & typeof CoValueBase,
163
+ existing: V,
164
+ depth: Depth & DepthsIn<V>,
165
+ ): Promise<DeeplyLoaded<V, Depth> | undefined>;
166
+ static load<V extends CoValue, Depth>(
167
+ this: ClassOf<V> & typeof CoValueBase,
168
+
169
+ ...args:
170
+ | [ID<V>, Account, Depth & DepthsIn<V>]
171
+ | [V, Depth & DepthsIn<V>]
172
+ ): Promise<DeeplyLoaded<V, Depth> | undefined> {
173
+ const { id, as, depth } =
174
+ args.length === 3
175
+ ? { id: args[0], as: args[1], depth: args[2] }
176
+ : { id: args[0].id, as: args[0]._loadedAs, depth: args[1] };
177
+ return Effect.runPromise(
178
+ this.loadEf(id, depth).pipe(
179
+ Effect.mapError(() => undefined),
180
+ Effect.merge,
181
+ Effect.provideService(AccountCtx, as),
182
+ ),
183
+ );
122
184
  }
123
185
 
124
- static load<V extends CoValue>(
125
- this: SubclassedConstructor<V> & typeof CoValueBase,
186
+ /** @category Subscription & Loading */
187
+ static loadEf<V extends CoValue, Depth>(
188
+ this: ClassOf<V> & typeof CoValueBase,
126
189
  id: ID<V>,
127
- options: {
128
- as: Account & Me;
129
- onProgress?: (progress: number) => void;
130
- }
131
- ): Promise<V | undefined> {
132
- return new ValueRef(id as ID<V>, options.as, this).load(
133
- options?.onProgress && { onProgress: options.onProgress }
190
+ depth: Depth & DepthsIn<V>,
191
+ ): Effect.Effect<DeeplyLoaded<V, Depth>, UnavailableError, AccountCtx> {
192
+ return this.subscribeEf(id, depth).pipe(
193
+ Stream.runHead,
194
+ Effect.andThen(
195
+ Effect.mapError((_noSuchElem) => "unavailable" as const),
196
+ ),
134
197
  );
135
198
  }
136
199
 
137
- static subscribe<V extends CoValue, Acc extends Account>(
138
- this: SubclassedConstructor<V> & typeof CoValueBase,
200
+ /** @category Subscription & Loading */
201
+ static subscribe<V extends CoValue, Depth>(
202
+ this: ClassOf<V> & typeof CoValueBase,
139
203
  id: ID<V>,
140
- options: { as: Acc & Me },
141
- onUpdate: (value: V) => void
204
+ as: Account,
205
+ depth: Depth & DepthsIn<V>,
206
+ listener: (value: DeeplyLoaded<V, Depth>) => void,
207
+ ): () => void;
208
+ static subscribe<V extends CoValue, Depth>(
209
+ this: ClassOf<V> & typeof CoValueBase,
210
+ existing: V,
211
+ depth: Depth & DepthsIn<V>,
212
+ listener: (value: DeeplyLoaded<V, Depth>) => void,
213
+ ): () => void;
214
+ static subscribe<V extends CoValue, Depth>(
215
+ this: ClassOf<V> & typeof CoValueBase,
216
+ ...args:
217
+ | [
218
+ ID<V>,
219
+ Account,
220
+ Depth & DepthsIn<V>,
221
+ (value: DeeplyLoaded<V, Depth>) => void,
222
+ ]
223
+ | [V, Depth & DepthsIn<V>, (value: DeeplyLoaded<V, Depth>) => void]
142
224
  ): () => void {
225
+ const { id, as, depth, listener } =
226
+ args.length === 4
227
+ ? {
228
+ id: args[0],
229
+ as: args[1],
230
+ depth: args[2],
231
+ listener: args[3],
232
+ }
233
+ : {
234
+ id: args[0].id,
235
+ as: args[0]._loadedAs,
236
+ depth: args[1],
237
+ listener: args[2],
238
+ };
143
239
  void Effect.runPromise(
144
240
  Effect.provideService(
145
- this.subscribeEf(id).pipe(
241
+ this.subscribeEf(id, depth).pipe(
146
242
  Stream.run(
147
243
  Sink.forEach((update) =>
148
- Effect.sync(() => onUpdate(update))
149
- )
150
- )
244
+ Effect.sync(() => listener(update)),
245
+ ),
246
+ ),
151
247
  ),
152
248
  AccountCtx,
153
- options.as as Account & Me
154
- )
249
+ as,
250
+ ),
155
251
  );
156
252
 
157
253
  return function unsubscribe() {};
158
254
  }
159
255
 
160
- static subscribeEf<V extends CoValue>(
161
- this: SubclassedConstructor<V> & typeof CoValueBase,
162
- id: ID<V>
163
- ): Stream.Stream<V, UnavailableError, AccountCtx> {
164
- return Stream.fromEffect(this.loadEf(id)).pipe(
165
- Stream.flatMap((value) =>
256
+ /** @category Subscription & Loading */
257
+ static subscribeEf<V extends CoValue, Depth>(
258
+ this: ClassOf<V> & typeof CoValueBase,
259
+ id: ID<V>,
260
+ depth: Depth & DepthsIn<V>,
261
+ ): Stream.Stream<DeeplyLoaded<V, Depth>, UnavailableError, AccountCtx> {
262
+ return AccountCtx.pipe(
263
+ Effect.andThen((account) =>
264
+ new Ref(id, account, this as CoValueClass<V>).loadEf(),
265
+ ),
266
+ Stream.fromEffect,
267
+ Stream.flatMap((value: V) =>
166
268
  Stream.asyncScoped<V, UnavailableError>((emit) =>
167
269
  Effect.gen(this, function* (_) {
168
270
  const subscription = new SubscriptionScope(
169
271
  value,
170
272
  this,
171
- (update) => {
172
- void emit.single(update as V);
173
- }
273
+ (update) => void emit.single(update as V),
174
274
  );
175
275
 
176
276
  yield* _(
177
277
  Effect.addFinalizer(() =>
178
- Effect.sync(() => subscription.unsubscribeAll())
179
- )
278
+ Effect.sync(() =>
279
+ subscription.unsubscribeAll(),
280
+ ),
281
+ ),
180
282
  );
181
- })
182
- )
183
- )
184
- );
185
- }
186
-
187
- subscribe(listener: (update: this) => void): () => void {
188
- return (this.constructor as unknown as typeof CoValueBase).subscribe(
189
- this.id as unknown as ID<CoValue>,
190
- { as: this._loadedAs },
191
- listener as unknown as (update: CoValue) => void
192
- );
193
- }
194
-
195
- subscribeEf(): Stream.Stream<this, UnavailableError, never> {
196
- return Stream.provideService(
197
- (this.constructor as unknown as typeof CoValueBase).subscribeEf(
198
- this.id as unknown as ID<CoValue>
283
+ }),
284
+ ),
285
+ ),
286
+ Stream.filterMap((update: V) =>
287
+ Option.fromNullable(
288
+ fulfillsDepth(depth, update)
289
+ ? (update as DeeplyLoaded<V, Depth>)
290
+ : undefined,
291
+ ),
199
292
  ),
200
- AccountCtx,
201
- this._loadedAs
202
- ) as unknown as Stream.Stream<this, UnavailableError, never>;
293
+ );
203
294
  }
204
295
 
296
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
205
297
  toJSON(): object | any[] {
206
298
  return {
207
299
  id: this.id,
@@ -213,4 +305,14 @@ export class CoValueBase implements CoValue {
213
305
  [inspect]() {
214
306
  return this.toJSON();
215
307
  }
308
+
309
+ /** @category Type Helpers*/
310
+ as<C extends CoValueClass>(otherSchema: C): InstanceType<C> {
311
+ const cast = otherSchema.fromRaw(this._raw) as InstanceType<C>;
312
+ const subScope = subscriptionsScopes.get(this);
313
+ if (subScope) {
314
+ subscriptionsScopes.set(cast, subScope);
315
+ }
316
+ return cast;
317
+ }
216
318
  }
@@ -0,0 +1,110 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { ItemsSym } from "./symbols.js";
3
+
4
+ (globalThis as any).devtoolsFormatters = [
5
+ {
6
+ header: (object: any) => {
7
+ if (object._type === "CoMap") {
8
+ return ["div", {}, ["span", {}, object.constructor.name]];
9
+ } else if (object._type === "CoList") {
10
+ return [
11
+ "div",
12
+ {},
13
+ [
14
+ "span",
15
+ {},
16
+ object.constructor.name + "(" + object.length + ") ",
17
+ ],
18
+ ];
19
+ } else if (object._type === "Account") {
20
+ return [
21
+ "div",
22
+ {},
23
+ [
24
+ "span",
25
+ {},
26
+ object.constructor.name +
27
+ "(" +
28
+ object._refs.profile.value?.name +
29
+ (object.isMe ? " ME" : "") +
30
+ ")",
31
+ ],
32
+ ];
33
+ } else {
34
+ return null;
35
+ }
36
+ },
37
+ hasBody: function () {
38
+ return true;
39
+ },
40
+ body: function (object: any) {
41
+ if (object._type === "CoMap" || object._type === "Account") {
42
+ return [
43
+ "div",
44
+ { style: "margin-left: 15px" },
45
+ ["div", "id: ", ["object", { object: object.id }]],
46
+ ...Object.entries(object).map(([k, v]) => [
47
+ "div",
48
+ { style: "white-space: nowrap;" },
49
+ [
50
+ "span",
51
+ { style: "font-weight: bold; opacity: 0.6" },
52
+ k,
53
+ ": ",
54
+ ],
55
+ ["object", { object: v }],
56
+ ...(typeof object._schema[k] === "function"
57
+ ? v === null
58
+ ? [
59
+ [
60
+ "span",
61
+ { style: "opacity: 0.5" },
62
+ ` (pending ${object._schema[k].name} `,
63
+ [
64
+ "object",
65
+ { object: object._refs[k] },
66
+ ],
67
+ ")",
68
+ ],
69
+ ]
70
+ : []
71
+ : []),
72
+ ]),
73
+ ];
74
+ } else if (object._type === "CoList") {
75
+ return [
76
+ "div",
77
+ { style: "margin-left: 15px" },
78
+ ["div", "id: ", ["object", { object: object.id }]],
79
+ ...(object as any[]).map((v, i) => [
80
+ "div",
81
+ { style: "white-space: nowrap;" },
82
+ [
83
+ "span",
84
+ { style: "font-weight: bold; opacity: 0.6" },
85
+ i,
86
+ ": ",
87
+ ],
88
+ ["object", { object: v }],
89
+ ...(typeof object._schema[ItemsSym] === "function"
90
+ ? v === null
91
+ ? [
92
+ [
93
+ "span",
94
+ { style: "opacity: 0.5" },
95
+ ` (pending ${object._schema[ItemsSym].name} `,
96
+ [
97
+ "object",
98
+ { object: object._refs[i] },
99
+ ],
100
+ ")",
101
+ ],
102
+ ]
103
+ : []
104
+ : []),
105
+ ]),
106
+ ];
107
+ }
108
+ },
109
+ },
110
+ ];
@@ -1,2 +1,2 @@
1
1
  export const inspect = Symbol.for("nodejs.util.inspect.custom");
2
- export type inspect = typeof inspect;
2
+ export type inspect = typeof inspect;