@dereekb/firebase-server 13.27.1 → 13.29.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.
package/index.cjs.js CHANGED
@@ -5486,6 +5486,31 @@ function resolveAnalyticsFromApiDetails(apiDetails, call, modelType, specifier)
5486
5486
  }
5487
5487
  return result;
5488
5488
  }
5489
+ // MARK: Required Scope Resolution
5490
+ /**
5491
+ * Resolves the leaf-level per-function required OIDC scope from the aggregated _apiDetails tree.
5492
+ *
5493
+ * Walks: call -> modelType -> specifier (if specifier-level), then reads the `requiredScope`
5494
+ * field from the handler-level {@link OnCallModelFunctionApiDetails}. Mirrors
5495
+ * {@link resolveAnalyticsFromApiDetails}.
5496
+ *
5497
+ * @param apiDetails - The top-level aggregated API details.
5498
+ * @param call - The CRUD operation type to look up.
5499
+ * @param modelType - The Firestore model type to look up.
5500
+ * @param specifier - Optional specifier key for variant handlers.
5501
+ * @returns The per-function required scope for the resolved handler, or undefined.
5502
+ */ // eslint-disable-next-line @typescript-eslint/max-params
5503
+ function resolveRequiredScopeFromApiDetails(apiDetails, call, modelType, specifier) {
5504
+ var _apiDetails_call;
5505
+ var modelDetails = (_apiDetails_call = apiDetails[call]) === null || _apiDetails_call === void 0 ? void 0 : _apiDetails_call.modelTypes[modelType];
5506
+ var result;
5507
+ if (modelDetails) {
5508
+ var _modelDetails_specifiers_key;
5509
+ var key = specifier !== null && specifier !== void 0 ? specifier : '_';
5510
+ result = (_modelDetails_specifiers_key = modelDetails.specifiers[key]) === null || _modelDetails_specifiers_key === void 0 ? void 0 : _modelDetails_specifiers_key.requiredScope;
5511
+ }
5512
+ return result;
5513
+ }
5489
5514
  /**
5490
5515
  * @deprecated Use {@link isOnCallModelTypeApiDetails} instead.
5491
5516
  */ var isOnCallSpecifierApiDetails = isOnCallModelTypeApiDetails;
@@ -5774,7 +5799,12 @@ function _object_spread_props$c(target, source) {
5774
5799
  data: request.data.data,
5775
5800
  request: request
5776
5801
  };
5777
- preAssert(context);
5802
+ // Resolve the leaf handler's per-function requiredScope (if any) so the preAssert can enforce
5803
+ // it additively with the per-verb OIDC scope. Same call -> modelType -> specifier walk as analytics.
5804
+ var requiredScope = resolveRequiredScopeFromApiDetails(modelApiDetails, call, modelType, specifier);
5805
+ preAssert(_object_spread_props$c(_object_spread$h({}, context), {
5806
+ requiredScope: requiredScope
5807
+ }));
5778
5808
  var result;
5779
5809
  // Resolve analytics from _apiDetails tree — callWithAnalytics handles undefined details
5780
5810
  var analyticsService = getAnalyticsService(request);
@@ -13282,6 +13312,7 @@ exports.readApiDetails = readApiDetails;
13282
13312
  exports.readModelUnknownModelTypeError = readModelUnknownModelTypeError;
13283
13313
  exports.resolveAdminOnlyValue = resolveAdminOnlyValue;
13284
13314
  exports.resolveAnalyticsFromApiDetails = resolveAnalyticsFromApiDetails;
13315
+ exports.resolveRequiredScopeFromApiDetails = resolveRequiredScopeFromApiDetails;
13285
13316
  exports.setNestContextOnRequest = setNestContextOnRequest;
13286
13317
  exports.setNestContextOnScheduleRequest = setNestContextOnScheduleRequest;
13287
13318
  exports.taskQueueFunctionHandlerWithNestContextFactory = taskQueueFunctionHandlerWithNestContextFactory;
package/index.esm.js CHANGED
@@ -5484,6 +5484,31 @@ function resolveAnalyticsFromApiDetails(apiDetails, call, modelType, specifier)
5484
5484
  }
5485
5485
  return result;
5486
5486
  }
