@atscript/moost-db 0.1.35 → 0.1.36

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/dist/index.cjs CHANGED
@@ -26,6 +26,7 @@ const __atscript_typescript_utils = __toESM(require("@atscript/typescript/utils"
26
26
  const __moostjs_event_http = __toESM(require("@moostjs/event-http"));
27
27
  const moost = __toESM(require("moost"));
28
28
  const __uniqu_url = __toESM(require("@uniqu/url"));
29
+ const __atscript_utils_db = __toESM(require("@atscript/utils-db"));
29
30
 
30
31
  //#region packages/moost-db/src/decorators.ts
31
32
  const READABLE_DEF = "__atscript_db_readable_def";
@@ -36,12 +37,21 @@ const ViewController = ReadableController;
36
37
 
37
38
  //#endregion
38
39
  //#region packages/moost-db/src/validation-interceptor.ts
40
+ const dbErrorCodeToStatus = { CONFLICT: 409 };
39
41
  function transformValidationError(error, reply) {
40
42
  if (error instanceof __atscript_typescript_utils.ValidatorError) reply(new __moostjs_event_http.HttpError(400, {
41
43
  message: error.message,
42
44
  statusCode: 400,
43
45
  errors: error.errors
44
46
  }));
47
+ else if (error instanceof __atscript_utils_db.DbError) {
48
+ const statusCode = dbErrorCodeToStatus[error.code] ?? 400;
49
+ reply(new __moostjs_event_http.HttpError(statusCode, {
50
+ message: error.message,
51
+ statusCode,
52
+ errors: error.errors
53
+ }));
54
+ }
45
55
  }
46
56
  const validationErrorTransform = () => (0, moost.defineInterceptor)({ error: transformValidationError }, moost.TInterceptorPriority.CATCH_ERROR);
47
57
  const UseValidationErrorTransform = () => (0, moost.Intercept)(validationErrorTransform());
@@ -205,6 +215,18 @@ var AsDbReadableController = class {
205
215
  const insightsError = this.validateInsights(parsed.insights);
206
216
  if (insightsError) return new __moostjs_event_http.HttpError(400, insightsError);
207
217
  }
218
+ const withRelations = parsed.controls.$with;
219
+ if (withRelations?.length) {
220
+ const relations = this.readable.relations;
221
+ for (const rel of withRelations) if (!rel.name.includes(".") && !relations.has(rel.name)) return new __moostjs_event_http.HttpError(400, {
222
+ message: `Unknown relation "${rel.name}" in $with. Available relations: ${[...relations.keys()].join(", ") || "(none)"}`,
223
+ statusCode: 400,
224
+ errors: [{
225
+ path: "$with",
226
+ message: `Unknown relation "${rel.name}"`
227
+ }]
228
+ });
229
+ }
208
230
  return undefined;
209
231
  }
210
232
  /**
@@ -227,6 +249,38 @@ var AsDbReadableController = class {
227
249
  return item;
228
250
  }
229
251
  /**
252
+ * Extracts a composite identifier object from query params.
253
+ * Tries composite primary key first, then compound unique indexes.
254
+ */ extractCompositeId(query) {
255
+ const pkFields = this.readable.primaryKeys;
256
+ if (pkFields.length > 1) {
257
+ const idObj = {};
258
+ let allPresent = true;
259
+ for (const field of pkFields) {
260
+ if (query[field] === undefined) {
261
+ allPresent = false;
262
+ break;
263
+ }
264
+ idObj[field] = query[field];
265
+ }
266
+ if (allPresent) return idObj;
267
+ }
268
+ for (const index of this.readable.indexes.values()) {
269
+ if (index.type !== "unique" || index.fields.length < 2) continue;
270
+ const idObj = {};
271
+ let allPresent = true;
272
+ for (const indexField of index.fields) {
273
+ if (query[indexField.name] === undefined) {
274
+ allPresent = false;
275
+ break;
276
+ }
277
+ idObj[indexField.name] = query[indexField.name];
278
+ }
279
+ if (allPresent) return idObj;
280
+ }
281
+ return new __moostjs_event_http.HttpError(400, "Query params do not match any composite primary key or compound unique index");
282
+ }
283
+ /**
230
284
  * **GET /query** — returns an array of records or a count.
231
285
  */ async query(url) {
232
286
  const parsed = this.parseQueryString(url);
@@ -310,6 +364,20 @@ else result = await this.readable.findManyWithCount(query);
310
364
  return this.returnOne(this.readable.findById(id, { controls }));
311
365
  }
312
366
  /**
367
+ * **GET /one?field1=val1&field2=val2** — retrieves a single record by composite key
368
+ * (composite primary key or compound unique index).
369
+ */ async getOneComposite(query, url) {
370
+ const idObj = this.extractCompositeId(query);
371
+ if (idObj instanceof __moostjs_event_http.HttpError) return idObj;
372
+ const parsed = this.parseQueryString(url);
373
+ const select = this.transformProjection(parsed.controls.$select);
374
+ const controls = {
375
+ ...parsed.controls,
376
+ $select: select
377
+ };
378
+ return this.returnOne(this.readable.findById(idObj, { controls }));
379
+ }
380
+ /**
313
381
  * **GET /meta** — returns table/view metadata for UI.
314
382
  */ meta() {
315
383
  return {
@@ -364,6 +432,14 @@ _ts_decorate$1([
364
432
  _ts_metadata$1("design:paramtypes", [String, String]),
365
433
  _ts_metadata$1("design:returntype", Promise)
366
434
  ], AsDbReadableController.prototype, "getOne", null);
435
+ _ts_decorate$1([
436
+ (0, __moostjs_event_http.Get)("one"),
437
+ _ts_param$1(0, (0, __moostjs_event_http.Query)()),
438
+ _ts_param$1(1, (0, __moostjs_event_http.Url)()),
439
+ _ts_metadata$1("design:type", Function),
440
+ _ts_metadata$1("design:paramtypes", [typeof Record === "undefined" ? Object : Record, String]),
441
+ _ts_metadata$1("design:returntype", Promise)
442
+ ], AsDbReadableController.prototype, "getOneComposite", null);
367
443
  _ts_decorate$1([
368
444
  (0, __moostjs_event_http.Get)("meta"),
369
445
  _ts_metadata$1("design:type", Function),
@@ -410,26 +486,35 @@ var AsDbController = class extends AsDbReadableController {
410
486
  /**
411
487
  * **POST /** — inserts one or many records.
412
488
  */ async insert(payload) {
413
- const arr = Array.isArray(payload) ? payload : [payload];
414
- if (arr.length === 1) {
415
- const data$1 = await this.onWrite("insert", arr[0]);
489
+ if (Array.isArray(payload)) {
490
+ const data$1 = await this.onWrite("insertMany", payload);
416
491
  if (data$1 === undefined) return new __moostjs_event_http.HttpError(500, "Not saved");
417
- return await this.table.insertOne(data$1);
492
+ return await this.table.insertMany(data$1);
418
493
  }
419
- const data = await this.onWrite("insertMany", arr);
494
+ const data = await this.onWrite("insert", payload);
420
495
  if (data === undefined) return new __moostjs_event_http.HttpError(500, "Not saved");
421
- return await this.table.insertMany(data);
496
+ return await this.table.insertOne(data);
422
497
  }
423
498
  /**
424
- * **PUT /** — fully replaces a record matched by primary key.
499
+ * **PUT /** — fully replaces one or many records matched by primary key.
425
500
  */ async replace(payload) {
501
+ if (Array.isArray(payload)) {
502
+ const data$1 = await this.onWrite("replaceMany", payload);
503
+ if (data$1 === undefined) return new __moostjs_event_http.HttpError(500, "Not saved");
504
+ return await this.table.bulkReplace(data$1);
505
+ }
426
506
  const data = await this.onWrite("replace", payload);
427
507
  if (data === undefined) return new __moostjs_event_http.HttpError(500, "Not saved");
428
508
  return await this.table.replaceOne(data);
429
509
  }
430
510
  /**
431
- * **PATCH /** — partially updates a record matched by primary key.
511
+ * **PATCH /** — partially updates one or many records matched by primary key.
432
512
  */ async update(payload) {
513
+ if (Array.isArray(payload)) {
514
+ const data$1 = await this.onWrite("updateMany", payload);
515
+ if (data$1 === undefined) return new __moostjs_event_http.HttpError(500, "Not saved");
516
+ return await this.table.bulkUpdate(data$1);
517
+ }
433
518
  const data = await this.onWrite("update", payload);
434
519
  if (data === undefined) return new __moostjs_event_http.HttpError(500, "Not saved");
435
520
  return await this.table.updateOne(data);
@@ -443,6 +528,18 @@ var AsDbController = class extends AsDbReadableController {
443
528
  if (result.deletedCount < 1) return new __moostjs_event_http.HttpError(404);
444
529
  return result;
445
530
  }
531
+ /**
532
+ * **DELETE /?field1=val1&field2=val2** — removes a record by composite key
533
+ * (composite primary key or compound unique index).
534
+ */ async removeComposite(query) {
535
+ const idObj = this.extractCompositeId(query);
536
+ if (idObj instanceof __moostjs_event_http.HttpError) return idObj;
537
+ const resolvedId = await this.onRemove(idObj);
538
+ if (resolvedId === undefined) return new __moostjs_event_http.HttpError(500, "Not deleted");
539
+ const result = await this.table.deleteOne(resolvedId);
540
+ if (result.deletedCount < 1) return new __moostjs_event_http.HttpError(404);
541
+ return result;
542
+ }
446
543
  constructor(table, app) {
447
544
  super(table, app);
448
545
  }
@@ -475,6 +572,13 @@ _ts_decorate([
475
572
  _ts_metadata("design:paramtypes", [String]),
476
573
  _ts_metadata("design:returntype", Promise)
477
574
  ], AsDbController.prototype, "remove", null);
575
+ _ts_decorate([
576
+ (0, __moostjs_event_http.Delete)(""),
577
+ _ts_param(0, (0, __moostjs_event_http.Query)()),
578
+ _ts_metadata("design:type", Function),
579
+ _ts_metadata("design:paramtypes", [typeof Record === "undefined" ? Object : Record]),
580
+ _ts_metadata("design:returntype", Promise)
581
+ ], AsDbController.prototype, "removeComposite", null);
478
582
  AsDbController = _ts_decorate([
479
583
  (0, moost.Inherit)(),
480
584
  _ts_param(0, (0, moost.Inject)(TABLE_DEF)),
package/dist/index.d.ts CHANGED
@@ -47,6 +47,11 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
47
47
  protected transformProjection(projection?: UniqueryControls['$select']): UniqueryControls['$select'] | undefined;
48
48
  protected parseQueryString(url: string): _uniqu_url.UrlQuery;
49
49
  protected returnOne(result: Promise<DataType | null>): Promise<DataType | HttpError>;
50
+ /**
51
+ * Extracts a composite identifier object from query params.
52
+ * Tries composite primary key first, then compound unique indexes.
53
+ */
54
+ protected extractCompositeId(query: Record<string, string>): Record<string, unknown> | HttpError;
50
55
  /**
51
56
  * **GET /query** — returns an array of records or a count.
52
57
  */
@@ -65,6 +70,11 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
65
70
  * **GET /one/:id** — retrieves a single record by ID or unique property.
66
71
  */
67
72
  getOne(id: string, url: string): Promise<DataType | HttpError>;
73
+ /**
74
+ * **GET /one?field1=val1&field2=val2** — retrieves a single record by composite key
75
+ * (composite primary key or compound unique index).
76
+ */
77
+ getOneComposite(query: Record<string, string>, url: string): Promise<DataType | HttpError>;
68
78
  /**
69
79
  * **GET /meta** — returns table/view metadata for UI.
70
80
  */
@@ -92,7 +102,7 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
92
102
  /**
93
103
  * Intercepts write operations. Return `undefined` to abort.
94
104
  */
95
- protected onWrite(action: 'insert' | 'insertMany' | 'replace' | 'update', data: unknown): unknown | Promise<unknown | undefined>;
105
+ protected onWrite(action: 'insert' | 'insertMany' | 'replace' | 'replaceMany' | 'update' | 'updateMany', data: unknown): unknown | Promise<unknown | undefined>;
96
106
  /**
97
107
  * Intercepts delete operations. Return `undefined` to abort.
98
108
  */
@@ -102,17 +112,22 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
102
112
  */
103
113
  insert(payload: unknown): Promise<HttpError | unknown>;
104
114
  /**
105
- * **PUT /** — fully replaces a record matched by primary key.
115
+ * **PUT /** — fully replaces one or many records matched by primary key.
106
116
  */
107
117
  replace(payload: unknown): Promise<HttpError | unknown>;
108
118
  /**
109
- * **PATCH /** — partially updates a record matched by primary key.
119
+ * **PATCH /** — partially updates one or many records matched by primary key.
110
120
  */
111
121
  update(payload: unknown): Promise<HttpError | unknown>;
112
122
  /**
113
123
  * **DELETE /:id** — removes a single record by primary key.
114
124
  */
115
125
  remove(id: string): Promise<HttpError | unknown>;
126
+ /**
127
+ * **DELETE /?field1=val1&field2=val2** — removes a record by composite key
128
+ * (composite primary key or compound unique index).
129
+ */
130
+ removeComposite(query: Record<string, string>): Promise<HttpError | unknown>;
116
131
  }
117
132
 
118
133
  /**
package/dist/index.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  import { ValidatorError, defineAnnotatedType, serializeAnnotatedType, throwFeatureDisabled } from "@atscript/typescript/utils";
2
- import { Body, Delete, Get, HttpError, Patch, Post, Put, Url } from "@moostjs/event-http";
2
+ import { Body, Delete, Get, HttpError, Patch, Post, Put, Query, Url } from "@moostjs/event-http";
3
3
  import { ApplyDecorators, Controller, Inherit, Inject, Intercept, Moost, Param, Provide, TInterceptorPriority, defineInterceptor } from "moost";
4
4
  import { parseUrl } from "@uniqu/url";
5
+ import { DbError } from "@atscript/utils-db";
5
6
 
6
7
  //#region packages/moost-db/src/decorators.ts
7
8
  const READABLE_DEF = "__atscript_db_readable_def";
@@ -12,12 +13,21 @@ const ViewController = ReadableController;
12
13
 
13
14
  //#endregion
14
15
  //#region packages/moost-db/src/validation-interceptor.ts
16
+ const dbErrorCodeToStatus = { CONFLICT: 409 };
15
17
  function transformValidationError(error, reply) {
16
18
  if (error instanceof ValidatorError) reply(new HttpError(400, {
17
19
  message: error.message,
18
20
  statusCode: 400,
19
21
  errors: error.errors
20
22
  }));
23
+ else if (error instanceof DbError) {
24
+ const statusCode = dbErrorCodeToStatus[error.code] ?? 400;
25
+ reply(new HttpError(statusCode, {
26
+ message: error.message,
27
+ statusCode,
28
+ errors: error.errors
29
+ }));
30
+ }
21
31
  }
22
32
  const validationErrorTransform = () => defineInterceptor({ error: transformValidationError }, TInterceptorPriority.CATCH_ERROR);
23
33
  const UseValidationErrorTransform = () => Intercept(validationErrorTransform());
@@ -181,6 +191,18 @@ var AsDbReadableController = class {
181
191
  const insightsError = this.validateInsights(parsed.insights);
182
192
  if (insightsError) return new HttpError(400, insightsError);
183
193
  }
194
+ const withRelations = parsed.controls.$with;
195
+ if (withRelations?.length) {
196
+ const relations = this.readable.relations;
197
+ for (const rel of withRelations) if (!rel.name.includes(".") && !relations.has(rel.name)) return new HttpError(400, {
198
+ message: `Unknown relation "${rel.name}" in $with. Available relations: ${[...relations.keys()].join(", ") || "(none)"}`,
199
+ statusCode: 400,
200
+ errors: [{
201
+ path: "$with",
202
+ message: `Unknown relation "${rel.name}"`
203
+ }]
204
+ });
205
+ }
184
206
  return undefined;
185
207
  }
186
208
  /**
@@ -203,6 +225,38 @@ var AsDbReadableController = class {
203
225
  return item;
204
226
  }
205
227
  /**
228
+ * Extracts a composite identifier object from query params.
229
+ * Tries composite primary key first, then compound unique indexes.
230
+ */ extractCompositeId(query) {
231
+ const pkFields = this.readable.primaryKeys;
232
+ if (pkFields.length > 1) {
233
+ const idObj = {};
234
+ let allPresent = true;
235
+ for (const field of pkFields) {
236
+ if (query[field] === undefined) {
237
+ allPresent = false;
238
+ break;
239
+ }
240
+ idObj[field] = query[field];
241
+ }
242
+ if (allPresent) return idObj;
243
+ }
244
+ for (const index of this.readable.indexes.values()) {
245
+ if (index.type !== "unique" || index.fields.length < 2) continue;
246
+ const idObj = {};
247
+ let allPresent = true;
248
+ for (const indexField of index.fields) {
249
+ if (query[indexField.name] === undefined) {
250
+ allPresent = false;
251
+ break;
252
+ }
253
+ idObj[indexField.name] = query[indexField.name];
254
+ }
255
+ if (allPresent) return idObj;
256
+ }
257
+ return new HttpError(400, "Query params do not match any composite primary key or compound unique index");
258
+ }
259
+ /**
206
260
  * **GET /query** — returns an array of records or a count.
207
261
  */ async query(url) {
208
262
  const parsed = this.parseQueryString(url);
@@ -286,6 +340,20 @@ else result = await this.readable.findManyWithCount(query);
286
340
  return this.returnOne(this.readable.findById(id, { controls }));
287
341
  }
288
342
  /**
343
+ * **GET /one?field1=val1&field2=val2** — retrieves a single record by composite key
344
+ * (composite primary key or compound unique index).
345
+ */ async getOneComposite(query, url) {
346
+ const idObj = this.extractCompositeId(query);
347
+ if (idObj instanceof HttpError) return idObj;
348
+ const parsed = this.parseQueryString(url);
349
+ const select = this.transformProjection(parsed.controls.$select);
350
+ const controls = {
351
+ ...parsed.controls,
352
+ $select: select
353
+ };
354
+ return this.returnOne(this.readable.findById(idObj, { controls }));
355
+ }
356
+ /**
289
357
  * **GET /meta** — returns table/view metadata for UI.
290
358
  */ meta() {
291
359
  return {
@@ -340,6 +408,14 @@ _ts_decorate$1([
340
408
  _ts_metadata$1("design:paramtypes", [String, String]),
341
409
  _ts_metadata$1("design:returntype", Promise)
342
410
  ], AsDbReadableController.prototype, "getOne", null);
411
+ _ts_decorate$1([
412
+ Get("one"),
413
+ _ts_param$1(0, Query()),
414
+ _ts_param$1(1, Url()),
415
+ _ts_metadata$1("design:type", Function),
416
+ _ts_metadata$1("design:paramtypes", [typeof Record === "undefined" ? Object : Record, String]),
417
+ _ts_metadata$1("design:returntype", Promise)
418
+ ], AsDbReadableController.prototype, "getOneComposite", null);
343
419
  _ts_decorate$1([
344
420
  Get("meta"),
345
421
  _ts_metadata$1("design:type", Function),
@@ -386,26 +462,35 @@ var AsDbController = class extends AsDbReadableController {
386
462
  /**
387
463
  * **POST /** — inserts one or many records.
388
464
  */ async insert(payload) {
389
- const arr = Array.isArray(payload) ? payload : [payload];
390
- if (arr.length === 1) {
391
- const data$1 = await this.onWrite("insert", arr[0]);
465
+ if (Array.isArray(payload)) {
466
+ const data$1 = await this.onWrite("insertMany", payload);
392
467
  if (data$1 === undefined) return new HttpError(500, "Not saved");
393
- return await this.table.insertOne(data$1);
468
+ return await this.table.insertMany(data$1);
394
469
  }
395
- const data = await this.onWrite("insertMany", arr);
470
+ const data = await this.onWrite("insert", payload);
396
471
  if (data === undefined) return new HttpError(500, "Not saved");
397
- return await this.table.insertMany(data);
472
+ return await this.table.insertOne(data);
398
473
  }
399
474
  /**
400
- * **PUT /** — fully replaces a record matched by primary key.
475
+ * **PUT /** — fully replaces one or many records matched by primary key.
401
476
  */ async replace(payload) {
477
+ if (Array.isArray(payload)) {
478
+ const data$1 = await this.onWrite("replaceMany", payload);
479
+ if (data$1 === undefined) return new HttpError(500, "Not saved");
480
+ return await this.table.bulkReplace(data$1);
481
+ }
402
482
  const data = await this.onWrite("replace", payload);
403
483
  if (data === undefined) return new HttpError(500, "Not saved");
404
484
  return await this.table.replaceOne(data);
405
485
  }
406
486
  /**
407
- * **PATCH /** — partially updates a record matched by primary key.
487
+ * **PATCH /** — partially updates one or many records matched by primary key.
408
488
  */ async update(payload) {
489
+ if (Array.isArray(payload)) {
490
+ const data$1 = await this.onWrite("updateMany", payload);
491
+ if (data$1 === undefined) return new HttpError(500, "Not saved");
492
+ return await this.table.bulkUpdate(data$1);
493
+ }
409
494
  const data = await this.onWrite("update", payload);
410
495
  if (data === undefined) return new HttpError(500, "Not saved");
411
496
  return await this.table.updateOne(data);
@@ -419,6 +504,18 @@ var AsDbController = class extends AsDbReadableController {
419
504
  if (result.deletedCount < 1) return new HttpError(404);
420
505
  return result;
421
506
  }
507
+ /**
508
+ * **DELETE /?field1=val1&field2=val2** — removes a record by composite key
509
+ * (composite primary key or compound unique index).
510
+ */ async removeComposite(query) {
511
+ const idObj = this.extractCompositeId(query);
512
+ if (idObj instanceof HttpError) return idObj;
513
+ const resolvedId = await this.onRemove(idObj);
514
+ if (resolvedId === undefined) return new HttpError(500, "Not deleted");
515
+ const result = await this.table.deleteOne(resolvedId);
516
+ if (result.deletedCount < 1) return new HttpError(404);
517
+ return result;
518
+ }
422
519
  constructor(table, app) {
423
520
  super(table, app);
424
521
  }
@@ -451,6 +548,13 @@ _ts_decorate([
451
548
  _ts_metadata("design:paramtypes", [String]),
452
549
  _ts_metadata("design:returntype", Promise)
453
550
  ], AsDbController.prototype, "remove", null);
551
+ _ts_decorate([
552
+ Delete(""),
553
+ _ts_param(0, Query()),
554
+ _ts_metadata("design:type", Function),
555
+ _ts_metadata("design:paramtypes", [typeof Record === "undefined" ? Object : Record]),
556
+ _ts_metadata("design:returntype", Promise)
557
+ ], AsDbController.prototype, "removeComposite", null);
454
558
  AsDbController = _ts_decorate([
455
559
  Inherit(),
456
560
  _ts_param(0, Inject(TABLE_DEF)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/moost-db",
3
- "version": "0.1.35",
3
+ "version": "0.1.36",
4
4
  "description": "Generic database controller for Moost with Atscript.",
5
5
  "keywords": [
6
6
  "annotations",
@@ -35,20 +35,20 @@
35
35
  "./package.json": "./package.json"
36
36
  },
37
37
  "dependencies": {
38
- "@uniqu/url": "^0.0.5"
38
+ "@uniqu/url": "^0.0.6"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@moostjs/event-http": "^0.6.2",
42
42
  "moost": "^0.6.2",
43
43
  "vitest": "3.2.4",
44
- "@atscript/core": "^0.1.35"
44
+ "@atscript/core": "^0.1.36"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@moostjs/event-http": "^0.6.2",
48
48
  "moost": "^0.6.2",
49
- "@uniqu/core": "^0.0.5",
50
- "@atscript/utils-db": "^0.1.35",
51
- "@atscript/typescript": "^0.1.35"
49
+ "@uniqu/core": "^0.0.6",
50
+ "@atscript/utils-db": "^0.1.36",
51
+ "@atscript/typescript": "^0.1.36"
52
52
  },
53
53
  "scripts": {
54
54
  "pub": "pnpm publish --access public",