@dereekb/firebase-server 13.3.0 → 13.4.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.
@@ -0,0 +1,56 @@
1
+ import { type InjectionToken } from '@nestjs/common';
2
+ import { type AuthData } from '../../type';
3
+ import { type FirebaseAuthUserId, type OnCallFunctionType, type FirestoreModelType, type ModelFirebaseCrudFunctionSpecifier } from '@dereekb/firebase';
4
+ import { type Maybe } from '@dereekb/util';
5
+ /**
6
+ * Structured analytics event emitted by the onCall CRUD dispatch chain.
7
+ *
8
+ * Each event captures the full context of a handler invocation — the operation being performed,
9
+ * the model type, the lifecycle stage, the authenticated user, and any custom properties.
10
+ *
11
+ * Consumed by {@link OnCallModelAnalyticsService} and forwarded to provider-specific listeners.
12
+ */
13
+ export interface OnCallModelAnalyticsEvent {
14
+ /** The event name describing what happened (e.g., `'Guestbook Created'`). */
15
+ readonly event: string;
16
+ /** The lifecycle stage at which this event was emitted. */
17
+ readonly lifecycle: 'triggered' | 'success' | 'error' | 'complete';
18
+ /** The CRUD operation type (e.g., `'create'`, `'update'`, `'delete'`). */
19
+ readonly call: OnCallFunctionType;
20
+ /** The model type being operated on (e.g., `'guestbook'`, `'profile'`). */
21
+ readonly modelType: FirestoreModelType;
22
+ /** Optional operation specifier for variant handlers (e.g., `'subscribeToNotifications'`). */
23
+ readonly specifier?: Maybe<ModelFirebaseCrudFunctionSpecifier>;
24
+ /** The Firebase Auth UID of the calling user, if authenticated. */
25
+ readonly uid?: Maybe<FirebaseAuthUserId>;
26
+ /** The full Firebase Auth context, if available. */
27
+ readonly auth?: Maybe<AuthData>;
28
+ /** The raw request object passed to the handler. */
29
+ readonly request?: Maybe<unknown>;
30
+ /** Custom key-value properties attached by lifecycle callbacks. */
31
+ readonly properties?: Maybe<Record<string, any>>;
32
+ /** The error object, if this event was emitted during the `'error'` lifecycle stage. */
33
+ readonly error?: Maybe<unknown>;
34
+ }
35
+ /**
36
+ * Abstract analytics service that apps implement to process analytics events.
37
+ *
38
+ * Analogous to {@link DbxAnalyticsService} on the frontend.
39
+ * Apps extend this class and provide it via {@link ON_CALL_MODEL_ANALYTICS_SERVICE}.
40
+ */
41
+ export declare abstract class OnCallModelAnalyticsService {
42
+ abstract handleOnCallAnalyticsEvent(event: OnCallModelAnalyticsEvent): void;
43
+ }
44
+ /**
45
+ * Default injection token for the analytics service.
46
+ * Apps provide this in their NestJS module to enable analytics in the onCall dispatch chain.
47
+ */
48
+ export declare const ON_CALL_MODEL_ANALYTICS_SERVICE: InjectionToken<OnCallModelAnalyticsService>;
49
+ /**
50
+ * @deprecated Use {@link OnCallModelAnalyticsService} instead.
51
+ */
52
+ export type OnCallModelAnalyticsHandler = OnCallModelAnalyticsService;
53
+ /**
54
+ * @deprecated Use {@link ON_CALL_MODEL_ANALYTICS_SERVICE} instead.
55
+ */
56
+ export declare const ON_CALL_MODEL_ANALYTICS_HANDLER: InjectionToken<OnCallModelAnalyticsService>;
@@ -1,4 +1,6 @@
1
1
  import { type Maybe } from '@dereekb/util';
2
+ import { type OnCallFunctionType, type FirestoreModelType, type ModelFirebaseCrudFunctionSpecifier } from '@dereekb/firebase';
3
+ import { type OnCallModelFunctionAnalyticsDetails } from './analytics.details';
2
4
  /**
3
5
  * Reference to a type that can produce a JSON Schema representation.
4
6
  *
@@ -43,6 +45,11 @@ export interface OnCallModelFunctionApiDetails {
43
45
  * MCP-specific customization. Auto-generated if omitted.
44
46
  */
45
47
  readonly mcp?: OnCallModelFunctionMcpDetails;
48
+ /**
49
+ * Analytics lifecycle configuration for this handler.
50
+ * When provided, the dispatch chain will call lifecycle hooks around handler execution.
51
+ */
52
+ readonly analytics?: OnCallModelFunctionAnalyticsDetails;
46
53
  }
