@dereekb/firebase-server 13.37.0 → 13.39.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.
Files changed (57) hide show
  1. package/calcom/package.json +15 -15
  2. package/discord/package.json +15 -15
  3. package/index.esm.js +1716 -512
  4. package/mailgun/package.json +14 -14
  5. package/mcp/index.esm.js +561 -40
  6. package/mcp/package.json +16 -16
  7. package/mcp/src/lib/mcp.config.d.ts +22 -1
  8. package/mcp/src/lib/service/index.d.ts +1 -0
  9. package/mcp/src/lib/service/mcp.server.factory.d.ts +3 -2
  10. package/mcp/src/lib/service/tools/mcp.tool.model-roles.d.ts +95 -0
  11. package/model/package.json +14 -14
  12. package/model/src/lib/notification/notification.module.d.ts +1 -1
  13. package/oidc/package.json +15 -15
  14. package/package.json +36 -45
  15. package/src/lib/env/env.service.d.ts +27 -3
  16. package/src/lib/nest/controller/api.scope.d.ts +63 -0
  17. package/src/lib/nest/controller/index.d.ts +2 -0
  18. package/src/lib/nest/controller/model/model.api.get.service.d.ts +146 -1
  19. package/src/lib/nest/controller/model/model.api.scope.d.ts +3 -2
  20. package/src/lib/nest/controller/session/index.d.ts +4 -0
  21. package/src/lib/nest/controller/session/session.api.config.d.ts +104 -0
  22. package/src/lib/nest/controller/session/session.api.controller.d.ts +26 -0
  23. package/src/lib/nest/controller/session/session.api.module.d.ts +49 -0
  24. package/src/lib/nest/controller/session/session.api.service.d.ts +71 -0
  25. package/test/package.json +16 -16
  26. package/twilio/package.json +13 -13
  27. package/zoho/package.json +15 -15
  28. package/calcom/index.cjs.default.js +0 -1
  29. package/calcom/index.cjs.js +0 -1451
  30. package/calcom/index.cjs.mjs +0 -2
  31. package/discord/index.cjs.default.js +0 -1
  32. package/discord/index.cjs.js +0 -761
  33. package/discord/index.cjs.mjs +0 -2
  34. package/index.cjs.default.js +0 -1
  35. package/index.cjs.js +0 -13491
  36. package/index.cjs.mjs +0 -2
  37. package/mailgun/index.cjs.default.js +0 -1
  38. package/mailgun/index.cjs.js +0 -436
  39. package/mailgun/index.cjs.mjs +0 -2
  40. package/mcp/index.cjs.default.js +0 -1
  41. package/mcp/index.cjs.js +0 -8254
  42. package/mcp/index.cjs.mjs +0 -2
  43. package/model/index.cjs.default.js +0 -1
  44. package/model/index.cjs.js +0 -19965
  45. package/model/index.cjs.mjs +0 -2
  46. package/oidc/index.cjs.default.js +0 -1
  47. package/oidc/index.cjs.js +0 -8085
  48. package/oidc/index.cjs.mjs +0 -2
  49. package/test/index.cjs.default.js +0 -1
  50. package/test/index.cjs.js +0 -5728
  51. package/test/index.cjs.mjs +0 -2
  52. package/twilio/index.cjs.default.js +0 -1
  53. package/twilio/index.cjs.js +0 -404
  54. package/twilio/index.cjs.mjs +0 -2
  55. package/zoho/index.cjs.default.js +0 -1
  56. package/zoho/index.cjs.js +0 -1810
  57. package/zoho/index.cjs.mjs +0 -2
