@drawbridge/drawbridge-utils 0.0.48 → 0.0.50

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.
@@ -115,7 +115,15 @@ var cookieOptions = ({
115
115
  secure: process.env.NODE_ENV !== "development",
116
116
  ...expiresAt && { expires: new Date(expiresAt) }
117
117
  });
118
- var menuEndpoint = (type, id) => "/menu/" + type + (id ? "/" + id : "");
118
+ var menuEndpoint = (type, ...ids) => {
119
+ if (type === "campaign") {
120
+ const [organization, campaign] = ids;
121
+ return "/menu/organization/" + organization + "/campaign/" + campaign;
122
+ }
123
+ ;
124
+ const [id] = ids;
125
+ return "/menu/" + type + (id ? "/" + id : "");
126
+ };
119
127
  var scopes = [
120
128
  "profile:read",
121
129
  "profile:write",
@@ -74,9 +74,29 @@ const cookieOptions = ({
74
74
  });
75
75
 
76
76
  // Path builder for the API menu endpoints. Single source of truth for
77
- // the /menu/<type>(/<id>) shape — drawbridge-api routes and
78
- // drawbridge-app-web's ProviderLayout wrappers both read from here.
79
- const menuEndpoint = ( type, id ) => '/menu/' + type + ( id ? '/' + id : '' );
77
+ // the /menu/<...> shape — drawbridge-api routes and drawbridge-app-web's
78
+ // ProviderLayout wrappers both read from here.
79
+ //
80
+ // menuEndpoint('account') → /menu/account
81
+ // menuEndpoint('admin') → /menu/admin
82
+ // menuEndpoint('organization', orgId) → /menu/organization/abc
83
+ // menuEndpoint('campaign', orgId, campaignId) → /menu/organization/abc/campaign/xyz
84
+ // (campaign menus piggyback on the org URL prefix so the standard
85
+ // hasOrganizationAccess → hasCampaignAccess middleware chain works.)
86
+ const menuEndpoint = ( type, ...ids ) => {
87
+
88
+ if( type === 'campaign' ){
89
+
90
+ const [ organization, campaign ] = ids;
91
+
92
+ return '/menu/organization/' + organization + '/campaign/' + campaign;
93
+
94
+ }
95
+ const [ id ] = ids;
96
+
97
+ return '/menu/' + type + ( id ? '/' + id : '' );
98
+
99
+ };
80
100
 
81
101
  // ─────────────────────────────────────────────────────────────────────────
82
102
  // Scope vocabulary (locked — 15 scopes, 10 in the developer-allowed subset)
@@ -74,9 +74,29 @@ const cookieOptions = ({
74
74
  });
75
75
 
76
76
  // Path builder for the API menu endpoints. Single source of truth for
77
- // the /menu/<type>(/<id>) shape — drawbridge-api routes and
78
- // drawbridge-app-web's ProviderLayout wrappers both read from here.
79
- const menuEndpoint = ( type, id ) => '/menu/' + type + ( id ? '/' + id : '' );
77
+ // the /menu/<...> shape — drawbridge-api routes and drawbridge-app-web's
78
+ // ProviderLayout wrappers both read from here.
79
+ //
80
+ // menuEndpoint('account') → /menu/account
81
+ // menuEndpoint('admin') → /menu/admin
82
+ // menuEndpoint('organization', orgId) → /menu/organization/abc
83
+ // menuEndpoint('campaign', orgId, campaignId) → /menu/organization/abc/campaign/xyz
84
+ // (campaign menus piggyback on the org URL prefix so the standard
85
+ // hasOrganizationAccess → hasCampaignAccess middleware chain works.)
86
+ const menuEndpoint = ( type, ...ids ) => {
87
+
88
+ if( type === 'campaign' ){
89
+
90
+ const [ organization, campaign ] = ids;
91
+
92
+ return '/menu/organization/' + organization + '/campaign/' + campaign;
93
+
94
+ }
95
+ const [ id ] = ids;
96
+
97
+ return '/menu/' + type + ( id ? '/' + id : '' );
98
+
99
+ };
80
100
 
81
101
  // ─────────────────────────────────────────────────────────────────────────
82
102
  // Scope vocabulary (locked — 15 scopes, 10 in the developer-allowed subset)
@@ -61,7 +61,15 @@ var cookieOptions = ({
61
61
  secure: process.env.NODE_ENV !== "development",
62
62
  ...expiresAt && { expires: new Date(expiresAt) }
63
63
  });
64
- var menuEndpoint = (type, id) => "/menu/" + type + (id ? "/" + id : "");
64
+ var menuEndpoint = (type, ...ids) => {
65
+ if (type === "campaign") {
66
+ const [organization, campaign] = ids;
67
+ return "/menu/organization/" + organization + "/campaign/" + campaign;
68
+ }
69
+ ;
70
+ const [id] = ids;
71
+ return "/menu/" + type + (id ? "/" + id : "");
72
+ };
65
73
  var scopes = [
66
74
  "profile:read",
67
75
  "profile:write",
@@ -146,19 +146,20 @@ var hashToken = (raw) => hash(raw, process.env.OAUTH_TOKEN_HMAC_KEY);
146
146
  var generateToken = () => generate(32, "base64url");
147
147
 
148
148
  // lib/oauth/server.js
149
+ var SUPPORTED_GRANTS = ["authorization_code", "client_credentials", "refresh_token"];
149
150
  var createOAuthModel = ({ controller }) => ({
150
151
  getClient: async (clientId, clientSecret) => {
151
152
  var _a;
152
- const client = await controller.get({
153
+ const record = await controller.get({
153
154
  collection: "oauth",
154
155
  query: {
155
- clientId,
156
+ client: clientId,
156
157
  status: "active"
157
158
  }
158
159
  });
159
- if (!client) return false;
160
+ if (!record) return false;
160
161
  if (clientSecret) {
161
- const validSecret = (_a = client.clientSecretHashes) == null ? void 0 : _a.some((entry) => {
162
+ const validSecret = (_a = record.hashes) == null ? void 0 : _a.some((entry) => {
162
163
  if (entry.retiredAt) return false;
163
164
  return compare(hashToken(clientSecret), entry.hash);
164
165
  });
@@ -166,11 +167,11 @@ var createOAuthModel = ({ controller }) => ({
166
167
  }
167
168
  ;
168
169
  return {
169
- clientId: client.clientId,
170
- grants: ["authorization_code", "client_credentials", "refresh_token"],
171
- id: client.id,
172
- redirectUris: client.redirectUris ?? [],
173
- scopes: client.allowedScopes ?? []
170
+ clientId: record.client,
171
+ grants: SUPPORTED_GRANTS,
172
+ id: record.id,
173
+ redirectUris: record.redirects ?? [],
174
+ scopes: record.scopes ?? []
174
175
  };
175
176
  },
176
177
  saveAuthorizationCode: async (code, client, user) => {
@@ -375,17 +376,16 @@ var createOAuthServer = ({ controller }) => new import_oauth2_server.default({
375
376
  }
376
377
  });
377
378
  var createClient = async ({
378
- allowedScopes = [],
379
379
  controller,
380
380
  createdBy = null,
381
381
  description = null,
382
- grantTypes = ["authorization_code"],
383
382
  name,
384
383
  organization = null,
385
- redirectUris = [],
386
- webOrigins = []
384
+ redirects = [],
385
+ scopes: scopes2 = [],
386
+ origins = []
387
387
  }) => {
388
- const invalid = allowedScopes.filter((scope) => !developer.includes(scope));
388
+ const invalid = scopes2.filter((scope) => !developer.includes(scope));
389
389
  if (invalid.length > 0) {
390
390
  const error = new Error("Scopes not permitted for developer apps: " + invalid.join(", "));
391
391
  error.status = 400;
@@ -393,37 +393,38 @@ var createClient = async ({
393
393
  }
394
394
  ;
395
395
  const rawClientId = generateToken();
396
- const clientId = "drawbridge." + rawClientId;
396
+ const client = "drawbridge." + rawClientId;
397
397
  const rawSecret = generateToken();
398
398
  const secretHash = hashToken(rawSecret);
399
399
  const now = /* @__PURE__ */ new Date();
400
- const doc = await controller.insert({
400
+ const record = await controller.create({
401
401
  collection: "oauth",
402
402
  data: {
403
- allowedScopes,
404
- clientId,
405
- clientSecretHashes: [
403
+ client,
404
+ createdBy,
405
+ description,
406
+ endpoint: {
407
+ auth: "client_secret_basic"
408
+ },
409
+ hashes: [
406
410
  {
407
411
  createdAt: now,
408
412
  hash: secretHash,
409
413
  retiredAt: null
410
414
  }
411
415
  ],
412
- clientType: "confidential",
413
- createdBy,
414
- description,
415
- grantTypes,
416
+ limits: null,
416
417
  name,
417
418
  organization,
418
- rateLimitOverrides: null,
419
- redirectUris,
419
+ origins,
420
+ redirects,
421
+ scopes: scopes2,
420
422
  status: "active",
421
- tokenEndpointAuthMethod: "client_secret_basic",
422
- webOrigins
423
+ type: "confidential"
423
424
  }
424
425
  });
425
426
  return {
426
- client: doc,
427
+ record,
427
428
  rawSecret
428
429
  };
429
430
  };
@@ -14,34 +14,43 @@ import 'crypto';
14
14
  // { collection, query, data } works. drawbridge-api passes its base
15
15
  // controller; tests can pass an in-memory mock.
16
16
  //
17
+ // Boundary translation: the DB uses our renamed schema (client,
18
+ // scopes, redirects, hashes, ...), but the @node-oauth library
19
+ // expects spec-named keys (clientId, scope, redirectUris, ...).
20
+ // Each model method translates at the return boundary.
21
+ //
17
22
  // - createOAuthServer({ controller })
18
23
  // Convenience: creates the model + wraps it in an OAuth2Server with
19
24
  // the standard Drawbridge lifetimes (1h access / 30d refresh) and
20
25
  // client-authentication requirements.
21
26
  //
22
- // - createClient({ base, allowedScopes, name, organization, ... })
27
+ // - createClient({ controller, scopes, name, organization, ... })
23
28
  // Inserts a new oauth client row. Validates scopes against the
24
29
  // developer set; throws 400 on invalid. Returns
25
- // { client, rawSecret } — secret is plaintext, return ONCE.
30
+ // { record, rawSecret } — secret is plaintext, return ONCE.
31
+
26
32
 
33
+ // Hardcoded — the supported grants don't vary per client today. If they
34
+ // ever need to, store `grants` on the oauth doc and read it here.
35
+ const SUPPORTED_GRANTS = [ 'authorization_code', 'client_credentials', 'refresh_token' ];
27
36
 
28
37
  const createOAuthModel = ({ controller }) => ({
29
38
 
30
39
  getClient : async ( clientId, clientSecret ) => {
31
40
 
32
- const client = await controller.get({
41
+ const record = await controller.get({
33
42
  collection : 'oauth',
34
43
  query : {
35
- clientId,
44
+ client : clientId,
36
45
  status : 'active'
37
46
  }
38
47
  });
39
48
 
40
- if( ! client ) return false;
49
+ if( ! record ) return false;
41
50
 
42
51
  if( clientSecret ){
43
52
 
44
- const validSecret = client.clientSecretHashes?.some( ( entry ) => {
53
+ const validSecret = record.hashes?.some( ( entry ) => {
45
54
 
46
55
  if( entry.retiredAt ) return false;
47
56
 
@@ -53,11 +62,11 @@ const createOAuthModel = ({ controller }) => ({
53
62
 
54
63
  }
55
64
  return {
56
- clientId : client.clientId,
57
- grants : [ 'authorization_code', 'client_credentials', 'refresh_token' ],
58
- id : client.id,
59
- redirectUris : client.redirectUris ?? [],
60
- scopes : client.allowedScopes ?? []
65
+ clientId : record.client,
66
+ grants : SUPPORTED_GRANTS,
67
+ id : record.id,
68
+ redirectUris : record.redirects ?? [],
69
+ scopes : record.scopes ?? []
61
70
  };
62
71
 
63
72
  },
@@ -312,23 +321,22 @@ const createOAuthServer = ({ controller }) => new OAuth2Server({
312
321
  }
313
322
  });
314
323
 
315
- // Inserts a new oauth client row. Validates allowedScopes against the
324
+ // Inserts a new oauth client row. Validates scopes against the
316
325
  // developer-allowed subset; throws a 400-flagged error on invalid scopes.
317
- // Returns { client: <inserted-doc>, rawSecret: <plaintext> }. The raw
326
+ // Returns { record: <inserted-doc>, rawSecret: <plaintext> }. The raw
318
327
  // secret is only available here — store immediately.
319
328
  const createClient = async ({
320
- allowedScopes = [],
321
329
  controller,
322
330
  createdBy = null,
323
331
  description = null,
324
- grantTypes = [ 'authorization_code' ],
325
332
  name,
326
333
  organization = null,
327
- redirectUris = [],
328
- webOrigins = []
334
+ redirects = [],
335
+ scopes = [],
336
+ origins = []
329
337
  }) => {
330
338
 
331
- const invalid = allowedScopes.filter( ( scope ) => ! developer.includes( scope ) );
339
+ const invalid = scopes.filter( ( scope ) => ! developer.includes( scope ) );
332
340
 
333
341
  if( invalid.length > 0 ){
334
342
 
@@ -340,41 +348,42 @@ const createClient = async ({
340
348
 
341
349
  }
342
350
  const rawClientId = generateToken();
343
- const clientId = 'drawbridge.' + rawClientId;
351
+ const client = 'drawbridge.' + rawClientId;
344
352
 
345
353
  const rawSecret = generateToken();
346
354
  const secretHash = hashToken( rawSecret );
347
355
 
348
356
  const now = new Date();
349
357
 
350
- const doc = await controller.insert({
358
+ const record = await controller.create({
351
359
  collection : 'oauth',
352
360
  data : {
353
- allowedScopes,
354
- clientId,
355
- clientSecretHashes : [
361
+ client,
362
+ createdBy,
363
+ description,
364
+ endpoint : {
365
+ auth : 'client_secret_basic'
366
+ },
367
+ hashes : [
356
368
  {
357
369
  createdAt : now,
358
370
  hash : secretHash,
359
371
  retiredAt : null
360
372
  }
361
373
  ],
362
- clientType : 'confidential',
363
- createdBy,
364
- description,
365
- grantTypes,
374
+ limits : null,
366
375
  name,
367
376
  organization,
368
- rateLimitOverrides : null,
369
- redirectUris,
377
+ origins,
378
+ redirects,
379
+ scopes,
370
380
  status : 'active',
371
- tokenEndpointAuthMethod : 'client_secret_basic',
372
- webOrigins
381
+ type : 'confidential'
373
382
  }
374
383
  });
375
384
 
376
385
  return {
377
- client : doc,
386
+ record,
378
387
  rawSecret
379
388
  };
380
389
 
@@ -14,34 +14,43 @@ import 'crypto';
14
14
  // { collection, query, data } works. drawbridge-api passes its base
15
15
  // controller; tests can pass an in-memory mock.
16
16
  //
17
+ // Boundary translation: the DB uses our renamed schema (client,
18
+ // scopes, redirects, hashes, ...), but the @node-oauth library
19
+ // expects spec-named keys (clientId, scope, redirectUris, ...).
20
+ // Each model method translates at the return boundary.
21
+ //
17
22
  // - createOAuthServer({ controller })
18
23
  // Convenience: creates the model + wraps it in an OAuth2Server with
19
24
  // the standard Drawbridge lifetimes (1h access / 30d refresh) and
20
25
  // client-authentication requirements.
21
26
  //
22
- // - createClient({ base, allowedScopes, name, organization, ... })
27
+ // - createClient({ controller, scopes, name, organization, ... })
23
28
  // Inserts a new oauth client row. Validates scopes against the
24
29
  // developer set; throws 400 on invalid. Returns
25
- // { client, rawSecret } — secret is plaintext, return ONCE.
30
+ // { record, rawSecret } — secret is plaintext, return ONCE.
31
+
26
32
 
33
+ // Hardcoded — the supported grants don't vary per client today. If they
34
+ // ever need to, store `grants` on the oauth doc and read it here.
35
+ const SUPPORTED_GRANTS = [ 'authorization_code', 'client_credentials', 'refresh_token' ];
27
36
 
28
37
  const createOAuthModel = ({ controller }) => ({
29
38
 
30
39
  getClient : async ( clientId, clientSecret ) => {
31
40
 
32
- const client = await controller.get({
41
+ const record = await controller.get({
33
42
  collection : 'oauth',
34
43
  query : {
35
- clientId,
44
+ client : clientId,
36
45
  status : 'active'
37
46
  }
38
47
  });
39
48
 
40
- if( ! client ) return false;
49
+ if( ! record ) return false;
41
50
 
42
51
  if( clientSecret ){
43
52
 
44
- const validSecret = client.clientSecretHashes?.some( ( entry ) => {
53
+ const validSecret = record.hashes?.some( ( entry ) => {
45
54
 
46
55
  if( entry.retiredAt ) return false;
47
56
 
@@ -53,11 +62,11 @@ const createOAuthModel = ({ controller }) => ({
53
62
 
54
63
  }
55
64
  return {
56
- clientId : client.clientId,
57
- grants : [ 'authorization_code', 'client_credentials', 'refresh_token' ],
58
- id : client.id,
59
- redirectUris : client.redirectUris ?? [],
60
- scopes : client.allowedScopes ?? []
65
+ clientId : record.client,
66
+ grants : SUPPORTED_GRANTS,
67
+ id : record.id,
68
+ redirectUris : record.redirects ?? [],
69
+ scopes : record.scopes ?? []
61
70
  };
62
71
 
63
72
  },
@@ -312,23 +321,22 @@ const createOAuthServer = ({ controller }) => new OAuth2Server({
312
321
  }
313
322
  });
314
323
 
315
- // Inserts a new oauth client row. Validates allowedScopes against the
324
+ // Inserts a new oauth client row. Validates scopes against the
316
325
  // developer-allowed subset; throws a 400-flagged error on invalid scopes.
317
- // Returns { client: <inserted-doc>, rawSecret: <plaintext> }. The raw
326
+ // Returns { record: <inserted-doc>, rawSecret: <plaintext> }. The raw
318
327
  // secret is only available here — store immediately.
319
328
  const createClient = async ({
320
- allowedScopes = [],
321
329
  controller,
322
330
  createdBy = null,
323
331
  description = null,
324
- grantTypes = [ 'authorization_code' ],
325
332
  name,
326
333
  organization = null,
327
- redirectUris = [],
328
- webOrigins = []
334
+ redirects = [],
335
+ scopes = [],
336
+ origins = []
329
337
  }) => {
330
338
 
331
- const invalid = allowedScopes.filter( ( scope ) => ! developer.includes( scope ) );
339
+ const invalid = scopes.filter( ( scope ) => ! developer.includes( scope ) );
332
340
 
333
341
  if( invalid.length > 0 ){
334
342
 
@@ -340,41 +348,42 @@ const createClient = async ({
340
348
 
341
349
  }
342
350
  const rawClientId = generateToken();
343
- const clientId = 'drawbridge.' + rawClientId;
351
+ const client = 'drawbridge.' + rawClientId;
344
352
 
345
353
  const rawSecret = generateToken();
346
354
  const secretHash = hashToken( rawSecret );
347
355
 
348
356
  const now = new Date();
349
357
 
350
- const doc = await controller.insert({
358
+ const record = await controller.create({
351
359
  collection : 'oauth',
352
360
  data : {
353
- allowedScopes,
354
- clientId,
355
- clientSecretHashes : [
361
+ client,
362
+ createdBy,
363
+ description,
364
+ endpoint : {
365
+ auth : 'client_secret_basic'
366
+ },
367
+ hashes : [
356
368
  {
357
369
  createdAt : now,
358
370
  hash : secretHash,
359
371
  retiredAt : null
360
372
  }
361
373
  ],
362
- clientType : 'confidential',
363
- createdBy,
364
- description,
365
- grantTypes,
374
+ limits : null,
366
375
  name,
367
376
  organization,
368
- rateLimitOverrides : null,
369
- redirectUris,
377
+ origins,
378
+ redirects,
379
+ scopes,
370
380
  status : 'active',
371
- tokenEndpointAuthMethod : 'client_secret_basic',
372
- webOrigins
381
+ type : 'confidential'
373
382
  }
374
383
  });
375
384
 
376
385
  return {
377
- client : doc,
386
+ record,
378
387
  rawSecret
379
388
  };
380
389
 
@@ -111,19 +111,20 @@ var hashToken = (raw) => hash(raw, process.env.OAUTH_TOKEN_HMAC_KEY);
111
111
  var generateToken = () => generate(32, "base64url");
112
112
 
113
113
  // lib/oauth/server.js
114
+ var SUPPORTED_GRANTS = ["authorization_code", "client_credentials", "refresh_token"];
114
115
  var createOAuthModel = ({ controller }) => ({
115
116
  getClient: async (clientId, clientSecret) => {
116
117
  var _a;
117
- const client = await controller.get({
118
+ const record = await controller.get({
118
119
  collection: "oauth",
119
120
  query: {
120
- clientId,
121
+ client: clientId,
121
122
  status: "active"
122
123
  }
123
124
  });
124
- if (!client) return false;
125
+ if (!record) return false;
125
126
  if (clientSecret) {
126
- const validSecret = (_a = client.clientSecretHashes) == null ? void 0 : _a.some((entry) => {
127
+ const validSecret = (_a = record.hashes) == null ? void 0 : _a.some((entry) => {
127
128
  if (entry.retiredAt) return false;
128
129
  return compare(hashToken(clientSecret), entry.hash);
129
130
  });
@@ -131,11 +132,11 @@ var createOAuthModel = ({ controller }) => ({
131
132
  }
132
133
  ;
133
134
  return {
134
- clientId: client.clientId,
135
- grants: ["authorization_code", "client_credentials", "refresh_token"],
136
- id: client.id,
137
- redirectUris: client.redirectUris ?? [],
138
- scopes: client.allowedScopes ?? []
135
+ clientId: record.client,
136
+ grants: SUPPORTED_GRANTS,
137
+ id: record.id,
138
+ redirectUris: record.redirects ?? [],
139
+ scopes: record.scopes ?? []
139
140
  };
140
141
  },
141
142
  saveAuthorizationCode: async (code, client, user) => {
@@ -340,17 +341,16 @@ var createOAuthServer = ({ controller }) => new OAuth2Server({
340
341
  }
341
342
  });
342
343
  var createClient = async ({
343
- allowedScopes = [],
344
344
  controller,
345
345
  createdBy = null,
346
346
  description = null,
347
- grantTypes = ["authorization_code"],
348
347
  name,
349
348
  organization = null,
350
- redirectUris = [],
351
- webOrigins = []
349
+ redirects = [],
350
+ scopes: scopes2 = [],
351
+ origins = []
352
352
  }) => {
353
- const invalid = allowedScopes.filter((scope) => !developer.includes(scope));
353
+ const invalid = scopes2.filter((scope) => !developer.includes(scope));
354
354
  if (invalid.length > 0) {
355
355
  const error = new Error("Scopes not permitted for developer apps: " + invalid.join(", "));
356
356
  error.status = 400;
@@ -358,37 +358,38 @@ var createClient = async ({
358
358
  }
359
359
  ;
360
360
  const rawClientId = generateToken();
361
- const clientId = "drawbridge." + rawClientId;
361
+ const client = "drawbridge." + rawClientId;
362
362
  const rawSecret = generateToken();
363
363
  const secretHash = hashToken(rawSecret);
364
364
  const now = /* @__PURE__ */ new Date();
365
- const doc = await controller.insert({
365
+ const record = await controller.create({
366
366
  collection: "oauth",
367
367
  data: {
368
- allowedScopes,
369
- clientId,
370
- clientSecretHashes: [
368
+ client,
369
+ createdBy,
370
+ description,
371
+ endpoint: {
372
+ auth: "client_secret_basic"
373
+ },
374
+ hashes: [
371
375
  {
372
376
  createdAt: now,
373
377
  hash: secretHash,
374
378
  retiredAt: null
375
379
  }
376
380
  ],
377
- clientType: "confidential",
378
- createdBy,
379
- description,
380
- grantTypes,
381
+ limits: null,
381
382
  name,
382
383
  organization,
383
- rateLimitOverrides: null,
384
- redirectUris,
384
+ origins,
385
+ redirects,
386
+ scopes: scopes2,
385
387
  status: "active",
386
- tokenEndpointAuthMethod: "client_secret_basic",
387
- webOrigins
388
+ type: "confidential"
388
389
  }
389
390
  });
390
391
  return {
391
- client: doc,
392
+ record,
392
393
  rawSecret
393
394
  };
394
395
  };
package/package.json CHANGED
@@ -130,5 +130,5 @@
130
130
  "build": "tsup && npm publish"
131
131
  },
132
132
  "types": "dist/index.d.ts",
133
- "version": "0.0.48"
133
+ "version": "0.0.50"
134
134
  }