5487
+ // MARK: Required Scope Resolution
5488
+ /**
5489
+ * Resolves the leaf-level per-function required OIDC scope from the aggregated _apiDetails tree.
5490
+ *
5491
+ * Walks: call -> modelType -> specifier (if specifier-level), then reads the `requiredScope`
5492
+ * field from the handler-level {@link OnCallModelFunctionApiDetails}. Mirrors
5493
+ * {@link resolveAnalyticsFromApiDetails}.
5494
+ *
5495
+ * @param apiDetails - The top-level aggregated API details.
5496
+ * @param call - The CRUD operation type to look up.
5497
+ * @param modelType - The Firestore model type to look up.
5498
+ * @param specifier - Optional specifier key for variant handlers.
5499
+ * @returns The per-function required scope for the resolved handler, or undefined.
5500
+ */ // eslint-disable-next-line @typescript-eslint/max-params
5501
+ function resolveRequiredScopeFromApiDetails(apiDetails, call, modelType, specifier) {
5502
+ var _apiDetails_call;
5503
+ var modelDetails = (_apiDetails_call = apiDetails[call]) === null || _apiDetails_call === void 0 ? void 0 : _apiDetails_call.modelTypes[modelType];
5504
+ var result;
5505
+ if (modelDetails) {
5506
+ var _modelDetails_specifiers_key;
5507
+ var key = specifier !== null && specifier !== void 0 ? specifier : '_';
5508
+ result = (_modelDetails_specifiers_key = modelDetails.specifiers[key]) === null || _modelDetails_specifiers_key === void 0 ? void 0 : _modelDetails_specifiers_key.requiredScope;
5509
+ }
5510
+ return result;
5511
+ }
5487
5512
  /**
5488
5513
  * @deprecated Use {@link isOnCallModelTypeApiDetails} instead.
5489
5514
  */ var isOnCallSpecifierApiDetails = isOnCallModelTypeApiDetails;
@@ -5772,7 +5797,12 @@ function _object_spread_props$c(target, source) {
5772
5797
  data: request.data.data,
5773
5798
  request: request
5774
5799
  };
5775
- preAssert(context);
5800
+ // Resolve the leaf handler's per-function requiredScope (if any) so the preAssert can enforce
5801
+ // it additively with the per-verb OIDC scope. Same call -> modelType -> specifier walk as analytics.
5802
+ var requiredScope = resolveRequiredScopeFromApiDetails(modelApiDetails, call, modelType, specifier);
5803
+ preAssert(_object_spread_props$c(_object_spread$h({}, context), {
5804
+ requiredScope: requiredScope
5805
+ }));
5776
5806
  var result;
5777
5807
  // Resolve analytics from _apiDetails tree — callWithAnalytics handles undefined details
5778
5808
  var analyticsService = getAnalyticsService(request);
@@ -13086,4 +13116,4 @@ function _define_property(obj, key, value) {
13086
13116
  }
13087
13117
  ();
13088
13118
 