package/mcp/package.json CHANGED
@@ -1,17 +1,18 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/mcp",
3
- "version": "13.37.0",
3
+ "version": "13.39.0",
4
+ "type": "module",
4
5
  "peerDependencies": {
5
- "@dereekb/analytics": "13.37.0",
6
- "@dereekb/date": "13.37.0",
7
- "@dereekb/firebase": "13.37.0",
8
- "@dereekb/firebase-server": "13.37.0",
9
- "@dereekb/firebase-server/oidc": "13.37.0",
10
- "@dereekb/model": "13.37.0",
11
- "@dereekb/nestjs": "13.37.0",
12
- "@dereekb/rxjs": "13.37.0",
13
- "@dereekb/util": "13.37.0",
14
- "@dereekb/zoho": "13.37.0",
6
+ "@dereekb/analytics": "13.39.0",
7
+ "@dereekb/date": "13.39.0",
8
+ "@dereekb/firebase": "13.39.0",
9
+ "@dereekb/firebase-server": "13.39.0",
10
+ "@dereekb/firebase-server/oidc": "13.39.0",
11
+ "@dereekb/model": "13.39.0",
12
+ "@dereekb/nestjs": "13.39.0",
13
+ "@dereekb/rxjs": "13.39.0",
14
+ "@dereekb/util": "13.39.0",
15
+ "@dereekb/zoho": "13.39.0",
15
16
  "@modelcontextprotocol/node": "2.0.0",
16
17
  "@modelcontextprotocol/server": "2.0.0",
17
18
  "@nestjs/common": "^11.1.19",
@@ -23,13 +24,12 @@
23
24
  "exports": {
24
25
  "./package.json": "./package.json",
25
26
  ".": {
26
- "module": "./index.esm.js",
27
27
  "types": "./index.d.ts",
28
- "import": "./index.cjs.mjs",
29
- "default": "./index.cjs.js"
28
+ "import": "./index.esm.js",
29
+ "default": "./index.esm.js"
30
30
  }
31
31
  },
32
32
  "module": "./index.esm.js",
33
- "main": "./index.cjs.js",
33
+ "main": "./index.esm.js",
34
34
  "types": "./index.d.ts"
35
- }
35
+ }
@@ -1,4 +1,5 @@
1
- import { type AuthClaims, type AuthRoleSet } from '@dereekb/util';
1
+ import { type AuthClaims, type AuthRoleSet, type Maybe, type PromiseOrValue } from '@dereekb/util';
2
+ import { type FirebaseServerAuthData } from '@dereekb/firebase-server';
2
3
  import { type FirestoreModelType, type OidcModelScopeRequirement, type OidcScope, type OidcScopeTerm } from '@dereekb/firebase';
3
4
  /**
4
5
  * Default path the MCP Streamable HTTP transport is mounted at.
@@ -238,3 +239,23 @@ export type McpAuthRoleReader = (claims: AuthClaims) => AuthRoleSet;
238
239
  * NestJS injection token for the optional {@link McpAuthRoleReader} provider.
239
240
  */
240
241
  export declare const MCP_AUTH_ROLE_READER = "MCP_AUTH_ROLE_READER";
242
+ /**
243
+ * Signature for the optional predicate that authorizes `model-roles` calls which target another
244
+ * user's uid.
245
+ *
246
+ * `model-roles` resolves permissions for the calling user by default, which is always safe. Passing
247
+ * a `uid` asks the server "what can *that* user do here?" — an answer that leaks the target's
248
+ * effective access, so it is gated behind this app-supplied predicate rather than being open.
249
+ *
250
+ * Receives the calling request's auth data (`undefined` for an unauthenticated request) and returns
251
+ * true if that caller may resolve roles for arbitrary uids. Typically an admin check, e.g.
252
+ * `(auth) => authRoleClaimsService.toRoles(auth?.token ?? {}).has('admin')`.
253
+ *
254
+ * When no predicate is provided the `uid` parameter fails closed for every caller — the tool is
255
+ * still registered and still answers for the caller themselves.
256
+ */
257
+ export type McpModelRolesTargetUidPredicate = (auth: Maybe<FirebaseServerAuthData>) => PromiseOrValue<boolean>;
258
+ /**
259
+ * NestJS injection token for the optional {@link McpModelRolesTargetUidPredicate} provider.
260
+ */
261
+ export declare const MCP_MODEL_ROLES_TARGET_UID_PREDICATE = "MCP_MODEL_ROLES_TARGET_UID_PREDICATE";
@@ -7,6 +7,7 @@ export * from './mcp.server.factory';
7
7
  export * from './mcp.tool-generator';
8
8
  export * from './mcp.visibility';
9
9
  export * from './tools/mcp.tool.model-get';
10
+ export * from './tools/mcp.tool.model-roles';
10
11
  export * from './tools/mcp.tool.model-info';
11
12
  export * from './tools/mcp.tool.model-decode';
12
13
  export * from './tools/mcp.tool.enum-info';