47
54
  /**
48
55
  * Ref interface for objects (functions) that carry handler-level _apiDetails.
@@ -56,7 +63,8 @@ export interface OnCallModelFunctionApiDetailsRef {
56
63
  * Produced by onCallSpecifierHandler when its handlers carry _apiDetails.
57
64
  * Maps specifier keys (e.g., '_', 'username', 'fromUpload') to handler-level details.
58
65
  */
59
- export interface OnCallSpecifierApiDetails {
66
+ export interface OnCallModelTypeApiDetails {
67
+ readonly isSpecifier: boolean;
60
68
  readonly specifiers: {
61
69
  readonly [key: string]: OnCallModelFunctionApiDetails | undefined;
62
70
  };
@@ -65,12 +73,13 @@ export interface OnCallSpecifierApiDetails {
65
73
  * API details aggregated at the CRUD model level.
66
74
  *
67
75
  * Produced by onCallCreateModel/onCallReadModel/etc.
68
- * Maps model type strings (e.g., 'profile', 'guestbook') to either handler-level
69
- * details (for direct handlers) or specifier-level details (for specifier handlers).
76
+ * Maps model type strings (e.g., 'profile', 'guestbook') to specifier-level details.
77
+ * Direct (non-specifier) handlers are wrapped with `isSpecifier: false` and their
78
+ * details placed under the `_` key.
70
79
  */
71
80
  export interface OnCallCrudModelApiDetails {
72
81
  readonly modelTypes: {
73
- readonly [key: string]: OnCallModelFunctionApiDetails | OnCallSpecifierApiDetails | undefined;
82
+ readonly [key: string]: OnCallModelTypeApiDetails | undefined;
74
83
  };
75
84
  }
76
85
  /**
@@ -88,7 +97,7 @@ export interface OnCallModelApiDetails {
88
97
  /**
89
98
  * Union of all API details types at any aggregation level.
90
99
  */
91
- export type OnCallApiDetails = OnCallModelFunctionApiDetails | OnCallSpecifierApiDetails | OnCallCrudModelApiDetails | OnCallModelApiDetails;
100
+ export type OnCallApiDetails = OnCallModelFunctionApiDetails | OnCallModelTypeApiDetails | OnCallCrudModelApiDetails | OnCallModelApiDetails;
92
101
  /**
93
102
  * Ref interface for functions at any level of the call model tree.
94
103
  */
@@ -98,7 +107,7 @@ export interface OnCallApiDetailsRef {
98
107
  /**
99
108
  * Whether the details are specifier-level (has specifiers map).
100
109
  */
101
- export declare function isOnCallSpecifierApiDetails(details: OnCallApiDetails): details is OnCallSpecifierApiDetails;
110
+ export declare function isOnCallModelTypeApiDetails(details: OnCallApiDetails): details is OnCallModelTypeApiDetails;
102
111
  /**
103
112
  * Whether the details are CRUD-model-level (has modelTypes map).
104
113
  */
@@ -107,6 +116,11 @@ export declare function isOnCallCrudModelApiDetails(details: OnCallApiDetails):
107
116
  * Whether the details are handler-level (leaf node — no specifiers or modelTypes).
108
117
  */
109
118
  export declare function isOnCallHandlerApiDetails(details: OnCallApiDetails): details is OnCallModelFunctionApiDetails;
119
+ /**
120
+ * Whether the specifier-level details represent a true specifier (multiple sub-operations)
121
+ * vs a wrapped direct handler (`isSpecifier: false`, details under `_`).
122
+ */
123
+ export declare function isActualSpecifier(details: OnCallModelTypeApiDetails): boolean;
110
124
  /**
111
125
  * Configuration for withApiDetails().
112
126
  *
@@ -121,6 +135,14 @@ export interface WithApiDetailsConfig<F extends (...args: any[]) => any> extends
121
135
  * which would lose _apiDetails since optionalAuthContext creates a new wrapper function.
122
136
  */
123
137
  readonly optionalAuth?: boolean;
138
+ /**
139
+ * Optional analytics lifecycle configuration for this handler.
140
+ * When provided, the dispatch chain will call lifecycle hooks around handler execution.
141
+ *
142
+ * The request type and return type are inferred from the `fn` parameter, providing
143
+ * full type safety in lifecycle callbacks.
144
+ */
145
+ readonly analytics?: OnCallModelFunctionAnalyticsDetails<Parameters<F>[0], Awaited<ReturnType<F>>>;
124
146
  /**
125
147
  * The handler function.
126
148
  */
@@ -160,15 +182,15 @@ export declare function withApiDetails<F extends (...args: any[]) => any>(config
160
182
  /**
161
183
  * Reads _apiDetails from a function if present.
162
184
  */
163
- export declare function readApiDetails(fn: Maybe<OnCallApiDetailsRef>): OnCallApiDetails | undefined;
185
+ export declare function readApiDetails(fn: Maybe<OnCallApiDetailsRef>): Maybe<OnCallApiDetails>;
164
186
  /**
165
187
  * Aggregates _apiDetails from a specifier handler config object.
166
188
  *
167
- * Returns OnCallSpecifierApiDetails if any handlers have _apiDetails, otherwise undefined.
189
+ * Returns OnCallModelTypeApiDetails if any handlers have _apiDetails, otherwise undefined.
168
190
  */
169
191
  export declare function aggregateSpecifierApiDetails(config: {
170
192
  readonly [key: string]: Maybe<OnCallApiDetailsRef>;
171
- }): OnCallSpecifierApiDetails | undefined;
193
+ }): Maybe<OnCallModelTypeApiDetails>;
172
194
  /**
173
195
  * Aggregates _apiDetails from a model type map (used by onCallCreateModel, etc.).
174
196
  *
@@ -176,7 +198,7 @@ export declare function aggregateSpecifierApiDetails(config: {
176
198
  */
177
199
  export declare function aggregateCrudModelApiDetails(map: {
178
200
  readonly [key: string]: Maybe<OnCallApiDetailsRef>;
179
- }): OnCallCrudModelApiDetails | undefined;
201
+ }): Maybe<OnCallCrudModelApiDetails>;
180
202
  /**
181
203
  * Aggregates _apiDetails from the top-level call model map.
182
204
  *
@@ -184,16 +206,19 @@ export declare function aggregateCrudModelApiDetails(map: {
184
206
  */
185
207
  export declare function aggregateModelApiDetails(map: {
186
208
  readonly [key: string]: Maybe<OnCallApiDetailsRef>;
187
- }): OnCallModelApiDetails | undefined;
209
+ }): Maybe<OnCallModelApiDetails>;
188
210
  /**
189
- * Handler or specifier-level details for a single CRUD call on a model type.
211
+ * API details for a single CRUD call on a model type.
212
+ *
213
+ * Always {@link OnCallModelTypeApiDetails} — direct handlers are wrapped with
214
+ * `isSpecifier: false` and their details placed under the `_` key.
190
215
  */
