@dereekb/firebase-server 13.18.0 → 13.20.0

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,15 +1,15 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/mailgun",
3
- "version": "13.18.0",
3
+ "version": "13.20.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.18.0",
6
- "@dereekb/firebase": "13.18.0",
7
- "@dereekb/firebase-server": "13.18.0",
8
- "@dereekb/date": "13.18.0",
9
- "@dereekb/nestjs": "13.18.0",
10
- "@dereekb/model": "13.18.0",
11
- "@dereekb/rxjs": "13.18.0",
12
- "@dereekb/util": "13.18.0"
5
+ "@dereekb/analytics": "13.20.0",
6
+ "@dereekb/firebase": "13.20.0",
7
+ "@dereekb/firebase-server": "13.20.0",
8
+ "@dereekb/date": "13.20.0",
9
+ "@dereekb/nestjs": "13.20.0",
10
+ "@dereekb/model": "13.20.0",
11
+ "@dereekb/rxjs": "13.20.0",
12
+ "@dereekb/util": "13.20.0"
13
13
  },
14
14
  "exports": {
15
15
  "./package.json": "./package.json",
package/mcp/index.cjs.js CHANGED
@@ -487,7 +487,7 @@ function _unsupported_iterable_to_array$9(o, minLen) {
487
487
  * Version stamp embedded in `route.manifest.json`. Runtime loaders refuse
488
488
  * manifests whose `version` does not match. Mirror in `@dereekb/dbx-cli`'s
489
489
  * `ROUTE_MANIFEST_VERSION` — bump both together.
490
- */ var ROUTE_MANIFEST_VERSION = 1;
490
+ */ var ROUTE_MANIFEST_VERSION = 2;
491
491
  /**
492
492
  * Extracts the normalized pathname from a full URL or a bare path. Strips the
493
493
  * scheme/host/port, query string, and hash; collapses an empty path to `/` and
@@ -3572,12 +3572,12 @@ function _unsupported_iterable_to_array$5(o, minLen) {
3572
3572
  */ function createWhoamiTool(deps) {
3573
3573
  var _ref;
3574
3574
  var _deps_auth_app;
3575
- var handler = function handler(_args, ctx) {
3576
- return Promise.resolve(whoamiToolHandler(ctx, deps));
3575
+ var handler = function handler(args, ctx) {
3576
+ return Promise.resolve(whoamiToolHandler(args, ctx, deps));
3577
3577
  };
3578
3578
  var name = WHOAMI_TOOL_NAME;
3579
3579
  var appLabel = (_ref = (_deps_auth_app = deps.auth.app) === null || _deps_auth_app === void 0 ? void 0 : _deps_auth_app.app) !== null && _ref !== void 0 ? _ref : 'this server';
3580
- var description = "Reports the authenticated caller's uid, raw custom claims, and decoded role set for `".concat(appLabel, "`. Returns `authenticated: false` for anonymous callers. Claim descriptions are drawn from the build-time auth manifest; only keys actually present on the live token are detailed.");
3580
+ var description = "Reports the authenticated caller's identity and decoded role set for `".concat(appLabel, '`. Returns `authenticated: false` for anonymous callers. Defaults to a concise summary (uid + roles only); pass `detail: "claims"` to also include the raw claims object, or `detail: "full"` to additionally include per-claim descriptions (drawn from the build-time auth manifest) and any unknown claim keys.');
3581
3581
  return buildStaticToolDefinition({
3582
3582
  name: name,
3583
3583
  description: description,
@@ -3593,24 +3593,20 @@ function _unsupported_iterable_to_array$5(o, minLen) {
3593
3593
  });
3594
3594
  }
3595
3595
  // MARK: Handler
3596
- function whoamiToolHandler(ctx, deps) {
3596
+ function whoamiToolHandler(args, ctx, deps) {
3597
3597
  var _auth_token;
3598
3598
  var _deps_auth_app;
3599
3599
  var auth = ctx.auth;
3600
3600
  if ((auth === null || auth === void 0 ? void 0 : auth.uid) == null) {
3601
- var output = {
3602
- authenticated: false,
3603
- claims: {},
3604
- roles: [],
3605
- claimDetails: [],
3606
- unknownClaimKeys: []
3607
- };
3608
- return buildResult(output, '# whoami\n\nUnauthenticated.');
3601
+ return buildResult({
3602
+ authenticated: false
3603
+ }, '# whoami\n\nNot authenticated (anonymous caller).');
3609
3604
  }
3605
+ var detail = parseDetailLevel(args);
3610
3606
  var token = (_auth_token = auth.token) !== null && _auth_token !== void 0 ? _auth_token : {};
3611
- var presentKeys = Object.entries(token).filter(function(param) {
3612
- var _param = _sliced_to_array$3(param, 2), value = _param[1];
3613
- return value !== undefined;
3607
+ var claimKeys = Object.entries(token).filter(function(param) {
3608
+ var _param = _sliced_to_array$3(param, 2), key = _param[0], value = _param[1];
3609
+ return value !== undefined && key !== 'email' && key !== 'email_verified';
3614
3610
  }).map(function(param) {
3615
3611
  var _param = _sliced_to_array$3(param, 1), key = _param[0];
3616
3612
  return key;
@@ -3644,11 +3640,8 @@ function whoamiToolHandler(ctx, deps) {
3644
3640
  var unknownClaimKeys = [];
3645
3641
  var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
3646
3642
  try {
3647
- for(var _iterator1 = presentKeys[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
3643
+ for(var _iterator1 = claimKeys[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
3648
3644
  var key = _step1.value;
3649
- if (key === 'email' || key === 'email_verified') {
3650
- continue;
3651
- }
3652
3645
  var claim1 = claimsByKey.get(key);
3653
3646
  if (claim1 == null) {
3654
3647
  unknownClaimKeys.push(key);
@@ -3678,22 +3671,30 @@ function whoamiToolHandler(ctx, deps) {
3678
3671
  }
3679
3672
  }
3680
3673
  }
3681
- var output1 = _object_spread$2({
3674
+ var output = _object_spread$2({
3682
3675
  authenticated: true,
3683
3676
  uid: auth.uid,
3684
- claims: _object_spread$2({}, token),
3685
3677
  roles: roles,
3686
- claimDetails: claimDetails,
3687
- unknownClaimKeys: unknownClaimKeys
3678
+ claimCount: claimKeys.length
3688
3679
  }, ((_deps_auth_app = deps.auth.app) === null || _deps_auth_app === void 0 ? void 0 : _deps_auth_app.app) == null ? {} : {
3689
3680
  app: deps.auth.app.app
3690
3681
  }, token.email == null ? {} : {
3691
3682
  email: token.email
3692
3683
  }, token.email_verified == null ? {} : {
3693
3684
  emailVerified: token.email_verified
3694
- });
3695
- var text = renderText(output1);
3696
- return buildResult(output1, text);
3685
+ }, detail === 'summary' ? {} : {
3686
+ claims: _object_spread$2({}, token)
3687
+ }, detail === 'full' ? {
3688
+ claimDetails: claimDetails,
3689
+ unknownClaimKeys: unknownClaimKeys
3690
+ } : {});
3691
+ var text = renderText(output, detail);
3692
+ return buildResult(output, text);
3693
+ }
3694
+ // Coerces the `detail` input into a WhoamiDetailLevel, defaulting unknown or absent values to `summary`.
3695
+ function parseDetailLevel(args) {
3696
+ var value = args.detail;
3697
+ return value === 'claims' || value === 'full' ? value : 'summary';
3697
3698
  }
3698
3699
  function buildResult(output, text) {
3699
3700
  return {
@@ -3706,34 +3707,51 @@ function buildResult(output, text) {
3706
3707
  structuredContent: output
3707
3708
  };
3708
3709
  }
3709
- function renderText(output) {
3710
+ function renderText(output, detail) {
3710
3711
  var lines = [
3711
3712
  '# whoami',
3712
3713
  ''
3713
3714
  ];
3714
3715
  appendHeaderLines(lines, output);
3715
- appendClaimDetailLines(lines, output.claimDetails);
3716
- appendUnknownClaimLines(lines, output.unknownClaimKeys);
3716
+ if (detail === 'full') {
3717
+ var _output_claimDetails, _output_unknownClaimKeys;
3718
+ appendClaimDetailLines(lines, (_output_claimDetails = output.claimDetails) !== null && _output_claimDetails !== void 0 ? _output_claimDetails : []);
3719
+ appendUnknownClaimLines(lines, (_output_unknownClaimKeys = output.unknownClaimKeys) !== null && _output_unknownClaimKeys !== void 0 ? _output_unknownClaimKeys : []);
3720
+ }
3721
+ if (detail !== 'summary') {
3722
+ var _output_claims;
3723
+ appendRawClaimsLines(lines, (_output_claims = output.claims) !== null && _output_claims !== void 0 ? _output_claims : {});
3724
+ }
3725
+ appendDetailHint(lines, detail);
3717
3726
  return lines.join('\n');
3718
3727
  }
3719
3728
  function appendHeaderLines(lines, output) {
3720
- if (output.uid != null) {
3721
- lines.push("- **uid:** `".concat(output.uid, "`"));
3722
- }
3723
- if (output.app != null) {
3724
- lines.push("- **app:** `".concat(output.app, "`"));
3725
- }
3729
+ var _output_roles, _output_claimCount;
3730
+ var appPart = output.app == null ? '' : " on `".concat(output.app, "`");
3731
+ lines.push("Authenticated as `".concat(output.uid, "`").concat(appPart, "."), '');
3726
3732
  if (output.email != null) {
3727
- lines.push("- **email:** `".concat(output.email, "`"));
3733
+ lines.push("- **email:** `".concat(output.email, "`").concat(formatEmailVerified(output.emailVerified)));
3728
3734
  }
3729
- if (output.roles.length > 0) {
3730
- var rolesText = output.roles.map(function(role) {
3735
+ var roles = (_output_roles = output.roles) !== null && _output_roles !== void 0 ? _output_roles : [];
3736
+ if (roles.length > 0) {
3737
+ var rolesText = roles.map(function(role) {
3731
3738
  return "`".concat(role, "`");
3732
3739
  }).join(', ');
3733
3740
  lines.push("- **roles:** ".concat(rolesText));
3734
3741
  } else {
3735
3742
  lines.push('- **roles:** _(none)_');
3736
3743
  }
3744
+ lines.push("- **claims:** ".concat((_output_claimCount = output.claimCount) !== null && _output_claimCount !== void 0 ? _output_claimCount : 0, " present"));
3745
+ }
3746
+ // Renders the parenthetical verification suffix for the email line, or an empty string when the token carries no `email_verified` claim.
3747
+ function formatEmailVerified(emailVerified) {
3748
+ if (emailVerified === true) {
3749
+ return ' (verified)';
3750
+ }
3751
+ if (emailVerified === false) {
3752
+ return ' (unverified)';
3753
+ }
3754
+ return '';
3737
3755
  }
3738
3756
  function appendClaimDetailLines(lines, claimDetails) {
3739
3757
  if (claimDetails.length === 0) {
@@ -3795,22 +3813,40 @@ function appendUnknownClaimLines(lines, unknownClaimKeys) {
3795
3813
  }
3796
3814
  }
3797
3815
  }
3816
+ // Renders the raw claims object as a fenced JSON block — the one place the tool emits raw JSON, since claims are an arbitrary key → value bag.
3817
+ function appendRawClaimsLines(lines, claims) {
3818
+ lines.push('', '## Raw claims', '```json', JSON.stringify(claims, null, 2), '```');
3819
+ }
3820
+ // Appends a hint pointing at the next, more detailed level. Omitted at `full`, which is already the most detailed level.
3821
+ function appendDetailHint(lines, detail) {
3822
+ if (detail === 'summary') {
3823
+ lines.push('', '_Call again with `detail: "claims"` for the raw claim values, or `detail: "full"` for values plus per-claim descriptions._');
3824
+ } else if (detail === 'claims') {
3825
+ lines.push('', '_Call again with `detail: "full"` for per-claim descriptions and unknown claim keys._');
3826
+ }
3827
+ }
3798
3828
  // MARK: Schemas
3799
3829
  var WHOAMI_INPUT_SCHEMA = {
3800
3830
  type: 'object',
3801
- properties: {},
3831
+ properties: {
3832
+ detail: {
3833
+ type: 'string',
3834
+ enum: [
3835
+ 'summary',
3836
+ 'claims',
3837
+ 'full'
3838
+ ],
3839
+ description: 'Level of detail. "summary" (default): identity + decoded roles only. "claims": adds the raw claims object (key → value). "full": adds per-claim descriptions, granted/revoked roles, tags, and unknown claim keys.'
3840
+ }
3841
+ },
3802
3842
  additionalProperties: false
3803
3843
  };
3804
3844
  var WHOAMI_OUTPUT_SCHEMA = {
3805
3845
  type: 'object',
3806
3846
  required: [
3807
- 'authenticated',
3808
- 'claims',
3809
- 'roles',
3810
- 'claimDetails',
3811
- 'unknownClaimKeys'
3847
+ 'authenticated'
3812
3848
  ],
3813
- description: 'Caller identity, raw token claims, decoded role set, and per-claim descriptions for every key present on the live token.',
3849
+ description: 'Caller identity and decoded role set. Claim-bearing fields (claims, claimDetails, unknownClaimKeys) are present only at the detail level that requests them via the `detail` input.',
3814
3850
  properties: {
3815
3851
  authenticated: {
3816
3852
  type: 'boolean'
@@ -3827,16 +3863,19 @@ var WHOAMI_OUTPUT_SCHEMA = {
3827
3863
  app: {
3828
3864
  type: 'string'
3829
3865
  },
3830
- claims: {
3831
- type: 'object',
3832
- additionalProperties: true
3833
- },
3834
3866
  roles: {
3835
3867
  type: 'array',
3836
3868
  items: {
3837
3869
  type: 'string'
3838
3870
  }
3839
3871
  },
3872
+ claimCount: {
3873
+ type: 'integer'
3874
+ },
3875
+ claims: {
3876
+ type: 'object',
3877
+ additionalProperties: true
3878
+ },
3840
3879
  claimDetails: {
3841
3880
  type: 'array',
3842
3881
  items: {
@@ -4132,6 +4171,11 @@ function _ts_generator$4(thisArg, body) {
4132
4171
  /**
4133
4172
  * Placeholder segment standing for the caller's authenticated uid.
4134
4173
  */ var AUTH_UID_PLACEHOLDER = '{authUid}';
4174
+ /**
4175
+ * Matches a `{flatKey:<param>}` key template, capturing the route param whose
4176
+ * URL value is a whole two-way-flat FirestoreModelKey. Mirror of the build-time
4177
+ * `FLAT_KEY_TOKEN_RE` in `@dereekb/dbx-cli`'s route-model-tag parser.
4178
+ */ var FLAT_KEY_TEMPLATE_RE = RegExp("^\\{flatKey:([a-zA-Z_][a-zA-Z0-9_]*)\\}$", "u");
4135
4179
  // MARK: Factory
4136
4180
  /**
4137
4181
  * Builds the built-in `url-models` MCP tool definition.
@@ -4410,6 +4454,8 @@ function resolveModelEntry(input) {
4410
4454
  result = base;
4411
4455
  } else if (entry.kind === 'id') {
4412
4456
  result = _object_spread$1({}, base, resolveIdKey(input));
4457
+ } else if (entry.kind === 'flatKey') {
4458
+ result = _object_spread$1({}, base, resolveFlatKey(input));
4413
4459
  } else {
4414
4460
  result = _object_spread$1({}, base, resolveFullKey(input));
4415
4461
  }
@@ -4448,6 +4494,57 @@ function resolveIdKey(input) {
4448
4494
  }
4449
4495
  return result;
4450
4496
  }
4497
+ function resolveFlatKey(input) {
4498
+ var _entry_keyTemplate;
4499
+ var entry = input.entry, params = input.params;
4500
+ var match = FLAT_KEY_TEMPLATE_RE.exec((_entry_keyTemplate = entry.keyTemplate) !== null && _entry_keyTemplate !== void 0 ? _entry_keyTemplate : '');
4501
+ var result;
4502
+ if (match === null) {
4503
+ var _entry_keyTemplate1;
4504
+ // Defensive: a flatKey entry whose template is not `{flatKey:<param>}` (a malformed manifest).
4505
+ result = {
4506
+ unresolved: {
4507
+ reason: 'invalid-flat-key',
4508
+ message: "Model `".concat(entry.modelType, "` is a flatKey binding but its key template `").concat((_entry_keyTemplate1 = entry.keyTemplate) !== null && _entry_keyTemplate1 !== void 0 ? _entry_keyTemplate1 : '', "` is not `{flatKey:<param>}`.")
4509
+ }
4510
+ };
4511
+ } else {
4512
+ var paramName = match[1];
4513
+ var value = params[paramName];
4514
+ if (value === undefined || value.length === 0) {
4515
+ result = {
4516
+ unresolved: {
4517
+ reason: 'missing-param',
4518
+ message: "Route param `:".concat(paramName, "` (the flattened model key) was not captured from the URL.")
4519
+ }
4520
+ };
4521
+ } else {
4522
+ // The param value IS a two-way-flat key (`r_<id>_cs_<id>`); un-flatten it back to `r/<id>/cs/<id>`.
4523
+ var key = firebase.inferKeyFromTwoWayFlatFirestoreModelKey(value);
4524
+ result = isEvenSegmentKey(key) ? {
4525
+ key: key
4526
+ } : {
4527
+ unresolved: {
4528
+ reason: 'invalid-flat-key',
4529
+ message: "Route param `:".concat(paramName, "` did not decode to a valid FirestoreModelKey (got `").concat(key, "`).")
4530
+ }
4531
+ };
4532
+ }
4533
+ }
4534
+ return result;
4535
+ }
4536
+ /**
4537
+ * Whether a decoded key has the shape of a FirestoreModelKey: a non-empty,
4538
+ * even number of `/`-separated segments (collection/id pairs).
4539
+ *
4540
+ * @param key - The decoded (un-flattened) key.
4541
+ * @returns True when the key has an even, non-zero segment count.
4542
+ */ function isEvenSegmentKey(key) {
4543
+ var segments = key.split('/').filter(function(s) {
4544
+ return s.length > 0;
4545
+ });
4546
+ return segments.length >= 2 && segments.length % 2 === 0;
4547
+ }
4451
4548
  function resolveFullKey(input) {
4452
4549
  var _entry_keyTemplate;
4453
4550
  var entry = input.entry, params = input.params, uid = input.uid;
package/mcp/index.esm.js CHANGED
@@ -7,7 +7,7 @@ import { makeValuesGroupMap, performAsyncTasks, authRolesSetHasRoles } from '@de
7
7
  import { ModelApiCallModelDispatchService, ModelApiGetService, FirebaseServerStorageService, FirebaseServerEnvService, FirebaseServerAnalyticsService } from '@dereekb/firebase-server';
8
8
  import { inspect } from 'node:util';
9
9
  import { arktypeToJsonSchemaForExport } from '@dereekb/model';
10
- import { callModelOidcScopeForCallType } from '@dereekb/firebase';
10
+ import { callModelOidcScopeForCallType, inferKeyFromTwoWayFlatFirestoreModelKey } from '@dereekb/firebase';
11
11
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
12
12
 
13
13
  function _class_call_check$5(instance, Constructor) {
@@ -485,7 +485,7 @@ function _unsupported_iterable_to_array$9(o, minLen) {
485
485
  * Version stamp embedded in `route.manifest.json`. Runtime loaders refuse
486
486
  * manifests whose `version` does not match. Mirror in `@dereekb/dbx-cli`'s
487
487
  * `ROUTE_MANIFEST_VERSION` — bump both together.
488
- */ var ROUTE_MANIFEST_VERSION = 1;
488
+ */ var ROUTE_MANIFEST_VERSION = 2;
489
489
  /**
490
490
  * Extracts the normalized pathname from a full URL or a bare path. Strips the
491
491
  * scheme/host/port, query string, and hash; collapses an empty path to `/` and
@@ -3570,12 +3570,12 @@ function _unsupported_iterable_to_array$5(o, minLen) {
3570
3570
  */ function createWhoamiTool(deps) {
3571
3571
  var _ref;
3572
3572
  var _deps_auth_app;
3573
- var handler = function handler(_args, ctx) {
3574
- return Promise.resolve(whoamiToolHandler(ctx, deps));
3573
+ var handler = function handler(args, ctx) {
3574
+ return Promise.resolve(whoamiToolHandler(args, ctx, deps));
3575
3575
  };
3576
3576
  var name = WHOAMI_TOOL_NAME;
3577
3577
  var appLabel = (_ref = (_deps_auth_app = deps.auth.app) === null || _deps_auth_app === void 0 ? void 0 : _deps_auth_app.app) !== null && _ref !== void 0 ? _ref : 'this server';
3578
- var description = "Reports the authenticated caller's uid, raw custom claims, and decoded role set for `".concat(appLabel, "`. Returns `authenticated: false` for anonymous callers. Claim descriptions are drawn from the build-time auth manifest; only keys actually present on the live token are detailed.");
3578
+ var description = "Reports the authenticated caller's identity and decoded role set for `".concat(appLabel, '`. Returns `authenticated: false` for anonymous callers. Defaults to a concise summary (uid + roles only); pass `detail: "claims"` to also include the raw claims object, or `detail: "full"` to additionally include per-claim descriptions (drawn from the build-time auth manifest) and any unknown claim keys.');
3579
3579
  return buildStaticToolDefinition({
3580
3580
  name: name,
3581
3581
  description: description,
@@ -3591,24 +3591,20 @@ function _unsupported_iterable_to_array$5(o, minLen) {
3591
3591
  });
3592
3592
  }
3593
3593
  // MARK: Handler
3594
- function whoamiToolHandler(ctx, deps) {
3594
+ function whoamiToolHandler(args, ctx, deps) {
3595
3595
  var _auth_token;
3596
3596
  var _deps_auth_app;
3597
3597
  var auth = ctx.auth;
3598
3598
  if ((auth === null || auth === void 0 ? void 0 : auth.uid) == null) {
3599
- var output = {
3600
- authenticated: false,
3601
- claims: {},
3602
- roles: [],
3603
- claimDetails: [],
3604
- unknownClaimKeys: []
3605
- };
3606
- return buildResult(output, '# whoami\n\nUnauthenticated.');
3599
+ return buildResult({
3600
+ authenticated: false
3601
+ }, '# whoami\n\nNot authenticated (anonymous caller).');
3607
3602
  }
3603
+ var detail = parseDetailLevel(args);
3608
3604
  var token = (_auth_token = auth.token) !== null && _auth_token !== void 0 ? _auth_token : {};
3609
- var presentKeys = Object.entries(token).filter(function(param) {
3610
- var _param = _sliced_to_array$3(param, 2), value = _param[1];
3611
- return value !== undefined;
3605
+ var claimKeys = Object.entries(token).filter(function(param) {
3606
+ var _param = _sliced_to_array$3(param, 2), key = _param[0], value = _param[1];
3607
+ return value !== undefined && key !== 'email' && key !== 'email_verified';
3612
3608
  }).map(function(param) {
3613
3609
  var _param = _sliced_to_array$3(param, 1), key = _param[0];
3614
3610
  return key;
@@ -3642,11 +3638,8 @@ function whoamiToolHandler(ctx, deps) {
3642
3638
  var unknownClaimKeys = [];
3643
3639
  var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
3644
3640
  try {
3645
- for(var _iterator1 = presentKeys[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
3641
+ for(var _iterator1 = claimKeys[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
3646
3642
  var key = _step1.value;
3647
- if (key === 'email' || key === 'email_verified') {
3648
- continue;
3649
- }
3650
3643
  var claim1 = claimsByKey.get(key);
3651
3644
  if (claim1 == null) {
3652
3645
  unknownClaimKeys.push(key);
@@ -3676,22 +3669,30 @@ function whoamiToolHandler(ctx, deps) {
3676
3669
  }
3677
3670
  }
3678
3671
  }
3679
- var output1 = _object_spread$2({
3672
+ var output = _object_spread$2({
3680
3673
  authenticated: true,
3681
3674
  uid: auth.uid,
3682
- claims: _object_spread$2({}, token),
3683
3675
  roles: roles,
3684
- claimDetails: claimDetails,
3685
- unknownClaimKeys: unknownClaimKeys
3676
+ claimCount: claimKeys.length
3686
3677
  }, ((_deps_auth_app = deps.auth.app) === null || _deps_auth_app === void 0 ? void 0 : _deps_auth_app.app) == null ? {} : {
3687
3678
  app: deps.auth.app.app
3688
3679
  }, token.email == null ? {} : {
3689
3680
  email: token.email
3690
3681
  }, token.email_verified == null ? {} : {
3691
3682
  emailVerified: token.email_verified
3692
- });
3693
- var text = renderText(output1);
3694
- return buildResult(output1, text);
3683
+ }, detail === 'summary' ? {} : {
3684
+ claims: _object_spread$2({}, token)
3685
+ }, detail === 'full' ? {
3686
+ claimDetails: claimDetails,
3687
+ unknownClaimKeys: unknownClaimKeys
3688
+ } : {});
3689
+ var text = renderText(output, detail);
3690
+ return buildResult(output, text);
3691
+ }
3692
+ // Coerces the `detail` input into a WhoamiDetailLevel, defaulting unknown or absent values to `summary`.
3693
+ function parseDetailLevel(args) {
3694
+ var value = args.detail;
3695
+ return value === 'claims' || value === 'full' ? value : 'summary';
3695
3696
  }
3696
3697
  function buildResult(output, text) {
3697
3698
  return {
@@ -3704,34 +3705,51 @@ function buildResult(output, text) {
3704
3705
  structuredContent: output
3705
3706
  };
3706
3707
  }
3707
- function renderText(output) {
3708
+ function renderText(output, detail) {
3708
3709
  var lines = [
3709
3710
  '# whoami',
3710
3711
  ''
3711
3712
  ];
3712
3713
  appendHeaderLines(lines, output);
3713
- appendClaimDetailLines(lines, output.claimDetails);
3714
- appendUnknownClaimLines(lines, output.unknownClaimKeys);
3714
+ if (detail === 'full') {
3715
+ var _output_claimDetails, _output_unknownClaimKeys;
3716
+ appendClaimDetailLines(lines, (_output_claimDetails = output.claimDetails) !== null && _output_claimDetails !== void 0 ? _output_claimDetails : []);
3717
+ appendUnknownClaimLines(lines, (_output_unknownClaimKeys = output.unknownClaimKeys) !== null && _output_unknownClaimKeys !== void 0 ? _output_unknownClaimKeys : []);
3718
+ }
3719
+ if (detail !== 'summary') {
3720
+ var _output_claims;
3721
+ appendRawClaimsLines(lines, (_output_claims = output.claims) !== null && _output_claims !== void 0 ? _output_claims : {});
3722
+ }
3723
+ appendDetailHint(lines, detail);
3715
3724
  return lines.join('\n');
3716
3725
  }
3717
3726
  function appendHeaderLines(lines, output) {
3718
- if (output.uid != null) {
3719
- lines.push("- **uid:** `".concat(output.uid, "`"));
3720
- }
3721
- if (output.app != null) {
3722
- lines.push("- **app:** `".concat(output.app, "`"));
3723
- }
3727
+ var _output_roles, _output_claimCount;
3728
+ var appPart = output.app == null ? '' : " on `".concat(output.app, "`");
3729
+ lines.push("Authenticated as `".concat(output.uid, "`").concat(appPart, "."), '');
3724
3730
  if (output.email != null) {
3725
- lines.push("- **email:** `".concat(output.email, "`"));
3731
+ lines.push("- **email:** `".concat(output.email, "`").concat(formatEmailVerified(output.emailVerified)));
3726
3732
  }
3727
- if (output.roles.length > 0) {
3728
- var rolesText = output.roles.map(function(role) {
3733
+ var roles = (_output_roles = output.roles) !== null && _output_roles !== void 0 ? _output_roles : [];
3734
+ if (roles.length > 0) {
3735
+ var rolesText = roles.map(function(role) {
3729
3736
  return "`".concat(role, "`");
3730
3737
  }).join(', ');
3731
3738
  lines.push("- **roles:** ".concat(rolesText));
3732
3739
  } else {
3733
3740
  lines.push('- **roles:** _(none)_');
3734
3741
  }
3742
+ lines.push("- **claims:** ".concat((_output_claimCount = output.claimCount) !== null && _output_claimCount !== void 0 ? _output_claimCount : 0, " present"));
3743
+ }
3744
+ // Renders the parenthetical verification suffix for the email line, or an empty string when the token carries no `email_verified` claim.
3745
+ function formatEmailVerified(emailVerified) {
3746
+ if (emailVerified === true) {
3747
+ return ' (verified)';
3748
+ }
3749
+ if (emailVerified === false) {
3750
+ return ' (unverified)';
3751
+ }
3752
+ return '';
3735
3753
  }
3736
3754
  function appendClaimDetailLines(lines, claimDetails) {
3737
3755
  if (claimDetails.length === 0) {
@@ -3793,22 +3811,40 @@ function appendUnknownClaimLines(lines, unknownClaimKeys) {
3793
3811
  }
3794
3812
  }
3795
3813
  }
3814
+ // Renders the raw claims object as a fenced JSON block — the one place the tool emits raw JSON, since claims are an arbitrary key → value bag.
3815
+ function appendRawClaimsLines(lines, claims) {
3816
+ lines.push('', '## Raw claims', '```json', JSON.stringify(claims, null, 2), '```');
3817
+ }
3818
+ // Appends a hint pointing at the next, more detailed level. Omitted at `full`, which is already the most detailed level.
3819
+ function appendDetailHint(lines, detail) {
3820
+ if (detail === 'summary') {
3821
+ lines.push('', '_Call again with `detail: "claims"` for the raw claim values, or `detail: "full"` for values plus per-claim descriptions._');
3822
+ } else if (detail === 'claims') {
3823
+ lines.push('', '_Call again with `detail: "full"` for per-claim descriptions and unknown claim keys._');
3824
+ }
3825
+ }
3796
3826
  // MARK: Schemas
3797
3827
  var WHOAMI_INPUT_SCHEMA = {
3798
3828
  type: 'object',
3799
- properties: {},
3829
+ properties: {
3830
+ detail: {
3831
+ type: 'string',
3832
+ enum: [
3833
+ 'summary',
3834
+ 'claims',
3835
+ 'full'
3836
+ ],
3837
+ description: 'Level of detail. "summary" (default): identity + decoded roles only. "claims": adds the raw claims object (key → value). "full": adds per-claim descriptions, granted/revoked roles, tags, and unknown claim keys.'
3838
+ }
3839
+ },
3800
3840
  additionalProperties: false
3801
3841
  };
3802
3842
  var WHOAMI_OUTPUT_SCHEMA = {
3803
3843
  type: 'object',
3804
3844
  required: [
3805
- 'authenticated',
3806
- 'claims',
3807
- 'roles',
3808
- 'claimDetails',
3809
- 'unknownClaimKeys'
3845
+ 'authenticated'
3810
3846
  ],
3811
- description: 'Caller identity, raw token claims, decoded role set, and per-claim descriptions for every key present on the live token.',
3847
+ description: 'Caller identity and decoded role set. Claim-bearing fields (claims, claimDetails, unknownClaimKeys) are present only at the detail level that requests them via the `detail` input.',
3812
3848
  properties: {
3813
3849
  authenticated: {
3814
3850
  type: 'boolean'
@@ -3825,16 +3861,19 @@ var WHOAMI_OUTPUT_SCHEMA = {
3825
3861
  app: {
3826
3862
  type: 'string'
3827
3863
  },
3828
- claims: {
3829
- type: 'object',
3830
- additionalProperties: true
3831
- },
3832
3864
  roles: {
3833
3865
  type: 'array',
3834
3866
  items: {
3835
3867
  type: 'string'
3836
3868
  }
3837
3869
  },
3870
+ claimCount: {
3871
+ type: 'integer'
3872
+ },
3873
+ claims: {
3874
+ type: 'object',
3875
+ additionalProperties: true
3876
+ },
3838
3877
  claimDetails: {
3839
3878
  type: 'array',
3840
3879
  items: {
@@ -4130,6 +4169,11 @@ function _ts_generator$4(thisArg, body) {
4130
4169
  /**
4131
4170
  * Placeholder segment standing for the caller's authenticated uid.
4132
4171
  */ var AUTH_UID_PLACEHOLDER = '{authUid}';
4172
+ /**
4173
+ * Matches a `{flatKey:<param>}` key template, capturing the route param whose
4174
+ * URL value is a whole two-way-flat FirestoreModelKey. Mirror of the build-time
4175
+ * `FLAT_KEY_TOKEN_RE` in `@dereekb/dbx-cli`'s route-model-tag parser.
4176
+ */ var FLAT_KEY_TEMPLATE_RE = RegExp("^\\{flatKey:([a-zA-Z_][a-zA-Z0-9_]*)\\}$", "u");
4133
4177
  // MARK: Factory
4134
4178
  /**
4135
4179
  * Builds the built-in `url-models` MCP tool definition.
@@ -4408,6 +4452,8 @@ function resolveModelEntry(input) {
4408
4452
  result = base;
4409
4453
  } else if (entry.kind === 'id') {
4410
4454
  result = _object_spread$1({}, base, resolveIdKey(input));
4455
+ } else if (entry.kind === 'flatKey') {
4456
+ result = _object_spread$1({}, base, resolveFlatKey(input));
4411
4457
  } else {
4412
4458
  result = _object_spread$1({}, base, resolveFullKey(input));
4413
4459
  }
@@ -4446,6 +4492,57 @@ function resolveIdKey(input) {
4446
4492
  }
4447
4493
  return result;
4448
4494
  }
4495
+ function resolveFlatKey(input) {
4496
+ var _entry_keyTemplate;
4497
+ var entry = input.entry, params = input.params;
4498
+ var match = FLAT_KEY_TEMPLATE_RE.exec((_entry_keyTemplate = entry.keyTemplate) !== null && _entry_keyTemplate !== void 0 ? _entry_keyTemplate : '');
4499
+ var result;
4500
+ if (match === null) {
4501
+ var _entry_keyTemplate1;
4502
+ // Defensive: a flatKey entry whose template is not `{flatKey:<param>}` (a malformed manifest).
4503
+ result = {
4504
+ unresolved: {
4505
+ reason: 'invalid-flat-key',
4506
+ message: "Model `".concat(entry.modelType, "` is a flatKey binding but its key template `").concat((_entry_keyTemplate1 = entry.keyTemplate) !== null && _entry_keyTemplate1 !== void 0 ? _entry_keyTemplate1 : '', "` is not `{flatKey:<param>}`.")
4507
+ }
4508
+ };
4509
+ } else {
4510
+ var paramName = match[1];
4511
+ var value = params[paramName];
4512
+ if (value === undefined || value.length === 0) {
4513
+ result = {
4514
+ unresolved: {
4515
+ reason: 'missing-param',
4516
+ message: "Route param `:".concat(paramName, "` (the flattened model key) was not captured from the URL.")
4517
+ }
4518
+ };
4519
+ } else {
4520
+ // The param value IS a two-way-flat key (`r_<id>_cs_<id>`); un-flatten it back to `r/<id>/cs/<id>`.
4521
+ var key = inferKeyFromTwoWayFlatFirestoreModelKey(value);
4522
+ result = isEvenSegmentKey(key) ? {
4523
+ key: key
4524
+ } : {
4525
+ unresolved: {
4526
+ reason: 'invalid-flat-key',
4527
+ message: "Route param `:".concat(paramName, "` did not decode to a valid FirestoreModelKey (got `").concat(key, "`).")
4528
+ }
4529
+ };
4530
+ }
4531
+ }
4532
+ return result;
4533
+ }
4534
+ /**
4535
+ * Whether a decoded key has the shape of a FirestoreModelKey: a non-empty,
4536
+ * even number of `/`-separated segments (collection/id pairs).
4537
+ *
4538
+ * @param key - The decoded (un-flattened) key.
4539
+ * @returns True when the key has an even, non-zero segment count.
4540
+ */ function isEvenSegmentKey(key) {
4541
+ var segments = key.split('/').filter(function(s) {
4542
+ return s.length > 0;
4543
+ });
4544
+ return segments.length >= 2 && segments.length % 2 === 0;
4545
+ }
4449
4546
  function resolveFullKey(input) {
4450
4547
  var _entry_keyTemplate;
4451
4548
  var entry = input.entry, params = input.params, uid = input.uid;
package/mcp/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/mcp",
3
- "version": "13.18.0",
3
+ "version": "13.20.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.18.0",
6
- "@dereekb/date": "13.18.0",
7
- "@dereekb/firebase": "13.18.0",
8
- "@dereekb/firebase-server": "13.18.0",
9
- "@dereekb/firebase-server/oidc": "13.18.0",
10
- "@dereekb/model": "13.18.0",
11
- "@dereekb/nestjs": "13.18.0",
12
- "@dereekb/rxjs": "13.18.0",
13
- "@dereekb/util": "13.18.0",
14
- "@dereekb/zoho": "13.18.0",
5
+ "@dereekb/analytics": "13.20.0",
6
+ "@dereekb/date": "13.20.0",
7
+ "@dereekb/firebase": "13.20.0",
8
+ "@dereekb/firebase-server": "13.20.0",
9
+ "@dereekb/firebase-server/oidc": "13.20.0",
10
+ "@dereekb/model": "13.20.0",
11
+ "@dereekb/nestjs": "13.20.0",
12
+ "@dereekb/rxjs": "13.20.0",
13
+ "@dereekb/util": "13.20.0",
14
+ "@dereekb/zoho": "13.20.0",
15
15
  "@modelcontextprotocol/sdk": "1.29.0",
16
16
  "@nestjs/common": "^11.1.19",
17
17
  "@nestjs/core": "^11.1.19",
@@ -1 +1,19 @@
1
+ /**
2
+ * Public entry for `@dereekb/firebase-server/mcp`.
3
+ *
4
+ * Packaging note: the published `exports["./mcp"]` maps the `import` condition to
5
+ * the real ESM build (`./mcp/index.esm.js`), NOT the `.cjs.mjs` CJS re-export
6
+ * shim, so a downstream `import { matchRouteManifestUrl, ROUTE_MANIFEST_VERSION }
7
+ * from '@dereekb/firebase-server/mcp'` exposes statically-analyzable named
8
+ * exports under vitest. The shim (`export * from './index.cjs.js'`) resolved to
9
+ * `undefined` for this subpath under vitest's externalized SSR resolution, while
10
+ * plain Node and the root `@dereekb/firebase-server` entry were unaffected.
11
+ * `require` / the `default` condition still resolves to the CJS build for CJS
12
+ * consumers (the NestJS server runtime).
13
+ *
14
+ * Fallback: if a downstream still sees undefined named imports (e.g. consuming a
15
+ * build that predates this fix), inline the package in its vitest config so Vite
16
+ * transforms it through the `module` (ESM) condition:
17
+ * `test: { server: { deps: { inline: [/@dereekb\/firebase-server/] } } }`.
18
+ */
1
19
  export * from './lib';
@@ -12,12 +12,13 @@
12
12
  * manifests whose `version` does not match. Mirror in `@dereekb/dbx-cli`'s
13
13
  * `ROUTE_MANIFEST_VERSION` — bump both together.
14
14
  */
15
- export declare const ROUTE_MANIFEST_VERSION: 1;
15
+ export declare const ROUTE_MANIFEST_VERSION: 2;
16
16
  /**
17
- * Whether a route-model entry resolves to a promoted id, a full key, or a
18
- * keyless list. Mirror of `@dereekb/dbx-cli`'s `RouteModelKind`.
17
+ * Whether a route-model entry resolves to a promoted id, a full key, a
18
+ * single-param flattened key, or a keyless list. Mirror of `@dereekb/dbx-cli`'s
19
+ * `RouteModelKind`.
19
20
  */
20
- export type RouteModelKind = 'id' | 'key' | 'list';
21
+ export type RouteModelKind = 'id' | 'key' | 'flatKey' | 'list';
21
22
  /**
22
23
  * One model an app page renders. Structural mirror of `@dereekb/dbx-cli`'s
23
24
  * `RouteManifestModelEntry`.
@@ -19,7 +19,7 @@ export declare const URL_MODELS_DISPATCH_MODEL_TYPE = "route";
19
19
  /**
20
20
  * Why a model binding could not be resolved into a concrete key.
21
21
  */
22
- export type UrlModelUnresolvedReason = 'missing-param' | 'subcollection-requires-key-template' | 'unknown-model-type' | 'auth-required';
22
+ export type UrlModelUnresolvedReason = 'missing-param' | 'subcollection-requires-key-template' | 'unknown-model-type' | 'auth-required' | 'invalid-flat-key';
23
23
  /**
24
24
  * One resolved model binding for a matched page.
25
25
  */
@@ -45,7 +45,17 @@ export interface WhoamiClaimDetail {
45
45
  readonly tags: readonly string[];
46
46
  }
47
47
  /**
48
- * Output payload for the `whoami` tool.
48
+ * Requested verbosity for the `whoami` tool, set via the `detail` input.
49
+ *
50
+ * - `summary` (default) — identity + decoded roles + a claim count. No claims.
51
+ * - `claims` — adds the raw claims object (key → value).
52
+ * - `full` — adds per-claim descriptions, granted/revoked roles, tags, and unknown claim keys.
53
+ */
54
+ export type WhoamiDetailLevel = 'summary' | 'claims' | 'full';
55
+ /**
56
+ * Output payload for the `whoami` tool. The claim-bearing fields are populated
57
+ * only at the detail level that requests them, so the structured payload scales
58
+ * with the `detail` input the same way the rendered text does.
49
59
  */
50
60
  export interface WhoamiToolOutput {
51
61
  readonly authenticated: boolean;
@@ -53,10 +63,30 @@ export interface WhoamiToolOutput {
53
63
  readonly email?: string;
54
64
  readonly emailVerified?: boolean;
55
65
  readonly app?: string;
56
- readonly claims: Readonly<Record<string, unknown>>;
57
- readonly roles: readonly string[];
58
- readonly claimDetails: readonly WhoamiClaimDetail[];
59
- readonly unknownClaimKeys: readonly string[];
66
+ /**
67
+ * Decoded role set. Present for authenticated callers (empty when no role
68
+ * reader is configured); omitted for anonymous callers.
69
+ */
70
+ readonly roles?: readonly string[];
71
+ /**
72
+ * Count of custom claim keys present on the live token, excluding the reserved
73
+ * `email` / `email_verified` keys. Present for authenticated callers.
74
+ */
75
+ readonly claimCount?: number;
76
+ /**
77
+ * Raw claims object copied from the live token. Included when `detail` is
78
+ * `claims` or `full`.
79
+ */
80
+ readonly claims?: Readonly<Record<string, unknown>>;
81
+ /**
82
+ * Per-claim descriptions for every manifest-known key present on the token.
83
+ * Included when `detail` is `full`.
84
+ */
85
+ readonly claimDetails?: readonly WhoamiClaimDetail[];
86
+ /**
87
+ * Token claim keys with no matching manifest entry. Included when `detail` is `full`.
88
+ */
89
+ readonly unknownClaimKeys?: readonly string[];
60
90
  }
61
91
  /**
62
92
  * Builds the built-in `whoami` MCP tool definition. Returns the calling
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/model",
3
- "version": "13.18.0",
3
+ "version": "13.20.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.18.0",
6
- "@dereekb/date": "13.18.0",
7
- "@dereekb/firebase": "13.18.0",
8
- "@dereekb/firebase-server": "13.18.0",
9
- "@dereekb/model": "13.18.0",
10
- "@dereekb/nestjs": "13.18.0",
11
- "@dereekb/rxjs": "13.18.0",
12
- "@dereekb/util": "13.18.0",
5
+ "@dereekb/analytics": "13.20.0",
6
+ "@dereekb/date": "13.20.0",
7
+ "@dereekb/firebase": "13.20.0",
8
+ "@dereekb/firebase-server": "13.20.0",
9
+ "@dereekb/model": "13.20.0",
10
+ "@dereekb/nestjs": "13.20.0",
11
+ "@dereekb/rxjs": "13.20.0",
12
+ "@dereekb/util": "13.20.0",
13
13
  "@nestjs/common": "^11.1.19",
14
14
  "@nestjs/config": "^4.0.4",
15
15
  "archiver": "^7.0.1",
@@ -17,7 +17,7 @@
17
17
  "firebase-functions": "^7.2.5",
18
18
  "make-error": "^1.3.6",
19
19
  "@cantoo/pdf-lib": "^2.6.5",
20
- "sharp": "0.34.5"
20
+ "sharp": "0.35.2"
21
21
  },
22
22
  "exports": {
23
23
  "./package.json": "./package.json",
package/oidc/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/oidc",
3
- "version": "13.18.0",
3
+ "version": "13.20.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.18.0",
6
- "@dereekb/date": "13.18.0",
7
- "@dereekb/firebase": "13.18.0",
8
- "@dereekb/firebase-server": "13.18.0",
9
- "@dereekb/model": "13.18.0",
10
- "@dereekb/nestjs": "13.18.0",
11
- "@dereekb/rxjs": "13.18.0",
12
- "@dereekb/util": "13.18.0",
13
- "@dereekb/zoho": "13.18.0",
5
+ "@dereekb/analytics": "13.20.0",
6
+ "@dereekb/date": "13.20.0",
7
+ "@dereekb/firebase": "13.20.0",
8
+ "@dereekb/firebase-server": "13.20.0",
9
+ "@dereekb/model": "13.20.0",
10
+ "@dereekb/nestjs": "13.20.0",
11
+ "@dereekb/rxjs": "13.20.0",
12
+ "@dereekb/util": "13.20.0",
13
+ "@dereekb/zoho": "13.20.0",
14
14
  "@nestjs/common": "^11.1.19",
15
15
  "@nestjs/config": "^4.0.4",
16
16
  "express": "^5.2.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server",
3
- "version": "13.18.0",
3
+ "version": "13.20.0",
4
4
  "sideEffects": false,
5
5
  "exports": {
6
6
  "./test": {
@@ -42,7 +42,7 @@
42
42
  "./mcp": {
43
43
  "module": "./mcp/index.esm.js",
44
44
  "types": "./mcp/index.d.ts",
45
- "import": "./mcp/index.cjs.mjs",
45
+ "import": "./mcp/index.esm.js",
46
46
  "default": "./mcp/index.cjs.js"
47
47
  },
48
48
  "./package.json": "./package.json",
@@ -57,15 +57,15 @@
57
57
  "types": "./src/index.d.ts",
58
58
  "peerDependencies": {
59
59
  "@cantoo/pdf-lib": "^2.6.5",
60
- "@dereekb/analytics": "13.18.0",
61
- "@dereekb/date": "13.18.0",
62
- "@dereekb/dbx-core": "13.18.0",
63
- "@dereekb/firebase": "13.18.0",
64
- "@dereekb/model": "13.18.0",
65
- "@dereekb/nestjs": "13.18.0",
66
- "@dereekb/rxjs": "13.18.0",
67
- "@dereekb/util": "13.18.0",
68
- "@dereekb/zoho": "13.18.0",
60
+ "@dereekb/analytics": "13.20.0",
61
+ "@dereekb/date": "13.20.0",
62
+ "@dereekb/dbx-core": "13.20.0",
63
+ "@dereekb/firebase": "13.20.0",
64
+ "@dereekb/model": "13.20.0",
65
+ "@dereekb/nestjs": "13.20.0",
66
+ "@dereekb/rxjs": "13.20.0",
67
+ "@dereekb/util": "13.20.0",
68
+ "@dereekb/zoho": "13.20.0",
69
69
  "@google-cloud/firestore": "^7.11.6",
70
70
  "@google-cloud/storage": "^7.19.0",
71
71
  "@modelcontextprotocol/sdk": "1.29.0",
@@ -86,7 +86,7 @@
86
86
  "nanoid": "^5.1.9",
87
87
  "oidc-provider": "^9.8.2",
88
88
  "rxjs": "^7.8.2",
89
- "sharp": "0.34.5",
89
+ "sharp": "0.35.2",
90
90
  "supertest": "^7.2.2",
91
91
  "ts-essentials": "^10.2.0"
92
92
  },
package/test/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/test",
3
- "version": "13.18.0",
3
+ "version": "13.20.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.18.0",
6
- "@dereekb/date": "13.18.0",
7
- "@dereekb/firebase": "13.18.0",
8
- "@dereekb/firebase-server": "13.18.0",
9
- "@dereekb/firebase-server/oidc": "13.18.0",
10
- "@dereekb/model": "13.18.0",
11
- "@dereekb/nestjs": "13.18.0",
12
- "@dereekb/rxjs": "13.18.0",
13
- "@dereekb/util": "13.18.0",
5
+ "@dereekb/analytics": "13.20.0",
6
+ "@dereekb/date": "13.20.0",
7
+ "@dereekb/firebase": "13.20.0",
8
+ "@dereekb/firebase-server": "13.20.0",
9
+ "@dereekb/firebase-server/oidc": "13.20.0",
10
+ "@dereekb/model": "13.20.0",
11
+ "@dereekb/nestjs": "13.20.0",
12
+ "@dereekb/rxjs": "13.20.0",
13
+ "@dereekb/util": "13.20.0",
14
14
  "@google-cloud/firestore": "^7.11.6",
15
15
  "@google-cloud/storage": "^7.19.0",
16
16
  "@nestjs/common": "^11.1.19",
@@ -23,7 +23,7 @@
23
23
  "supertest": "^7.2.2"
24
24
  },
25
25
  "devDependencies": {
26
- "@dereekb/nestjs": "13.18.0"
26
+ "@dereekb/nestjs": "13.20.0"
27
27
  },
28
28
  "exports": {
29
29
  "./package.json": "./package.json",
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/twilio",
3
- "version": "13.18.0",
3
+ "version": "13.20.0",
4
4
  "peerDependencies": {
5
- "@dereekb/date": "13.18.0",
6
- "@dereekb/firebase": "13.18.0",
7
- "@dereekb/firebase-server": "13.18.0",
8
- "@dereekb/model": "13.18.0",
9
- "@dereekb/nestjs": "13.18.0",
10
- "@dereekb/rxjs": "13.18.0",
11
- "@dereekb/util": "13.18.0"
5
+ "@dereekb/date": "13.20.0",
6
+ "@dereekb/firebase": "13.20.0",
7
+ "@dereekb/firebase-server": "13.20.0",
8
+ "@dereekb/model": "13.20.0",
9
+ "@dereekb/nestjs": "13.20.0",
10
+ "@dereekb/rxjs": "13.20.0",
11
+ "@dereekb/util": "13.20.0"
12
12
  },
13
13
  "exports": {
14
14
  "./package.json": "./package.json",
package/zoho/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/zoho",
3
- "version": "13.18.0",
3
+ "version": "13.20.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.18.0",
6
- "@dereekb/date": "13.18.0",
7
- "@dereekb/model": "13.18.0",
8
- "@dereekb/nestjs": "13.18.0",
9
- "@dereekb/rxjs": "13.18.0",
10
- "@dereekb/firebase": "13.18.0",
11
- "@dereekb/util": "13.18.0",
12
- "@dereekb/zoho": "13.18.0"
5
+ "@dereekb/analytics": "13.20.0",
6
+ "@dereekb/date": "13.20.0",
7
+ "@dereekb/model": "13.20.0",
8
+ "@dereekb/nestjs": "13.20.0",
9
+ "@dereekb/rxjs": "13.20.0",
10
+ "@dereekb/firebase": "13.20.0",
11
+ "@dereekb/util": "13.20.0",
12
+ "@dereekb/zoho": "13.20.0"
13
13
  },
14
14
  "exports": {
15
15
  "./package.json": "./package.json",