@@ -1,7 +1,7 @@
1
1
  import { McpServer } from '@modelcontextprotocol/server';
2
2
  import { type Request } from 'express';
3
3
  import { ModelApiCallModelDispatchService, ModelApiGetService, FirebaseServerStorageService, type FirebaseServerAuthData } from '@dereekb/firebase-server';
4
- import { McpModuleConfig, type McpAuthRoleReader } from '../mcp.config';
4
+ import { McpModuleConfig, type McpAuthRoleReader, type McpModelRolesTargetUidPredicate } from '../mcp.config';
5
5
  import { type McpAnalyticsService } from './analytics/mcp.analytics.handler';
6
6
  /**
7
7
  * Optional per-request context passed when invoking the MCP server through a
@@ -28,6 +28,7 @@ export declare class McpServerFactoryService {
28
28
  private readonly modelApiGetService?;
29
29
  private readonly roleReader?;
30
30
  private readonly storageService?;
31
+ private readonly modelRolesTargetUidPredicate?;
31
32
  private readonly _logger;
32
33
  private _cachedTools;
33
34
  private _cachedStaticTools;
@@ -42,7 +43,7 @@ export declare class McpServerFactoryService {
42
43
  private _warnedMissingRoleReader;
43
44
  private _resolvedReasonConfig?;
44
45
  private readonly _analyticsService;
45
- constructor(mcpConfig: McpModuleConfig, dispatchService: ModelApiCallModelDispatchService, modelApiGetService?: ModelApiGetService | undefined, roleReader?: McpAuthRoleReader | undefined, analyticsService?: McpAnalyticsService, storageService?: FirebaseServerStorageService | undefined);
46
+ constructor(mcpConfig: McpModuleConfig, dispatchService: ModelApiCallModelDispatchService, modelApiGetService?: ModelApiGetService | undefined, roleReader?: McpAuthRoleReader | undefined, analyticsService?: McpAnalyticsService, storageService?: FirebaseServerStorageService | undefined, modelRolesTargetUidPredicate?: McpModelRolesTargetUidPredicate | undefined);
46
47
  /**
47
48
  * Builds a configured MCP server with tool listing + dispatch handlers wired up.
48
49
  *
@@ -0,0 +1,95 @@
1
+ import { type Maybe, type PromiseOrValue } from '@dereekb/util';
2
+ import { type FirebaseAuthUserId, type FirestoreModelIdentity, type FirestoreModelKey, type FirestoreModelType } from '@dereekb/firebase';
3
+ import { type ModelAccessMultiRoleMapResult, type FirebaseServerAuthData } from '@dereekb/firebase-server';
4
+ import { type McpToolDefinition } from '../mcp.tool-generator';
5
+ /**
6
+ * Reserved tool name for the built-in `model-roles` static tool.
7
+ */
8
+ export declare const MODEL_ROLES_TOOL_NAME = "model-roles";
9
+ /**
10
+ * Synthetic call type used in the tool's dispatch identity. Distinct from `model-get`'s `get` so
11
+ * visibility predicates can target the two independently.
12
+ */
13
+ export declare const MODEL_ROLES_DISPATCH_CALL = "roles";
14
+ /**
15
+ * Synthetic model type used in the tool's dispatch identity. Mirrors {@link MODEL_GET_DISPATCH_MODEL_TYPE}
16
+ * — the tool isn't bound to one model type, so the literal "model" stands in.
17
+ */
18
+ export declare const MODEL_ROLES_DISPATCH_MODEL_TYPE = "model";
19
+ /**
20
+ * Maximum number of keys accepted per `model-roles` call. Role resolution runs the model's real
21
+ * permission delegate per key (which may itself read parent documents), so this is deliberately
22
+ * lower than the `model-get` batch size.
23
+ */
24
+ export declare const MCP_MODEL_ROLES_MAX_KEYS = 25;
25
+ /**
26
+ * Resolves granted role maps for a batch of keys. Signature matches
27
+ * `ModelApiGetService.readRoleMaps` so the service method can be passed directly.
28
+ */
29
+ export type McpModelRolesReadRoleMaps = (params: {
30
+ readonly modelType: FirestoreModelType;
31
+ readonly keys: FirestoreModelKey[];
32
+ readonly auth: Maybe<FirebaseServerAuthData>;
33
+ readonly targetUid?: Maybe<FirebaseAuthUserId>;
34
+ }) => Promise<ModelAccessMultiRoleMapResult>;
35
+ /**
36
+ * Lookup that resolves a `modelType` to its registered {@link FirestoreModelIdentity}, so bare ids
37
+ * can be promoted to full keys. Mirrors `McpModelGetResolveIdentity`.
38
+ */
39
+ export type McpModelRolesResolveIdentity = (modelType: FirestoreModelType, auth: Maybe<FirebaseServerAuthData>) => Maybe<FirestoreModelIdentity>;
40
+ /**
41
+ * Predicate authorizing a caller to resolve roles for a uid other than their own. Mirrors
42
+ * `McpModelRolesTargetUidPredicate` from the module config; redeclared here so this module does not
43
+ * depend on the config module.
44
+ */
45
+ export type McpModelRolesTargetUidCheck = (auth: Maybe<FirebaseServerAuthData>) => PromiseOrValue<boolean>;
46
+ /**
47
+ * Constructor dependencies for {@link createModelRolesTool}.
48
+ */
49
+ export interface CreateModelRolesToolDeps {
50
+ /**
51
+ * Resolves the granted role maps for a batch of keys.
52
+ */
53
+ readonly readRoleMaps: McpModelRolesReadRoleMaps;
54
+ /**
55
+ * Resolves the registered identity for a model type so bare ids can be promoted into full keys.
56
+ */
57
+ readonly resolveIdentity: McpModelRolesResolveIdentity;
58
+ /**
59
+ * Authorizes use of the `uid` parameter. Omitted means the parameter fails closed for everyone;
60
+ * the tool still answers for the calling user.
61
+ */
62
+ readonly canTargetOtherUids?: Maybe<McpModelRolesTargetUidCheck>;
63
+ }
64
+ /**
65
+ * Shape of the `model-roles` tool input.
66
+ */
67
+ export interface ModelRolesToolInput {
68
+ readonly modelType: string;
69
+ readonly keys: ReadonlyArray<string>;
70
+ readonly uid?: string;
71
+ }
72
+ /**
73
+ * Builds the built-in `model-roles` MCP tool definition.
74
+ *
75
+ * Answers "what is this user actually allowed to do with this document?" by running the same
76
+ * `roleMapForModel()` delegate the permission-checked read/write paths use, and returning the
77
+ * resolved roles instead of consuming them. Because the model's real delegate runs, derived and
78
+ * cascading roles appear exactly as the API grants them — there is no second implementation of the
79
+ * rules to drift.
80
+ *
81
+ * Two things it disambiguates that a plain read cannot:
82
+ * - **Missing vs. forbidden.** A key that resolves to a missing document returns
83
+ * `exists: false, roles: []` rather than an error, so "the document is not there" reads
84
+ * differently from "it is there and you have no access" (`exists: true, roles: []`).
85
+ * - **Full access.** Admin short-circuits that grant the full-access marker come back as
86
+ * `fullAccess: true` rather than as an opaque enumerated set.
87
+ *
88
+ * By default roles are resolved for the calling user. Passing `uid` resolves them for another user
89
+ * and is gated behind {@link CreateModelRolesToolDeps.canTargetOtherUids}.
90
+ *
91
+ * @param deps - Role-map reader, identity resolver, and the target-uid authorization predicate.
92
+ * @returns A statically-registered {@link McpToolDefinition} ready to be appended to the MCP
93
+ * server factory's tool registry.
94
+ */
95
+ export declare function createModelRolesTool(deps: CreateModelRolesToolDeps): McpToolDefinition;
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/model",
3
- "version": "13.37.0",
3
+ "version": "13.39.0",
4
+ "type": "module",
4
5
  "peerDependencies": {
5
- "@dereekb/analytics": "13.37.0",
6
- "@dereekb/date": "13.37.0",
7
- "@dereekb/firebase": "13.37.0",
8
- "@dereekb/firebase-server": "13.37.0",
9
- "@dereekb/model": "13.37.0",
10
- "@dereekb/nestjs": "13.37.0",
11
- "@dereekb/rxjs": "13.37.0",
12
- "@dereekb/util": "13.37.0",
6
+ "@dereekb/analytics": "13.39.0",
7
+ "@dereekb/date": "13.39.0",
8
+ "@dereekb/firebase": "13.39.0",
9
+ "@dereekb/firebase-server": "13.39.0",
10
+ "@dereekb/model": "13.39.0",
11
+ "@dereekb/nestjs": "13.39.0",
12
+ "@dereekb/rxjs": "13.39.0",
13
+ "@dereekb/util": "13.39.0",
13
14
  "@nestjs/common": "^11.1.19",
14
15
  "@nestjs/config": "^4.0.4",
15
16
  "archiver": "^7.0.1",
@@ -23,13 +24,12 @@
23
24
  "exports": {
24
25
  "./package.json": "./package.json",
25
26
  ".": {
26
- "module": "./index.esm.js",
27
27
  "types": "./index.d.ts",
28
- "import": "./index.cjs.mjs",
29
- "default": "./index.cjs.js"
28
+ "import": "./index.esm.js",
29
+ "default": "./index.esm.js"
30
30
  }
31
31
  },
32
32
  "module": "./index.esm.js",
33
- "main": "./index.cjs.js",
33
+ "main": "./index.esm.js",
34
34
  "types": "./index.d.ts"
35
- }
35
+ }
@@ -35,7 +35,7 @@ export declare function notificationServerActionsContextFactory(context: BaseNot
35
35
  notificationLoggedEventDayCollectionGroup: import("@dereekb/firebase").NotificationLoggedEventDayFirestoreCollectionGroup;
36
36
  notificationLoggedEventDayPagedItemsCollectionFactory: import("@dereekb/firebase").NotificationLoggedEventDayPagedItemsFirestoreCollectionFactory;
37
37
  notificationLoggedEventDayPageCollectionGroup: import("@dereekb/firebase").NotificationLoggedEventDayPageFirestoreCollectionGroup;
38
- authService: import("dist/packages/firebase-server/src").FirebaseServerAuthService<import("dist/packages/firebase-server/src").FirebaseServerAuthUserContext, import("dist/packages/firebase-server/src").FirebaseServerAuthContext<import("dist/packages/firebase-server/src").FirebaseServerAuthUserContext>>;
38
+ authService: import("@dereekb/firebase-server").FirebaseServerAuthService<import("@dereekb/firebase-server").FirebaseServerAuthUserContext, import("@dereekb/firebase-server").FirebaseServerAuthContext<import("@dereekb/firebase-server").FirebaseServerAuthUserContext>>;
39
39
  firestoreContext: import("@dereekb/firebase").FirestoreContext<import("@dereekb/firebase").Firestore>;
40
40
  };