13089
- export { AbstractFirebaseNestContext, AbstractFirebaseServerActionsContext, AbstractFirebaseServerAuthContext, AbstractFirebaseServerAuthService, AbstractFirebaseServerAuthUserContext, AbstractFirebaseServerNewUserService, AbstractFirebaseServerUserPasswordResetService, AbstractNestContext, AbstractServerFirebaseNestContext, ConfigureFirebaseAppCheckMiddlewareModule, ConfigureFirebaseWebhookMiddlewareModule, DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR, DEFAULT_RESET_CODE_EXPIRES_IN, DEFAULT_RESET_COM_THROTTLE_TIME, DEFAULT_SERVER_ASSETS_BASE_PATH, DEFAULT_SETUP_COM_THROTTLE_TIME, DefaultFirebaseServerEnvService, FIREBASE_APP_TOKEN, FIREBASE_AUTH_TOKEN, FIREBASE_FIRESTORE_CONTEXT_TOKEN, FIREBASE_FIRESTORE_TOKEN, FIREBASE_SERVER_ENV_TOKEN, FIREBASE_SERVER_VALIDATION_ERROR_CODE, FIREBASE_STORAGE_CONTEXT_FACTORY_CONFIG_TOKEN, FIREBASE_STORAGE_CONTEXT_TOKEN, FIREBASE_STORAGE_TOKEN, FIRESTORE_CLIENT_QUERY_CONSTRAINT_HANDLER_MAPPING, FirebaseAppCheckMiddleware, FirebaseAppCheckMiddlewareConfig, FirebaseNestServerRootModule, FirebaseRawBodyMiddleware, FirebaseServerAnalyticsSegmentListenerService, FirebaseServerAnalyticsSegmentModule, FirebaseServerAnalyticsService, FirebaseServerAnalyticsServiceListener, FirebaseServerAuthModule, FirebaseServerAuthNewUserSendSetupDetailsNoSetupConfigError, FirebaseServerAuthNewUserSendSetupDetailsSendOnceError, FirebaseServerAuthNewUserSendSetupDetailsThrottleError, FirebaseServerAuthPasswordResetInvalidCodeError, FirebaseServerAuthPasswordResetNoResetConfigError, FirebaseServerAuthPasswordResetSendOnceError, FirebaseServerAuthPasswordResetThrottleError, FirebaseServerAuthService, FirebaseServerAuthUserBadInputError, FirebaseServerAuthUserExistsError, FirebaseServerEnvService, FirebaseServerFirestoreContextModule, FirebaseServerFirestoreModule, FirebaseServerStorageContextModule, FirebaseServerStorageModule, FirebaseServerStorageService, GlobalRoutePrefixConfig, MAX_MODEL_ACCESS_MULTI_READ_KEYS, MODEL_API_NEST_APPLICATION_CONTEXT, ModelApiCallModelDispatchService, ModelApiController, ModelApiDispatchConfig, ModelApiGetService, NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_CODE, NoContentFirebaseServerUserPasswordResetService, NoSetupContentFirebaseServerNewUserService, ON_CALL_MODEL_ANALYTICS_HANDLER, ON_CALL_MODEL_ANALYTICS_SERVICE, OnCallModelAnalyticsService, PHONE_NUMBER_ALREADY_EXISTS_ERROR_CODE, SkipAppCheck, UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_NAME_CODE, UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_TYPE_CODE, _onCallWithCallTypeFunction, aggregateCrudModelApiDetails, aggregateModelApiDetails, aggregateSpecifierApiDetails, alreadyExistsError, appAnalyticsModuleMetadata, appFirestoreModuleMetadata, assertContextHasAuth, assertDocumentExists, assertHasRolesInRequest, assertHasSignedTosInRequest, assertIsAdminInRequest, assertIsAdminOrTargetUserInRequestData, assertIsContextWithAuthData, assertRequestRequiresAuthForFunction, assertSnapshotData, assertSnapshotDataWithKey, authServicePasswordResetInvalidCodeError, authServicePasswordResetNoConfigError, authServicePasswordResetSendOnceError, authServicePasswordResetThrottleError, badRequestError, blockingFunctionHandlerWithNestContextFactory, buildNestServerRootModule, callWithAnalytics, catchAndThrowPasswordResetServerErrors, cloudEventHandlerWithNestContextFactory, collectionRefForPath, createModelUnknownModelTypeError, decodeFirebaseServerUserPasswordResetOobCode, defaultFirebaseServerActionsTransformFactoryLogErrorFunction, defaultProvideFirebaseServerStorageServiceSimple, deleteModelUnknownModelTypeError, developmentUnknownSpecifierError, docRefForPath, documentModelNotAvailableError, encodeFirebaseServerUserPasswordResetOobCode, executeOnCallQuery, firebaseAuthTokenFromDecodedIdToken, firebaseServerActionsContext, firebaseServerActionsTransformContext, firebaseServerActionsTransformFactory, firebaseServerAppTokenProvider, firebaseServerAuthModuleMetadata, firebaseServerDevFunctions, firebaseServerEnvTokenProvider, firebaseServerEnvTokenProviders, firebaseServerErrorInfo, firebaseServerErrorInfoCodePair, firebaseServerErrorInfoServerErrorCodePair, firebaseServerErrorInfoServerErrorPair, firebaseServerFirestoreContextModuleMetadata, firebaseServerStorageDefaultBucketIdTokenProvider, firebaseServerStorageModuleMetadata, firebaseServerValidationError, firebaseServerValidationServerError, firestoreClientQueryConstraintFunctionsDriver, firestoreEncryptedField, firestoreServerIncrementUpdateToUpdateData, forbiddenError, getAuthUserOrUndefined, getModelApiDetails, googleCloudFileMetadataToStorageMetadata, googleCloudFirebaseStorageContextFactory, googleCloudFirebaseStorageDrivers, googleCloudFirestoreAccessorDriver, googleCloudFirestoreContextFactory, googleCloudFirestoreDrivers, googleCloudFirestoreQueryDriver, googleCloudStorageAccessorFile, googleCloudStorageAccessorFolder, googleCloudStorageBucketForStorageFilePath, googleCloudStorageFileForStorageFilePath, googleCloudStorageFirebaseStorageAccessorDriver, googleCloudStorageFromFirebaseAdminStorage, googleCloudStorageListFilesResultFactory, handleFirebaseAuthError, handleFirebaseError, hasAuthRolesInRequest, hasNewUserSetupPasswordInRequest, hasSignedTosInRequest, inAuthContext, injectNestApplicationContextIntoRequest, injectNestIntoRequest, internalServerError, invokeModelUnknownModelTypeError, isActualSpecifier, isAdminInRequest, isAdminOrTargetUserInRequestData, isContextWithAuthData, isFirebaseError, isFirebaseHttpsError, isOnCallCrudModelApiDetails, isOnCallHandlerApiDetails, isOnCallModelTypeApiDetails, isOnCallSpecifierApiDetails, makeBlockingFunctionWithHandler, makeOnScheduleHandlerWithNestApplicationRequest, makeScheduledFunctionDevelopmentFunction, modelAccessReadErrorFromUseMultipleModelsFailure, modelApiModuleMetadata, modelNotAvailableError, nestAppHasDevelopmentSchedulerEnabled, nestAppIsProductionEnvironment, nestFirebaseDoesNotExistError, nestFirebaseForbiddenPermissionError, nestServerInstance, noRunNameSpecifiedForScheduledFunctionDevelopmentFunction, noopFirebaseServerAnalyticsServiceListener, noopOnCallModelAnalyticsService, notFoundError, onCallAnalyticsEmitterInstance, onCallCreateModel, onCallDeleteModel, onCallDevelopmentFunction, onCallHandlerWithNestApplicationFactory, onCallHandlerWithNestContextFactory, onCallInvokeModel, onCallModel, onCallModelMissingCallTypeError, onCallModelUnknownCallTypeError, onCallQueryModel, onCallReadModel, onCallSpecifierHandler, onCallUpdateModel, onScheduleHandlerWithNestApplicationFactory, onScheduleHandlerWithNestContextFactory, optionalAuthContext, optionalFirestoreEncryptedField, permissionDeniedError, phoneNumberAlreadyExistsError, preconditionConflictError, provideAppFirestoreCollections, provideFirebaseServerAuthService, provideFirebaseServerStorageService, queryModelBadCursorError, queryModelUnknownModelTypeError, readApiDetails, readModelUnknownModelTypeError, resolveAdminOnlyValue, resolveAnalyticsFromApiDetails, setNestContextOnRequest, setNestContextOnScheduleRequest, taskQueueFunctionHandlerWithNestContextFactory, unauthenticatedContextHasNoAuthData, unauthenticatedContextHasNoUidError, unauthenticatedError, unavailableError, unavailableOrDeactivatedFunctionError, unknownModelCrudFunctionSpecifierError, unknownScheduledFunctionDevelopmentFunctionName, unknownScheduledFunctionDevelopmentFunctionType, updateModelUnknownModelTypeError, userContextFromUid, verifyAppCheckInRequest, withApiDetails };
13119
+ export { AbstractFirebaseNestContext, AbstractFirebaseServerActionsContext, AbstractFirebaseServerAuthContext, AbstractFirebaseServerAuthService, AbstractFirebaseServerAuthUserContext, AbstractFirebaseServerNewUserService, AbstractFirebaseServerUserPasswordResetService, AbstractNestContext, AbstractServerFirebaseNestContext, ConfigureFirebaseAppCheckMiddlewareModule, ConfigureFirebaseWebhookMiddlewareModule, DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR, DEFAULT_RESET_CODE_EXPIRES_IN, DEFAULT_RESET_COM_THROTTLE_TIME, DEFAULT_SERVER_ASSETS_BASE_PATH, DEFAULT_SETUP_COM_THROTTLE_TIME, DefaultFirebaseServerEnvService, FIREBASE_APP_TOKEN, FIREBASE_AUTH_TOKEN, FIREBASE_FIRESTORE_CONTEXT_TOKEN, FIREBASE_FIRESTORE_TOKEN, FIREBASE_SERVER_ENV_TOKEN, FIREBASE_SERVER_VALIDATION_ERROR_CODE, FIREBASE_STORAGE_CONTEXT_FACTORY_CONFIG_TOKEN, FIREBASE_STORAGE_CONTEXT_TOKEN, FIREBASE_STORAGE_TOKEN, FIRESTORE_CLIENT_QUERY_CONSTRAINT_HANDLER_MAPPING, FirebaseAppCheckMiddleware, FirebaseAppCheckMiddlewareConfig, FirebaseNestServerRootModule, FirebaseRawBodyMiddleware, FirebaseServerAnalyticsSegmentListenerService, FirebaseServerAnalyticsSegmentModule, FirebaseServerAnalyticsService, FirebaseServerAnalyticsServiceListener, FirebaseServerAuthModule, FirebaseServerAuthNewUserSendSetupDetailsNoSetupConfigError, FirebaseServerAuthNewUserSendSetupDetailsSendOnceError, FirebaseServerAuthNewUserSendSetupDetailsThrottleError, FirebaseServerAuthPasswordResetInvalidCodeError, FirebaseServerAuthPasswordResetNoResetConfigError, FirebaseServerAuthPasswordResetSendOnceError, FirebaseServerAuthPasswordResetThrottleError, FirebaseServerAuthService, FirebaseServerAuthUserBadInputError, FirebaseServerAuthUserExistsError, FirebaseServerEnvService, FirebaseServerFirestoreContextModule, FirebaseServerFirestoreModule, FirebaseServerStorageContextModule, FirebaseServerStorageModule, FirebaseServerStorageService, GlobalRoutePrefixConfig, MAX_MODEL_ACCESS_MULTI_READ_KEYS, MODEL_API_NEST_APPLICATION_CONTEXT, ModelApiCallModelDispatchService, ModelApiController, ModelApiDispatchConfig, ModelApiGetService, NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_CODE, NoContentFirebaseServerUserPasswordResetService, NoSetupContentFirebaseServerNewUserService, ON_CALL_MODEL_ANALYTICS_HANDLER, ON_CALL_MODEL_ANALYTICS_SERVICE, OnCallModelAnalyticsService, PHONE_NUMBER_ALREADY_EXISTS_ERROR_CODE, SkipAppCheck, UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_NAME_CODE, UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_TYPE_CODE, _onCallWithCallTypeFunction, aggregateCrudModelApiDetails, aggregateModelApiDetails, aggregateSpecifierApiDetails, alreadyExistsError, appAnalyticsModuleMetadata, appFirestoreModuleMetadata, assertContextHasAuth, assertDocumentExists, assertHasRolesInRequest, assertHasSignedTosInRequest, assertIsAdminInRequest, assertIsAdminOrTargetUserInRequestData, assertIsContextWithAuthData, assertRequestRequiresAuthForFunction, assertSnapshotData, assertSnapshotDataWithKey, authServicePasswordResetInvalidCodeError, authServicePasswordResetNoConfigError, authServicePasswordResetSendOnceError, authServicePasswordResetThrottleError, badRequestError, blockingFunctionHandlerWithNestContextFactory, buildNestServerRootModule, callWithAnalytics, catchAndThrowPasswordResetServerErrors, cloudEventHandlerWithNestContextFactory, collectionRefForPath, createModelUnknownModelTypeError, decodeFirebaseServerUserPasswordResetOobCode, defaultFirebaseServerActionsTransformFactoryLogErrorFunction, defaultProvideFirebaseServerStorageServiceSimple, deleteModelUnknownModelTypeError, developmentUnknownSpecifierError, docRefForPath, documentModelNotAvailableError, encodeFirebaseServerUserPasswordResetOobCode, executeOnCallQuery, firebaseAuthTokenFromDecodedIdToken, firebaseServerActionsContext, firebaseServerActionsTransformContext, firebaseServerActionsTransformFactory, firebaseServerAppTokenProvider, firebaseServerAuthModuleMetadata, firebaseServerDevFunctions, firebaseServerEnvTokenProvider, firebaseServerEnvTokenProviders, firebaseServerErrorInfo, firebaseServerErrorInfoCodePair, firebaseServerErrorInfoServerErrorCodePair, firebaseServerErrorInfoServerErrorPair, firebaseServerFirestoreContextModuleMetadata, firebaseServerStorageDefaultBucketIdTokenProvider, firebaseServerStorageModuleMetadata, firebaseServerValidationError, firebaseServerValidationServerError, firestoreClientQueryConstraintFunctionsDriver, firestoreEncryptedField, firestoreServerIncrementUpdateToUpdateData, forbiddenError, getAuthUserOrUndefined, getModelApiDetails, googleCloudFileMetadataToStorageMetadata, googleCloudFirebaseStorageContextFactory, googleCloudFirebaseStorageDrivers, googleCloudFirestoreAccessorDriver, googleCloudFirestoreContextFactory, googleCloudFirestoreDrivers, googleCloudFirestoreQueryDriver, googleCloudStorageAccessorFile, googleCloudStorageAccessorFolder, googleCloudStorageBucketForStorageFilePath, googleCloudStorageFileForStorageFilePath, googleCloudStorageFirebaseStorageAccessorDriver, googleCloudStorageFromFirebaseAdminStorage, googleCloudStorageListFilesResultFactory, handleFirebaseAuthError, handleFirebaseError, hasAuthRolesInRequest, hasNewUserSetupPasswordInRequest, hasSignedTosInRequest, inAuthContext, injectNestApplicationContextIntoRequest, injectNestIntoRequest, internalServerError, invokeModelUnknownModelTypeError, isActualSpecifier, isAdminInRequest, isAdminOrTargetUserInRequestData, isContextWithAuthData, isFirebaseError, isFirebaseHttpsError, isOnCallCrudModelApiDetails, isOnCallHandlerApiDetails, isOnCallModelTypeApiDetails, isOnCallSpecifierApiDetails, makeBlockingFunctionWithHandler, makeOnScheduleHandlerWithNestApplicationRequest, makeScheduledFunctionDevelopmentFunction, modelAccessReadErrorFromUseMultipleModelsFailure, modelApiModuleMetadata, modelNotAvailableError, nestAppHasDevelopmentSchedulerEnabled, nestAppIsProductionEnvironment, nestFirebaseDoesNotExistError, nestFirebaseForbiddenPermissionError, nestServerInstance, noRunNameSpecifiedForScheduledFunctionDevelopmentFunction, noopFirebaseServerAnalyticsServiceListener, noopOnCallModelAnalyticsService, notFoundError, onCallAnalyticsEmitterInstance, onCallCreateModel, onCallDeleteModel, onCallDevelopmentFunction, onCallHandlerWithNestApplicationFactory, onCallHandlerWithNestContextFactory, onCallInvokeModel, onCallModel, onCallModelMissingCallTypeError, onCallModelUnknownCallTypeError, onCallQueryModel, onCallReadModel, onCallSpecifierHandler, onCallUpdateModel, onScheduleHandlerWithNestApplicationFactory, onScheduleHandlerWithNestContextFactory, optionalAuthContext, optionalFirestoreEncryptedField, permissionDeniedError, phoneNumberAlreadyExistsError, preconditionConflictError, provideAppFirestoreCollections, provideFirebaseServerAuthService, provideFirebaseServerStorageService, queryModelBadCursorError, queryModelUnknownModelTypeError, readApiDetails, readModelUnknownModelTypeError, resolveAdminOnlyValue, resolveAnalyticsFromApiDetails, resolveRequiredScopeFromApiDetails, setNestContextOnRequest, setNestContextOnScheduleRequest, taskQueueFunctionHandlerWithNestContextFactory, unauthenticatedContextHasNoAuthData, unauthenticatedContextHasNoUidError, unauthenticatedError, unavailableError, unavailableOrDeactivatedFunctionError, unknownModelCrudFunctionSpecifierError, unknownScheduledFunctionDevelopmentFunctionName, unknownScheduledFunctionDevelopmentFunctionType, updateModelUnknownModelTypeError, userContextFromUid, verifyAppCheckInRequest, withApiDetails };
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/mailgun",
3
- "version": "13.27.1",
3
+ "version": "13.29.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.27.1",
6
- "@dereekb/firebase": "13.27.1",
7
- "@dereekb/firebase-server": "13.27.1",
8
- "@dereekb/date": "13.27.1",
9
- "@dereekb/nestjs": "13.27.1",
10
- "@dereekb/model": "13.27.1",
11
- "@dereekb/rxjs": "13.27.1",
12
- "@dereekb/util": "13.27.1"
5
+ "@dereekb/analytics": "13.29.0",
6
+ "@dereekb/firebase": "13.29.0",
7
+ "@dereekb/firebase-server": "13.29.0",
8
+ "@dereekb/date": "13.29.0",
9
+ "@dereekb/nestjs": "13.29.0",
10
+ "@dereekb/model": "13.29.0",
11
+ "@dereekb/rxjs": "13.29.0",
12
+ "@dereekb/util": "13.29.0"
13
13
  },