191
- export type ModelCallApiDetails = OnCallModelFunctionApiDetails | OnCallSpecifierApiDetails;
216
+ export type ModelCallApiDetails = OnCallModelTypeApiDetails;
192
217
  /**
193
218
  * CRUD calls available for a single model type.
194
219
  *
195
220
  * Keyed by call type ('create', 'read', 'update', 'delete').
196
- * Each value is either handler-level details (direct handler) or specifier-level details.
221
+ * Each value is specifier-level details (direct handlers wrapped with `isSpecifier: false`).
197
222
  */
198
223
  export interface ModelCallsApiDetails {
199
224
  readonly create?: ModelCallApiDetails;
@@ -246,4 +271,19 @@ export interface ModelApiDetailsModelEntry {
246
271
  * // details.models['profile'].calls.update => { specifiers: { _: {...}, username: {...} } }
247
272
  * ```
248
273
  */
249
- export declare function getModelApiDetails(callModelFn: Maybe<OnCallApiDetailsRef>): ModelApiDetailsResult | undefined;
274
+ export declare function getModelApiDetails(callModelFn: Maybe<OnCallApiDetailsRef>): Maybe<ModelApiDetailsResult>;
275
+ /**
276
+ * Resolves leaf-level analytics details from the aggregated _apiDetails tree.
277
+ *
278
+ * Walks: call → modelType → specifier (if specifier-level), then reads the `analytics`
279
+ * field from the handler-level {@link OnCallModelFunctionApiDetails}.
280
+ */
281
+ export declare function resolveAnalyticsFromApiDetails(apiDetails: OnCallModelApiDetails, call: OnCallFunctionType, modelType: FirestoreModelType, specifier?: ModelFirebaseCrudFunctionSpecifier): Maybe<OnCallModelFunctionAnalyticsDetails>;
282
+ /**
283
+ * @deprecated Use {@link OnCallModelTypeApiDetails} instead.
284
+ */
285
+ export type OnCallSpecifierApiDetails = OnCallModelTypeApiDetails;
286
+ /**
287
+ * @deprecated Use {@link isOnCallModelTypeApiDetails} instead.
288
+ */
289
+ export declare const isOnCallSpecifierApiDetails: typeof isOnCallModelTypeApiDetails;
@@ -25,6 +25,11 @@ export interface OnCallModelConfig {
25
25
  * Throw to reject the request.
26
26
  */
27
27
  readonly preAssert?: AssertModelCrudRequestFunction<unknown, OnCallTypedModelParams>;
28
+ /**
29
+ * Override the analytics handler injection token.
30
+ * Default: {@link ON_CALL_MODEL_ANALYTICS_SERVICE}
31
+ */
32
+ readonly analyticsToken?: string;
28
33
  }
29
34
  /**
30
35
  * Top-level factory that creates a single callable function dispatching to CRUD operations.
@@ -1,6 +1,6 @@
1
1
  import { type Maybe } from '@dereekb/util';
2
2
  import { type NestContextCallableRequest } from '../function/nest';
3
- import { type OnCallFunctionType } from '@dereekb/firebase';
3
+ import { type OnCallFunctionType, type FirestoreModelType } from '@dereekb/firebase';
4
4
  /**
5
5
  * Discriminator for the category of CRUD operation being asserted.
6
6
  *
@@ -23,7 +23,7 @@ export interface AssertModelCrudRequestFunctionContext<N, I = unknown> {
23
23
  /** The CRUD call type string (e.g., 'create', 'read'). */
24
24
  readonly call: OnCallFunctionType;
25
25
  /** The Firestore model type being targeted (e.g., 'profile', 'guestbook'). */
26
- readonly modelType: string;
26
+ readonly modelType: FirestoreModelType;
27
27
  /** The optional sub-operation specifier (e.g., 'username', 'fromUpload'). */
28
28
  readonly specifier: Maybe<string>;
29
29
  }
@@ -1,3 +1,6 @@
1
+ export * from './analytics.details';
2
+ export * from './analytics.emit';
3
+ export * from './analytics.handler';
1
4
  export * from './api.details';
2
5
  export * from './permission.error';
3
6
  export * from './specifier.function';
@@ -58,7 +58,7 @@ export type OnCallSpecifierHandlerConfig<N> = {
58
58
  * delegated to each individual specifier handler.
59
59
  *
60
60
  * API details from all specifier handlers are aggregated into
61
- * {@link OnCallSpecifierApiDetails} for MCP introspection.
61
+ * {@link OnCallModelTypeApiDetails} for MCP introspection.
62
62
  *
63
63
  * @param config - Maps specifier keys to their handler functions.
64
64
  * @returns A callable function that dispatches by specifier, with aggregated API details.
package/test/package.json CHANGED
@@ -1,24 +1,28 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/test",
3
- "version": "13.3.0",
3
+ "version": "13.4.0",
4
4
  "peerDependencies": {
5
- "@dereekb/date": "13.3.0",
6
- "@dereekb/firebase": "13.3.0",
7
- "@dereekb/firebase-server": "13.3.0",
8
- "@dereekb/model": "13.3.0",
9
- "@dereekb/nestjs": "13.3.0",
10
- "@dereekb/rxjs": "13.3.0",
11
- "@dereekb/util": "13.3.0",
5
+ "@dereekb/analytics": "13.4.0",
6
+ "@dereekb/date": "13.4.0",
7
+ "@dereekb/firebase": "13.4.0",
8
+ "@dereekb/firebase-server": "13.4.0",
9
+ "@dereekb/model": "13.4.0",
10
+ "@dereekb/nestjs": "13.4.0",
11
+ "@dereekb/rxjs": "13.4.0",
12
+ "@dereekb/util": "13.4.0",
12
13
  "@google-cloud/firestore": "^7.11.6",
13
14
  "@google-cloud/storage": "^7.19.0",
14
- "@nestjs/common": "^11.0.0",
15
- "@nestjs/testing": "^11.0.0",
15
+ "@nestjs/common": "^11.1.16",
16
+ "@nestjs/testing": "^11.1.14",
16
17
  "firebase-admin": "^13.0.0",
17
18
  "firebase-functions": "^7.0.0",
18
19
  "firebase-functions-test": "3.4.1",
19
20
  "jsonwebtoken": "^9.0.0",
20
21
  "make-error": "^1.3.0"
21
22
  },
23
+ "devDependencies": {
24
+ "@dereekb/nestjs": "13.4.0"
25
+ },
22
26
  "exports": {
23
27
  "./package.json": "./package.json",
24
28
  ".": {
package/zoho/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/zoho",
3
- "version": "13.3.0",
3
+ "version": "13.4.0",
4
4
  "peerDependencies": {
5
- "@dereekb/date": "13.3.0",
6
- "@dereekb/model": "13.3.0",
7
- "@dereekb/nestjs": "13.3.0",
8
- "@dereekb/rxjs": "13.3.0",
9
- "@dereekb/firebase": "13.3.0",
10
- "@dereekb/util": "13.3.0",
11
- "@dereekb/zoho": "13.3.0"
5
+ "@dereekb/analytics": "13.4.0",
6
+ "@dereekb/date": "13.4.0",
7
+ "@dereekb/model": "13.4.0",
8
+ "@dereekb/nestjs": "13.4.0",
9
+ "@dereekb/rxjs": "13.4.0",
10
+ "@dereekb/firebase": "13.4.0",
11
+ "@dereekb/util": "13.4.0",
12
+ "@dereekb/zoho": "13.4.0"
12
13
  },
13
14
  "exports": {
14
15
  "./package.json": "./package.json",