41
41
  /**
package/oidc/package.json CHANGED
@@ -1,16 +1,17 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/oidc",
3
- "version": "13.37.0",
3
+ "version": "13.39.0",
4
+ "type": "module",
4
5
  "peerDependencies": {
5
- "@dereekb/analytics": "13.37.0",
6
- "@dereekb/date": "13.37.0",
7
- "@dereekb/firebase": "13.37.0",
8
- "@dereekb/firebase-server": "13.37.0",
9
- "@dereekb/model": "13.37.0",
10
- "@dereekb/nestjs": "13.37.0",
11
- "@dereekb/rxjs": "13.37.0",
12
- "@dereekb/util": "13.37.0",
13
- "@dereekb/zoho": "13.37.0",
6
+ "@dereekb/analytics": "13.39.0",
7
+ "@dereekb/date": "13.39.0",
8
+ "@dereekb/firebase": "13.39.0",
9
+ "@dereekb/firebase-server": "13.39.0",
10
+ "@dereekb/model": "13.39.0",
11
+ "@dereekb/nestjs": "13.39.0",
12
+ "@dereekb/rxjs": "13.39.0",
13
+ "@dereekb/util": "13.39.0",
14
+ "@dereekb/zoho": "13.39.0",
14
15
  "@nestjs/common": "^11.1.19",
15
16
  "@nestjs/config": "^4.0.4",
16
17
  "express": "^5.2.1",
@@ -21,13 +22,12 @@
21
22
  "exports": {
22
23
  "./package.json": "./package.json",
23
24
  ".": {
24
- "module": "./index.esm.js",
25
25
  "types": "./index.d.ts",
26
- "import": "./index.cjs.mjs",
27
- "default": "./index.cjs.js"
26
+ "import": "./index.esm.js",
27
+ "default": "./index.esm.js"
28
28
  }
29
29
  },
30
30
  "module": "./index.esm.js",
31
- "main": "./index.cjs.js",
31
+ "main": "./index.esm.js",
32
32
  "types": "./index.d.ts"
33
- }
33
+ }
package/package.json CHANGED
@@ -1,85 +1,74 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server",
3
- "version": "13.37.0",
3
+ "version": "13.39.0",
4
+ "type": "module",
4
5
  "sideEffects": false,
5
6
  "exports": {
6
7
  "./test": {
7
- "module": "./test/index.esm.js",
8
8
  "types": "./test/index.d.ts",
9
- "import": "./test/index.cjs.mjs",
10
- "default": "./test/index.cjs.js"
9
+ "import": "./test/index.esm.js",
10
+ "default": "./test/index.esm.js"
11
11
  },
12
12
  "./calcom": {
13
- "module": "./calcom/index.esm.js",
14
13
  "types": "./calcom/index.d.ts",
15
- "import": "./calcom/index.cjs.mjs",
16
- "default": "./calcom/index.cjs.js"
14
+ "import": "./calcom/index.esm.js",
15
+ "default": "./calcom/index.esm.js"
17
16
  },
18
17
  "./discord": {
19
- "module": "./discord/index.esm.js",
20
18
  "types": "./discord/index.d.ts",
21
- "import": "./discord/index.cjs.mjs",
22
- "default": "./discord/index.cjs.js"
19
+ "import": "./discord/index.esm.js",
20
+ "default": "./discord/index.esm.js"
23
21
  },
24
22
  "./mailgun": {
25
- "module": "./mailgun/index.esm.js",
26
23
  "types": "./mailgun/index.d.ts",
27
- "import": "./mailgun/index.cjs.mjs",
28
- "default": "./mailgun/index.cjs.js"
24
+ "import": "./mailgun/index.esm.js",
25
+ "default": "./mailgun/index.esm.js"
29
26
  },
30
27
  "./twilio": {
31
- "module": "./twilio/index.esm.js",
32
28
  "types": "./twilio/index.d.ts",
33
- "import": "./twilio/index.cjs.mjs",
34
- "default": "./twilio/index.cjs.js"
29
+ "import": "./twilio/index.esm.js",
30
+ "default": "./twilio/index.esm.js"
35
31
  },
36
32
  "./zoho": {
37
- "module": "./zoho/index.esm.js",
38
33
  "types": "./zoho/index.d.ts",
39
- "import": "./zoho/index.cjs.mjs",
40
- "default": "./zoho/index.cjs.js"
34
+ "import": "./zoho/index.esm.js",
35
+ "default": "./zoho/index.esm.js"
41
36
  },
42
37
  "./model": {
43
- "module": "./model/index.esm.js",
44
38
  "types": "./model/index.d.ts",
45
- "import": "./model/index.cjs.mjs",
46
- "default": "./model/index.cjs.js"
39
+ "import": "./model/index.esm.js",
40
+ "default": "./model/index.esm.js"
47
41
  },
48
42
  "./oidc": {
49
- "module": "./oidc/index.esm.js",
50
43
  "types": "./oidc/index.d.ts",
51
- "import": "./oidc/index.cjs.mjs",
52
- "default": "./oidc/index.cjs.js"
44
+ "import": "./oidc/index.esm.js",
45
+ "default": "./oidc/index.esm.js"
53
46
  },
54
47
  "./mcp": {
55
- "module": "./mcp/index.esm.js",
56
48
  "types": "./mcp/index.d.ts",
57
49
  "import": "./mcp/index.esm.js",
58
- "default": "./mcp/index.cjs.js"
50
+ "default": "./mcp/index.esm.js"
59
51
  },
60
52
  "./package.json": "./package.json",
61
53
  ".": {
62
- "module": "./index.esm.js",
63
54
  "types": "./index.d.ts",
64
- "import": "./index.cjs.mjs",
65
- "default": "./index.cjs.js"
55
+ "import": "./index.esm.js",
56
+ "default": "./index.esm.js"
66
57
  }
67
58
  },
68
- "main": "./index.cjs.js",
69
- "types": "./src/index.d.ts",
70
59
  "peerDependencies": {
71
60
  "@cantoo/pdf-lib": "^2.6.5",
72
- "@dereekb/analytics": "13.37.0",
73
- "@dereekb/calcom": "13.37.0",
74
- "@dereekb/date": "13.37.0",
75
- "@dereekb/dbx-core": "13.37.0",
76
- "@dereekb/discord": "13.37.0",
77
- "@dereekb/firebase": "13.37.0",
78
- "@dereekb/model": "13.37.0",
79
- "@dereekb/nestjs": "13.37.0",
80
- "@dereekb/rxjs": "13.37.0",
81
- "@dereekb/util": "13.37.0",
82
- "@dereekb/zoho": "13.37.0",
61
+ "@dereekb/analytics": "13.39.0",
62
+ "@dereekb/calcom": "13.39.0",
63
+ "@dereekb/date": "13.39.0",
64
+ "@dereekb/dbx-core": "13.39.0",
65
+ "@dereekb/discord": "13.39.0",
66
+ "@dereekb/firebase": "13.39.0",
67
+ "@dereekb/model": "13.39.0",
68
+ "@dereekb/nestjs": "13.39.0",
69
+ "@dereekb/rxjs": "13.39.0",
70
+ "@dereekb/util": "13.39.0",
71
+ "@dereekb/zoho": "13.39.0",
83
72
  "@google-cloud/firestore": "^7.11.6",
84
73
  "@google-cloud/storage": "^7.19.0",
85
74
  "@modelcontextprotocol/node": "2.0.0",
@@ -105,5 +94,7 @@
105
94
  "supertest": "^7.2.2",
106
95
  "ts-essentials": "^10.2.0"
107
96
  },
108
- "module": "./index.esm.js"
109
- }
97
+ "module": "./index.esm.js",
98
+ "main": "./index.esm.js",
99
+ "types": "./index.d.ts"
100
+ }
@@ -14,15 +14,39 @@ export interface FirebaseServerEnvServiceRef<S extends FirebaseServerEnvService
14
14
  */