14
14
  "exports": {
15
15
  "./package.json": "./package.json",
package/mcp/index.cjs.js CHANGED
@@ -1580,7 +1580,7 @@ function _unsupported_iterable_to_array$9(o, minLen) {
1580
1580
  *
1581
1581
  * @param context - The candidate plus the shared generation accumulators.
1582
1582
  */ function buildToolFromCandidate(context) {
1583
- var _clashCounts_get, _ref, _resolveRequiredScope;
1583
+ var _clashCounts_get, _ref;
1584
1584
  var _handlerDetails_mcp, _handlerDetails_mcp1;
1585
1585
  var candidate = context.candidate, clashCounts = context.clashCounts, options = context.options, manifest = context.manifest, seenNames = context.seenNames, outTools = context.outTools, outNeverVisibleTools = context.outNeverVisibleTools, outSkipped = context.outSkipped, outWarnings = context.outWarnings;
1586
1586
  var modelType = candidate.modelType, callType = candidate.callType, handlerDetails = candidate.handlerDetails, specifier = candidate.specifier, dispatch = candidate.dispatch, modelSegment = candidate.modelSegment, overrideName = candidate.overrideName, baseName = candidate.baseName, classified = candidate.classified, isVisible = candidate.isVisible;
@@ -1632,7 +1632,13 @@ function _unsupported_iterable_to_array$9(o, minLen) {
1632
1632
  });
1633
1633
  }
1634
1634
  }