15
15
  export declare abstract class FirebaseServerEnvService {
16
16
  /**
17
- * Whether the server is running in a test/CI environment.
17
+ * Whether the server is running under a test runner / in CI.
18
+ *
19
+ * NOT the complement of {@link isProduction}. There are three distinct situations, and a local
20
+ * emulator run (`nx run <app>:serve`) is the third one — neither testing nor deployed:
21
+ *
22
+ * | situation | isTestingEnv | isProduction |
23
+ * | -------------------------- | ------------ | ------------ |
24
+ * | test / CI run | true | false |
25
+ * | local emulator `serve` | false | false |
26
+ * | deployed (staging or prod) | false | true |
27
+ *
28
+ * Use this only to branch on "a test runner is driving this" — seeded fixtures, relaxed timeouts,
29
+ * assertions. To decide whether real Firebase infrastructure is reachable, use {@link isProduction}:
30
+ * `!isTestingEnv` wrongly includes local emulator runs, which is a common source of calls to live
31
+ * backends (App Check, IAM signing, service-account discovery) that fail with no emulator behind them.
18
32
  */
19
33
  abstract readonly isTestingEnv: boolean;
20
34
  /**
21
- * Whether the server is running in production mode. (This may be true in both prod or a staging running as production).
35
+ * Whether the server is DEPLOYED — true for every deployed environment, including staging.
36
+ *
37
+ * This is the gate for "real Firebase infrastructure is reachable": services with no emulator
38
+ * (App Check token minting, IAM signing, GCE metadata service-account discovery) may only be
39
+ * called when this is true. It is false for both a test run and a local emulator `serve`, which is
40
+ * what makes it — and not `!isTestingEnv` — the correct check for those calls.
41
+ *
42
+ * Use {@link isStaging} to distinguish staging from true production.
22
43
  */
23
44
  abstract readonly isProduction: boolean;
24
45
  /**
25
- * Whether the server is running in a staging environment. isProduction is also typically true when this is true.
46
+ * Whether the server is running in a staging environment.
47
+ *
48
+ * Narrows within a deployed environment: {@link isProduction} is also true when this is true, since
49
+ * staging runs as production against real infrastructure.
26
50
  */
27
51
  abstract readonly isStaging: boolean;
28
52
  /**
@@ -0,0 +1,63 @@
1
+ import { type Maybe } from '@dereekb/util';
2
+ import { type OidcScope, type OidcScopeTerm } from '@dereekb/firebase';
3
+ import { type FirebaseServerAuthData } from './auth.context.server';
4
+ /**
5
+ * Error code thrown by {@link assertEndpointOidcScope} when an OIDC caller is missing the scope a
6
+ * non-model endpoint requires.
7
+ *
8
+ * Distinct from the callModel-layer code so a client can tell "you cannot call this endpoint" apart
9
+ * from "you cannot make this model call".
10
+ */
11
+ export declare const MISSING_ENDPOINT_OIDC_SCOPE_ERROR_CODE = "MISSING_ENDPOINT_OIDC_SCOPE_ERROR";
12
+ /**
13
+ * Reads the set of OIDC scopes carried by an authenticated request, or `undefined` for a non-OIDC
14
+ * (regular Firebase ID-token) caller.
15
+ *
16
+ * The OIDC bearer-token middleware attaches the validated access-token claims at
17
+ * `auth.oidcValidatedToken` (with the space-delimited `scope` string); a non-OIDC caller has neither
18
+ * that field nor a `scope` on `auth.token`. Reading is defensive — the auth shape is only typed as
19
+ * {@link FirebaseServerAuthData} here, since the OIDC-specific `oidcValidatedToken` lives in the
20
+ * `@dereekb/firebase-server/oidc` sub-package this core layer cannot import — and the actual parse is
21
+ * delegated to the shared {@link oidcScopesFromScopeClaim} so there is no drift with
22
+ * `getOidcScopesFromRequest`.
23
+ *
24
+ * @param auth - The request auth data, or undefined for unauthenticated requests.
25
+ * @returns The granted scope set, or `undefined` when the request carries no OIDC `scope` claim.
26
+ */
27
+ export declare function oidcScopesFromRequestAuth(auth: Maybe<FirebaseServerAuthData>): Maybe<Set<OidcScope>>;
28
+ /**
29
+ * Inputs to {@link assertEndpointOidcScope}.
30
+ */
31
+ export interface AssertEndpointOidcScopeInput {
32
+ /**
33
+ * The scope term the endpoint requires — a single scope, or an OR-group satisfied by holding any
34
+ * one of its scopes. `undefined`/`null` imposes no requirement.
35
+ */
36
+ readonly requiredScope: Maybe<OidcScopeTerm>;
37
+ /**
38
+ * The scopes the caller was granted, or `undefined` for a non-OIDC caller (bypasses enforcement).
39
+ */
40
+ readonly grantedScopes: Maybe<ReadonlySet<OidcScope>>;
41
+ /**
42
+ * Human-readable endpoint identifier used in the thrown error message (e.g. `/api/session/firestore`).
43
+ */
44
+ readonly endpoint: string;
45
+ }
46
+ /**
47
+ * Enforces a single OIDC scope requirement for a plain (non-callModel) endpoint, throwing a `403`
48
+ * when an OIDC caller does not hold it.
49
+ *
50
+ * The non-model counterpart to `assertModelApiOidcScope`, which is unusable outside the model API
51
+ * because it requires a `{ call, modelType }` pair. Evaluation reuses the same shipped
52
+ * {@link oidcScopeTermSatisfied} primitive so a term means the same thing on both surfaces.
53
+ *
54
+ * Bypasses (no-op) when `grantedScopes` is `undefined` — i.e. a non-OIDC caller carrying a plain
55
+ * Firebase ID token, which has no `scope` claim to enforce against. **This is why scope-gating alone
56
+ * is never a sufficient gate**: an endpoint that must reject non-admins needs its own admin check,
57
+ * with the scope acting as defence in depth.
58
+ *
59
+ * @param input - The required term, the caller's granted scopes, and the endpoint name for the message.
60
+ * @throws A `403` forbidden error (code {@link MISSING_ENDPOINT_OIDC_SCOPE_ERROR_CODE}) when an OIDC
61
+ * caller does not satisfy the requirement.
62
+ */
63
+ export declare function assertEndpointOidcScope(input: AssertEndpointOidcScopeInput): void;
@@ -1,2 +1,4 @@
1
1
  export * from './auth.context.server';
2
+ export * from './api.scope';
2
3
  export * from './model';
4
+ export * from './session';