1635
- var requiredScope = (_resolveRequiredScope = resolveRequiredScope(callType)) !== null && _resolveRequiredScope !== void 0 ? _resolveRequiredScope : undefined;
1635
+ // Additive scope set: the per-verb call-type scope (model.<call>) plus any per-function scope
1636
+ // declared via withApiDetails({ requiredScope }). Nullish entries drop out, so a non-CRUD call
1637
+ // type with no per-function scope yields an empty set (no scope gate).
1638
+ var requiredScopes = util.filterMaybeArrayValues([
1639
+ resolveRequiredScope(callType),
1640
+ handlerDetails.requiredScope
1641
+ ]);
1636
1642
  var effectiveReadOnly = resolveEffectiveReadOnly((_handlerDetails_mcp = handlerDetails.mcp) === null || _handlerDetails_mcp === void 0 ? void 0 : _handlerDetails_mcp.readOnly, callType);
1637
1643
  var annotations = resolveMcpToolAnnotations(effectiveReadOnly);
1638
1644
  var description = applyWriteMarker(baseDescription, annotations);
@@ -1641,20 +1647,20 @@ function _unsupported_iterable_to_array$9(o, minLen) {
1641
1647
  filterMetadata = {
1642
1648
  visibilityKind: 'declarative',
1643
1649
  rule: classified.rule,
1644
- requiredScope: requiredScope,
1650
+ requiredScopes: requiredScopes,
1645
1651
  effectiveReadOnly: effectiveReadOnly
1646
1652
  };
1647
1653
  } else if (classified.visibilityKind === 'dynamic') {
1648
1654
  filterMetadata = {
1649
1655
  visibilityKind: 'dynamic',
1650
1656
  visibilityFn: classified.visibilityFn,
1651
- requiredScope: requiredScope,
1657
+ requiredScopes: requiredScopes,
1652
1658
  effectiveReadOnly: effectiveReadOnly
1653
1659
  };
1654
1660
  } else {
1655
1661
  filterMetadata = {
1656
1662
  visibilityKind: classified.visibilityKind,
1657
- requiredScope: requiredScope,
1663
+ requiredScopes: requiredScopes,
1658
1664
  effectiveReadOnly: effectiveReadOnly
1659
1665
  };
1660
1666
  }
@@ -6670,7 +6676,9 @@ var McpServerFactoryService_1;
6670
6676
  for(var _iterator = tools[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
6671
6677
  var tool = _step.value;
6672
6678
  var filterMetadata = tool.filterMetadata;
6673
- if (scopes != null && filterMetadata.requiredScope != null && !scopes.has(filterMetadata.requiredScope)) {
6679
+ if (scopes != null && filterMetadata.requiredScopes != null && !filterMetadata.requiredScopes.every(function(scope) {
6680
+ return scopes.has(scope);
6681
+ })) {
6674
6682
  continue;
6675
6683
  }
6676
6684
  if (readOnlyMode && filterMetadata.effectiveReadOnly !== true) {
package/mcp/index.esm.js CHANGED
@@ -3,7 +3,7 @@ import { existsSync, readFileSync } from 'node:fs';
3
3
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
4
  import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
5
5
  import { getOidcScopesFromRequest, OidcProviderConfigService } from '@dereekb/firebase-server/oidc';
6
- import { makeValuesGroupMap, IMPERSONATION_URL_QUERY_PARAM, performAsyncTasks, authRolesSetHasRoles } from '@dereekb/util';
6
+ import { filterMaybeArrayValues, makeValuesGroupMap, IMPERSONATION_URL_QUERY_PARAM, performAsyncTasks, authRolesSetHasRoles } from '@dereekb/util';
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';
@@ -1578,7 +1578,7 @@ function _unsupported_iterable_to_array$9(o, minLen) {
1578
1578
  *
1579
1579
  * @param context - The candidate plus the shared generation accumulators.
1580
1580
  */ function buildToolFromCandidate(context) {
1581
- var _clashCounts_get, _ref, _resolveRequiredScope;
1581
+ var _clashCounts_get, _ref;
1582
1582
  var _handlerDetails_mcp, _handlerDetails_mcp1;
1583
1583
  var candidate = context.candidate, clashCounts = context.clashCounts, options = context.options, manifest = context.manifest, seenNames = context.seenNames, outTools = context.outTools, outNeverVisibleTools = context.outNeverVisibleTools, outSkipped = context.outSkipped, outWarnings = context.outWarnings;
1584
1584
  var modelType = candidate.modelType, callType = candidate.callType, handlerDetails = candidate.handlerDetails, specifier = candidate.specifier, dispatch = candidate.dispatch, modelSegment = candidate.modelSegment, overrideName = candidate.overrideName, baseName = candidate.baseName, classified = candidate.classified, isVisible = candidate.isVisible;
@@ -1630,7 +1630,13 @@ function _unsupported_iterable_to_array$9(o, minLen) {
1630
1630
  });
1631
1631
  }
1632
1632
  }
1633
- var requiredScope = (_resolveRequiredScope = resolveRequiredScope(callType)) !== null && _resolveRequiredScope !== void 0 ? _resolveRequiredScope : undefined;
1633
+ // Additive scope set: the per-verb call-type scope (model.<call>) plus any per-function scope
1634
+ // declared via withApiDetails({ requiredScope }). Nullish entries drop out, so a non-CRUD call
1635
+ // type with no per-function scope yields an empty set (no scope gate).
1636
+ var requiredScopes = filterMaybeArrayValues([
1637
+ resolveRequiredScope(callType),
1638
+ handlerDetails.requiredScope
1639
+ ]);
1634
1640
  var effectiveReadOnly = resolveEffectiveReadOnly((_handlerDetails_mcp = handlerDetails.mcp) === null || _handlerDetails_mcp === void 0 ? void 0 : _handlerDetails_mcp.readOnly, callType);
1635
1641
  var annotations = resolveMcpToolAnnotations(effectiveReadOnly);
1636
1642
  var description = applyWriteMarker(baseDescription, annotations);
@@ -1639,20 +1645,20 @@ function _unsupported_iterable_to_array$9(o, minLen) {
1639
1645
  filterMetadata = {
1640
1646
  visibilityKind: 'declarative',
1641
1647
  rule: classified.rule,
1642
- requiredScope: requiredScope,
1648
+ requiredScopes: requiredScopes,
1643
1649
  effectiveReadOnly: effectiveReadOnly
1644
1650
  };
1645
1651
  } else if (classified.visibilityKind === 'dynamic') {
1646
1652
  filterMetadata = {
1647
1653
  visibilityKind: 'dynamic',
1648
1654
  visibilityFn: classified.visibilityFn,
1649
- requiredScope: requiredScope,
1655
+ requiredScopes: requiredScopes,
1650
1656
  effectiveReadOnly: effectiveReadOnly
1651
1657
  };
1652
1658
  } else {
1653
1659
  filterMetadata = {
1654
1660
  visibilityKind: classified.visibilityKind,
1655
- requiredScope: requiredScope,
1661
+ requiredScopes: requiredScopes,
1656
1662
  effectiveReadOnly: effectiveReadOnly
1657
1663
  };
1658
1664
  }
@@ -6668,7 +6674,9 @@ var McpServerFactoryService_1;
6668
6674
  for(var _iterator = tools[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
6669
6675
  var tool = _step.value;
6670
6676
  var filterMetadata = tool.filterMetadata;
6671
- if (scopes != null && filterMetadata.requiredScope != null && !scopes.has(filterMetadata.requiredScope)) {
6677
+ if (scopes != null && filterMetadata.requiredScopes != null && !filterMetadata.requiredScopes.every(function(scope) {
6678
+ return scopes.has(scope);
6679
+ })) {
6672
6680
  continue;
6673
6681
  }
6674
6682
  if (readOnlyMode && filterMetadata.effectiveReadOnly !== true) {
package/mcp/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/mcp",
3
- "version": "13.27.1",
3
+ "version": "13.29.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.27.1",
6
- "@dereekb/date": "13.27.1",
7
- "@dereekb/firebase": "13.27.1",
8
- "@dereekb/firebase-server": "13.27.1",
9
- "@dereekb/firebase-server/oidc": "13.27.1",
10
- "@dereekb/model": "13.27.1",
11
- "@dereekb/nestjs": "13.27.1",
12
- "@dereekb/rxjs": "13.27.1",
13
- "@dereekb/util": "13.27.1",
14
- "@dereekb/zoho": "13.27.1",
5
+ "@dereekb/analytics": "13.29.0",
6
+ "@dereekb/date": "13.29.0",
7
+ "@dereekb/firebase": "13.29.0",
8
+ "@dereekb/firebase-server": "13.29.0",
9
+ "@dereekb/firebase-server/oidc": "13.29.0",
10
+ "@dereekb/model": "13.29.0",
11
+ "@dereekb/nestjs": "13.29.0",
12
+ "@dereekb/rxjs": "13.29.0",
13
+ "@dereekb/util": "13.29.0",
14
+ "@dereekb/zoho": "13.29.0",
15
15
  "@modelcontextprotocol/sdk": "1.29.0",
16
16
  "@nestjs/common": "^11.1.19",
17
17
  "@nestjs/core": "^11.1.19",
@@ -1,5 +1,5 @@
1
1
  import { type Maybe } from '@dereekb/util';
2
- import { type CallModelOidcScope } from '@dereekb/firebase';
2
+ import { type CallModelOidcScope, type OidcScope } from '@dereekb/firebase';
3
3
  import { type McpToolVisibility, type McpVisibilityContext, type McpVisibilityRule } from '@dereekb/firebase-server';
4
4
  import { type ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
5
5
  /**
@@ -41,10 +41,12 @@ export interface ClassifiedMcpToolVisibilityDynamic {
41
41
  export type McpToolFilterMetadata = McpToolFilterMetadataAlways | McpToolFilterMetadataNever | McpToolFilterMetadataDeclarative | McpToolFilterMetadataDynamic;
42
42
  interface McpToolFilterMetadataBase {
43
43
  /**
44
- * OIDC scope required to invoke this tool. Precomputed from the dispatch call type.
45
- * `undefined` for non-CRUD call types (apps gate those via their own preAssert if needed).
44
+ * OIDC scopes required to invoke this tool (AND-semantics — the caller must hold every entry).
45
+ * Precomputed from the dispatch call type's per-verb scope (`model.<call>`) plus any per-function
46
+ * scope declared via `withApiDetails({ requiredScope })`. Empty when no scope is enforced (a
47
+ * non-CRUD call type with no per-function scope); apps gate custom verbs via their own preAssert.
46
48
  */
47
- readonly requiredScope?: CallModelOidcScope;
49
+ readonly requiredScopes?: readonly OidcScope[];
48
50
  /**
49
51
  * Effective read-only classification used by the module-level `readOnly` filter.
50
52
  *
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/model",
3
- "version": "13.27.1",
3
+ "version": "13.29.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.27.1",
6
- "@dereekb/date": "13.27.1",
7
- "@dereekb/firebase": "13.27.1",
8
- "@dereekb/firebase-server": "13.27.1",
9
- "@dereekb/model": "13.27.1",
10
- "@dereekb/nestjs": "13.27.1",
11
- "@dereekb/rxjs": "13.27.1",
12
- "@dereekb/util": "13.27.1",
5
+ "@dereekb/analytics": "13.29.0",
6
+ "@dereekb/date": "13.29.0",
7
+ "@dereekb/firebase": "13.29.0",
8
+ "@dereekb/firebase-server": "13.29.0",
9
+ "@dereekb/model": "13.29.0",
10
+ "@dereekb/nestjs": "13.29.0",
11
+ "@dereekb/rxjs": "13.29.0",
12
+ "@dereekb/util": "13.29.0",
13
13
  "@nestjs/common": "^11.1.19",
14
14
  "@nestjs/config": "^4.0.4",
15
15
  "archiver": "^7.0.1",