@dereekb/firebase 13.32.0 → 13.34.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/eslint/package.json +3 -3
- package/index.cjs.js +1077 -551
- package/index.esm.js +1048 -552
- package/package.json +5 -5
- package/src/lib/common/firestore/snapshot/snapshot.field.d.ts +59 -4
- package/src/lib/model/index.d.ts +1 -0
- package/src/lib/model/system/system.d.ts +61 -3
- package/src/lib/model/userexternalconnection/index.d.ts +6 -0
- package/src/lib/model/userexternalconnection/userexternalconnection.action.d.ts +21 -0
- package/src/lib/model/userexternalconnection/userexternalconnection.api.d.ts +120 -0
- package/src/lib/model/userexternalconnection/userexternalconnection.d.ts +191 -0
- package/src/lib/model/userexternalconnection/userexternalconnection.id.d.ts +51 -0
- package/src/lib/model/userexternalconnection/userexternalconnection.query.d.ts +19 -0
- package/src/lib/model/userexternalconnection/userexternalconnection.util.d.ts +149 -0
- package/test/index.cjs.js +371 -50
- package/test/index.esm.js +362 -51
- package/test/package.json +6 -6
- package/test/src/lib/client/firebase.rules.d.ts +114 -0
- package/test/src/lib/client/index.d.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.34.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"exports": {
|
|
6
6
|
"./test": {
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"@dereekb/date": "13.
|
|
28
|
-
"@dereekb/model": "13.
|
|
29
|
-
"@dereekb/rxjs": "13.
|
|
30
|
-
"@dereekb/util": "13.
|
|
27
|
+
"@dereekb/date": "13.34.0",
|
|
28
|
+
"@dereekb/model": "13.34.0",
|
|
29
|
+
"@dereekb/rxjs": "13.34.0",
|
|
30
|
+
"@dereekb/util": "13.34.0",
|
|
31
31
|
"@firebase/rules-unit-testing": "5.0.0",
|
|
32
32
|
"@marcbachmann/cel-js": "^7.6.1",
|
|
33
33
|
"@typescript-eslint/parser": "8.59.3",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* - **Primitives**: `firestoreString`, `firestoreNumber`, `firestoreBoolean`, `firestoreEnum`
|
|
24
24
|
* - **Dates**: `firestoreDate` (ISO8601), `firestoreDateNumber` (unix seconds)
|
|
25
25
|
* - **Arrays**: `firestoreArray`, `firestoreUniqueArray`, `firestoreEnumArray`, `firestoreEncodedArray`
|
|
26
|
-
* - **Maps**: `firestoreMap`, `firestoreEncodedObjectMap`, `firestoreArrayMap`
|
|
26
|
+
* - **Maps**: `firestoreMap`, `firestoreEncodedObjectMap`, `firestoreObjectMap`, `firestoreArrayMap`
|
|
27
27
|
* - **Objects**: `firestoreSubObject`, `firestoreObjectArray`
|
|
28
28
|
* - **Specialized**: `firestoreUID`, `firestoreLatLngString`, `firestoreWebsiteLink`,
|
|
29
29
|
* `firestoreDateCellRange`, `firestoreBitwiseSet`, `firestoreUnitedStatesAddress`
|
|
@@ -1361,9 +1361,11 @@ export type FirestoreSubObjectFieldMapFunctionsConfig<T extends object, O extend
|
|
|
1361
1361
|
* // Nested address object with its own converters
|
|
1362
1362
|
* const addressField = firestoreSubObject<Address>({
|
|
1363
1363
|
* objectField: {
|
|
1364
|
-
*
|
|
1365
|
-
*
|
|
1366
|
-
*
|
|
1364
|
+
* fields: {
|
|
1365
|
+
* street: firestoreString(),
|
|
1366
|
+
* city: firestoreString(),
|
|
1367
|
+
* zip: firestoreString()
|
|
1368
|
+
* }
|
|
1367
1369
|
* }
|
|
1368
1370
|
* });
|
|
1369
1371
|
* ```
|
|
@@ -1371,6 +1373,59 @@ export type FirestoreSubObjectFieldMapFunctionsConfig<T extends object, O extend
|
|
|
1371
1373
|
* @__NO_SIDE_EFFECTS__
|
|
1372
1374
|
*/
|
|
1373
1375
|
export declare function firestoreSubObject<T extends object, O extends object = FirestoreModelData<T>>(config: FirestoreSubObjectFieldConfig<T, O>): FirestoreSubObjectFieldMapFunctionsConfig<T, O>;
|
|
1376
|
+
/**
|
|
1377
|
+
* A Firestore map type where each value is an object that is converted using its own set of field converters.
|
|
1378
|
+
*/
|
|
1379
|
+
export type FirestoreObjectMapFieldValueType<T extends object, S extends string = string> = Record<S, T>;
|
|
1380
|
+
/**
|
|
1381
|
+
* firestoreObjectMap configuration
|
|
1382
|
+
*/
|
|
1383
|
+
export type FirestoreObjectMapFieldConfig<T extends object, O extends object = FirestoreModelData<T>, S extends string = string> = DefaultMapConfiguredFirestoreFieldConfig<FirestoreObjectMapFieldValueType<T, S>, FirestoreMapFieldType<O, S>> & (FirestoreObjectArrayFieldConfigObjectFieldInput<T, O> | FirestoreObjectArrayFieldConfigFirestoreFieldInput<T, O>) & {
|
|
1384
|
+
/**
|
|
1385
|
+
* Optional filter to apply when saving to data.
|
|
1386
|
+
*
|
|
1387
|
+
* By default filters all empty values from the map. Objects with no keys are considered empty.
|
|
1388
|
+
*/
|
|
1389
|
+
readonly mapFilter?: FilterKeyValueTuplesInput<FirestoreMapFieldType<O, S>>;
|
|
1390
|
+
};
|
|
1391
|
+
/**
|
|
1392
|
+
* Creates a field mapping configuration for a Firestore map whose values are complex objects.
|
|
1393
|
+
*
|
|
1394
|
+
* This is the map-keyed counterpart to {@link firestoreObjectArray}: each value in the map is
|
|
1395
|
+
* converted using its own set of field converters (via `objectField` or `firestoreField`), so
|
|
1396
|
+
* nested `Date`/enum/array fields round-trip properly.
|
|
1397
|
+
*
|
|
1398
|
+
* On write, null/undefined values are filtered from each object to match Firestore semantics, and
|
|
1399
|
+
* empty values (including objects with no keys) are removed from the map entirely.
|
|
1400
|
+
*
|
|
1401
|
+
* NOTE: this exists instead of passing a sub-object's `mapFunctions.to`/`.from` directly to
|
|
1402
|
+
* {@link firestoreEncodedObjectMap}. That encoder is invoked as `mapFn(value, key)` by
|
|
1403
|
+
* `mapObjectMap()`, while a `ModelMapFunctions`' second parameter is the accumulator *target* — so
|
|
1404
|
+
* the map key string would be used as the write target and the conversion would throw at runtime.
|
|
1405
|
+
* The arity is wrapped here, once, rather than at each call site.
|
|
1406
|
+
*
|
|
1407
|
+
* @param config - Configuration including the per-value conversions and optional map filtering.
|
|
1408
|
+
* @returns A field mapping configuration for object map values.
|
|
1409
|
+
*
|
|
1410
|
+
* @dbxModelSnapshotField
|
|
1411
|
+
* @dbxModelSnapshotFieldCategory map
|
|
1412
|
+
* @dbxModelSnapshotFieldTags map, record, dictionary, object, nested, embedded, structured, factory
|
|
1413
|
+
* @dbxModelSnapshotFieldRelated firestore-sub-object, firestore-object-array
|
|
1414
|
+
* @template T - The value model type
|
|
1415
|
+
* @template O - The value Firestore data type (defaults to FirestoreModelData<T>)
|
|
1416
|
+
* @template S - Key type (string, defaults to string)
|
|
1417
|
+
*
|
|
1418
|
+
* @example
|
|
1419
|
+
* ```ts
|
|
1420
|
+
* // Map of provider id -> connection entry
|
|
1421
|
+
* const entriesField = firestoreObjectMap<UserExternalConnectionEntry, FirestoreModelData<UserExternalConnectionEntry>, UserExternalConnectionProviderType>({
|
|
1422
|
+
* objectField: { fields: userExternalConnectionEntryFields }
|
|
1423
|
+
* });
|
|
1424
|
+
* ```
|
|
1425
|
+
*
|
|
1426
|
+
* @__NO_SIDE_EFFECTS__
|
|
1427
|
+
*/
|
|
1428
|
+
export declare function firestoreObjectMap<T extends object, O extends object = FirestoreModelData<T>, S extends string = string>(config: FirestoreObjectMapFieldConfig<T, O, S>): FirestoreModelFieldMapFunctionsConfig<FirestoreEncodedObjectMapFieldValueType<T, S>, FirestoreMapFieldType<O, S>>;
|
|
1374
1429
|
export interface FirestoreLatLngStringConfig extends DefaultMapConfiguredFirestoreFieldConfig<LatLngString, LatLngString> {
|
|
1375
1430
|
readonly precision?: LatLngPrecision;
|
|
1376
1431
|
}
|
package/src/lib/model/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { type GrantedSysAdminRole } from '@dereekb/model';
|
|
2
|
-
import { AbstractFirestoreDocument } from '../../common/firestore/accessor/document';
|
|
2
|
+
import { AbstractFirestoreDocument, type FirestoreDocument } from '../../common/firestore/accessor/document';
|
|
3
|
+
import { type InterceptFirestoreDataConverterFactory } from '../../common/firestore/accessor/converter';
|
|
3
4
|
import { type FirestoreCollection } from '../../common/firestore/collection/collection';
|
|
4
5
|
import { type FirestoreContext } from '../../common/firestore/context';
|
|
5
6
|
import { type CollectionReference } from '../../common/firestore/types';
|
|
6
|
-
import { type ModelFieldMapFunctionsConfig } from '@dereekb/util';
|
|
7
|
+
import { type Maybe, type ModelFieldMapFunctionsConfig } from '@dereekb/util';
|
|
7
8
|
/**
|
|
8
9
|
* @module system
|
|
9
10
|
*
|
|
@@ -31,7 +32,12 @@ export declare abstract class SystemStateFirestoreCollections {
|
|
|
31
32
|
*/
|
|
32
33
|
export type SystemStateTypes = typeof systemStateIdentity;
|
|
33
34
|
/**
|
|
34
|
-
* Model identity for the SystemState collection
|
|
35
|
+
* Model identity for the SystemState collection.
|
|
36
|
+
*
|
|
37
|
+
* NOTE: the second argument of `firestoreModelIdentity()` is the COLLECTION NAME, so this is model
|
|
38
|
+
* type `systemState` stored at the Firestore path `sys/<docId>` — not the other way around.
|
|
39
|
+
* `systemStateCollectionReference()` resolves `identity.collectionName`, and `firestore.rules` match
|
|
40
|
+
* blocks are written against `/sys`.
|
|
35
41
|
*/
|
|
36
42
|
export declare const systemStateIdentity: import("../..").RootFirestoreModelIdentity<"systemState", "sys">;
|
|
37
43
|
/**
|
|
@@ -106,6 +112,14 @@ export declare const systemStateConverter: import("../..").SnapshotConverterFunc
|
|
|
106
112
|
*/
|
|
107
113
|
export declare function systemStateCollectionReference(context: FirestoreContext): CollectionReference<SystemState>;
|
|
108
114
|
export type SystemStateFirestoreCollection<T extends SystemStateStoredData = SystemStateStoredData> = FirestoreCollection<SystemState<T>, SystemStateDocument<T>>;
|
|
115
|
+
/**
|
|
116
|
+
* A {@link SystemState} collection with any document type.
|
|
117
|
+
*
|
|
118
|
+
* Use this where a consumer only needs to read/write SystemState documents and should accept either
|
|
119
|
+
* the client-shared {@link SystemStateFirestoreCollection} or a server-only variant that uses its own
|
|
120
|
+
* document class (e.g. `SystemStatePrivateFirestoreCollection` in `@dereekb/firebase-server/model`).
|
|
121
|
+
*/
|
|
122
|
+
export type SystemStateFirestoreCollectionLike<T extends SystemStateStoredData = SystemStateStoredData, D extends FirestoreDocument<SystemState<T>> = FirestoreDocument<SystemState<T>>> = FirestoreCollection<SystemState<T>, D>;
|
|
109
123
|
/**
|
|
110
124
|
* Field conversion config for a specific SystemState data type.
|
|
111
125
|
*
|
|
@@ -122,6 +136,50 @@ export type SystemStateStoredDataFieldConverterConfig<T extends SystemStateStore
|
|
|
122
136
|
export type SystemStateStoredDataConverterMap = {
|
|
123
137
|
[key: string]: SystemStateStoredDataFieldConverterConfig<any>;
|
|
124
138
|
};
|
|
139
|
+
/**
|
|
140
|
+
* What a SystemState collection does when a document's type has no registered converter.
|
|
141
|
+
*
|
|
142
|
+
* - `passthrough`: fall back to the default pass-through converter. Historical behavior, and what
|
|
143
|
+
* {@link systemStateFirestoreCollection} uses.
|
|
144
|
+
* - `error`: throw. Appropriate for a collection whose types are all known up front — notably a
|
|
145
|
+
* server-only collection, where silently reading a secret-bearing document through a pass-through
|
|
146
|
+
* converter would skip its field mapping (dates come back as raw `Timestamp`s, encrypted fields as
|
|
147
|
+
* raw ciphertext) with nothing to signal it.
|
|
148
|
+
*/
|
|
149
|
+
export type SystemStateUnknownTypeBehavior = 'passthrough' | 'error';
|
|
150
|
+
/**
|
|
151
|
+
* Configuration for {@link systemStateStoredDataConverterFactory}.
|
|
152
|
+
*/
|
|
153
|
+
export interface SystemStateStoredDataConverterFactoryConfig {
|
|
154
|
+
/**
|
|
155
|
+
* Map of type identifiers to their data field converters.
|
|
156
|
+
*/
|
|
157
|
+
readonly converters: SystemStateStoredDataConverterMap;
|
|
158
|
+
/**
|
|
159
|
+
* Behavior when a document's type has no registered converter. Defaults to `passthrough`.
|
|
160
|
+
*/
|
|
161
|
+
readonly unknownTypeBehavior?: Maybe<SystemStateUnknownTypeBehavior>;
|
|
162
|
+
/**
|
|
163
|
+
* Collection name used only to make the `error` message actionable.
|
|
164
|
+
*/
|
|
165
|
+
readonly collectionName?: Maybe<string>;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Creates the `converterFactory` for a SystemState collection, selecting a converter by document id
|
|
169
|
+
* (which is the {@link SystemStateTypeIdentifier}).
|
|
170
|
+
*
|
|
171
|
+
* Returning `undefined` is what produces the pass-through fallback — the accessor resolves
|
|
172
|
+
* `converterFactory(ref) ?? defaultConverter`.
|
|
173
|
+
*
|
|
174
|
+
* NOTE for `error`: the factory runs in `loadDocument` / `documentRefForKey`, so an unregistered type
|
|
175
|
+
* throws at document *load*, including while hydrating query results. That is intended for a
|
|
176
|
+
* server-only collection, but it does mean you cannot generically iterate such a collection unless
|
|
177
|
+
* every type it contains is registered.
|
|
178
|
+
*
|
|
179
|
+
* @param config - The converter map and unknown-type behavior.
|
|
180
|
+
* @returns A converter factory suitable for a Firestore collection's `converterFactory`.
|
|
181
|
+
*/
|
|
182
|
+
export declare function systemStateStoredDataConverterFactory(config: SystemStateStoredDataConverterFactoryConfig): InterceptFirestoreDataConverterFactory<SystemState>;
|
|
125
183
|
/**
|
|
126
184
|
* Creates a {@link SystemStateFirestoreCollection} with per-type data converters.
|
|
127
185
|
*
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './userexternalconnection';
|
|
2
|
+
export * from './userexternalconnection.id';
|
|
3
|
+
export * from './userexternalconnection.util';
|
|
4
|
+
export * from './userexternalconnection.query';
|
|
5
|
+
export * from './userexternalconnection.api';
|
|
6
|
+
export * from './userexternalconnection.action';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AsyncFirebaseFunctionUpdateAction, type FirebaseFunctionUpdateAction } from '../../common';
|
|
2
|
+
import { type UserExternalConnectionDocument } from './userexternalconnection';
|
|
3
|
+
/**
|
|
4
|
+
* @module userexternalconnection.action
|
|
5
|
+
*
|
|
6
|
+
* Type aliases for UserExternalConnection server action functions.
|
|
7
|
+
*
|
|
8
|
+
* NOTE: there are no create/delete action aliases here on purpose. The document pair is only ever
|
|
9
|
+
* mutated through the paired per-provider connect/update/disconnect operations in
|
|
10
|
+
* `@dereekb/firebase-server/model`, which create and remove the documents themselves.
|
|
11
|
+
*
|
|
12
|
+
* @template P - the API parameter type for the action
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Synchronous update action targeting a {@link UserExternalConnectionDocument}.
|
|
16
|
+
*/
|
|
17
|
+
export type UserExternalConnectionUpdateAction<P extends object> = FirebaseFunctionUpdateAction<P, UserExternalConnectionDocument>;
|
|
18
|
+
/**
|
|
19
|
+
* Async update action targeting a {@link UserExternalConnectionDocument}.
|
|
20
|
+
*/
|
|
21
|
+
export type AsyncUserExternalConnectionUpdateAction<P extends object> = AsyncFirebaseFunctionUpdateAction<P, UserExternalConnectionDocument>;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { type Type } from 'arktype';
|
|
2
|
+
import { type InferredTargetModelParams } from '../../common/model/model/model.param';
|
|
3
|
+
import { type FirebaseFunctionTypeConfigMap, type ModelFirebaseCreateFunction, type ModelFirebaseCrudFunction, type ModelFirebaseCrudFunctionConfigMap, type ModelFirebaseFunctionMap } from '../../client';
|
|
4
|
+
import { type UserExternalConnectionTypes } from './userexternalconnection';
|
|
5
|
+
import { type UserExternalConnectionProviderType } from './userexternalconnection.id';
|
|
6
|
+
/**
|
|
7
|
+
* Parameters for creating the calling user's connection document.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately empty: the document is keyed by uid, so there is nothing to target and nothing to
|
|
10
|
+
* seed it with — providers arrive one at a time through the OAuth flow, never at creation.
|
|
11
|
+
*
|
|
12
|
+
* @dbxModelApiParams
|
|
13
|
+
*/
|
|
14
|
+
export interface CreateUserExternalConnectionParams {
|
|
15
|
+
}
|
|
16
|
+
export declare const createUserExternalConnectionParamsType: Type<CreateUserExternalConnectionParams>;
|
|
17
|
+
/**
|
|
18
|
+
* Parameters for disconnecting the current user from a third-party provider.
|
|
19
|
+
*
|
|
20
|
+
* This is the ONLY write path a client has to the connection pair. There is deliberately no connect
|
|
21
|
+
* or update params type here: connecting requires credentials, which only the server ever sees.
|
|
22
|
+
*
|
|
23
|
+
* If no target model is provided, the current user's connection document is assumed.
|
|
24
|
+
*
|
|
25
|
+
* @dbxModelApiParams
|
|
26
|
+
*/
|
|
27
|
+
export interface DisconnectUserExternalConnectionParams extends InferredTargetModelParams {
|
|
28
|
+
/**
|
|
29
|
+
* The provider type to disconnect from.
|
|
30
|
+
*/
|
|
31
|
+
readonly providerType: UserExternalConnectionProviderType;
|
|
32
|
+
}
|
|
33
|
+
export declare const disconnectUserExternalConnectionParamsType: Type<DisconnectUserExternalConnectionParams>;
|
|
34
|
+
/**
|
|
35
|
+
* Parameters for beginning an OAuth connect handoff for a provider.
|
|
36
|
+
*
|
|
37
|
+
* @dbxModelApiParams
|
|
38
|
+
*/
|
|
39
|
+
export interface ReadUserExternalConnectionAuthorizeStateParams extends InferredTargetModelParams {
|
|
40
|
+
/**
|
|
41
|
+
* The provider type to begin connecting to.
|
|
42
|
+
*/
|
|
43
|
+
readonly providerType: UserExternalConnectionProviderType;
|
|
44
|
+
}
|
|
45
|
+
export declare const readUserExternalConnectionAuthorizeStateParamsType: Type<ReadUserExternalConnectionAuthorizeStateParams>;
|
|
46
|
+
/**
|
|
47
|
+
* The opaque, short-lived `state` to carry through a provider's OAuth handoff.
|
|
48
|
+
*
|
|
49
|
+
* Minting it requires an authenticated call, because a top-level navigation to the provider's
|
|
50
|
+
* authorize endpoint carries no credentials and the server must already know who is connecting. The
|
|
51
|
+
* client's only job is to pass this through — it must never append its ID token to the redirect.
|
|
52
|
+
*/
|
|
53
|
+
export interface UserExternalConnectionAuthorizeStateResult {
|
|
54
|
+
/**
|
|
55
|
+
* The state to send on the authorize request.
|
|
56
|
+
*/
|
|
57
|
+
readonly state: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Custom (non-CRUD) function type map for UserExternalConnection. There are none.
|
|
61
|
+
*/
|
|
62
|
+
export type UserExternalConnectionFunctionTypeMap = {};
|
|
63
|
+
export declare const USER_EXTERNAL_CONNECTION_FUNCTION_TYPE_CONFIG_MAP: FirebaseFunctionTypeConfigMap<UserExternalConnectionFunctionTypeMap>;
|
|
64
|
+
/**
|
|
65
|
+
* CRUD function configuration map for the UserExternalConnection model.
|
|
66
|
+
*/
|
|
67
|
+
export type UserExternalConnectionModelCrudFunctionsConfig = {
|
|
68
|
+
readonly userExternalConnection: {
|
|
69
|
+
/**
|
|
70
|
+
* Creates the calling user's connection document.
|
|
71
|
+
*
|
|
72
|
+
* Every other client-reachable operation asserts a role against this document, so it must exist
|
|
73
|
+
* before a user can begin a connect. Creating it is its own call — rather than a side effect of
|
|
74
|
+
* the first connect — so that "may this user have external connections at all?" is decided in
|
|
75
|
+
* one place instead of being folded into the OAuth handoff.
|
|
76
|
+
*
|
|
77
|
+
* Throws when the document already exists.
|
|
78
|
+
*/
|
|
79
|
+
create: CreateUserExternalConnectionParams;
|
|
80
|
+
read: {
|
|
81
|
+
/**
|
|
82
|
+
* Mints the short-lived `state` that begins an OAuth connect handoff for a provider.
|
|
83
|
+
*
|
|
84
|
+
* A read rather than an update: it changes nothing, it just proves who is asking. The app
|
|
85
|
+
* decides how the state is signed and how long it lives.
|
|
86
|
+
*/
|
|
87
|
+
authorizeState: [ReadUserExternalConnectionAuthorizeStateParams, UserExternalConnectionAuthorizeStateResult];
|
|
88
|
+
};
|
|
89
|
+
update: {
|
|
90
|
+
/**
|
|
91
|
+
* Disconnects the current user from the given provider.
|
|
92
|
+
*
|
|
93
|
+
* Removes the provider's credentials and its entry in one transaction, and recomputes the
|
|
94
|
+
* connected-provider array from the result.
|
|
95
|
+
*/
|
|
96
|
+
disconnect: DisconnectUserExternalConnectionParams;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
export declare const USER_EXTERNAL_CONNECTION_MODEL_CRUD_FUNCTIONS_CONFIG: ModelFirebaseCrudFunctionConfigMap<UserExternalConnectionModelCrudFunctionsConfig, UserExternalConnectionTypes>;
|
|
101
|
+
/**
|
|
102
|
+
* Abstract class defining all callable UserExternalConnection cloud functions.
|
|
103
|
+
*
|
|
104
|
+
* Implement this in your app module to wire up the function endpoints.
|
|
105
|
+
*/
|
|
106
|
+
export declare abstract class UserExternalConnectionFunctions implements ModelFirebaseFunctionMap<UserExternalConnectionFunctionTypeMap, UserExternalConnectionModelCrudFunctionsConfig> {
|
|
107
|
+
abstract userExternalConnection: {
|
|
108
|
+
createUserExternalConnection: ModelFirebaseCreateFunction<CreateUserExternalConnectionParams>;
|
|
109
|
+
readUserExternalConnection: {
|
|
110
|
+
authorizeState: ModelFirebaseCrudFunction<ReadUserExternalConnectionAuthorizeStateParams, UserExternalConnectionAuthorizeStateResult>;
|
|
111
|
+
};
|
|
112
|
+
updateUserExternalConnection: {
|
|
113
|
+
disconnect: ModelFirebaseCrudFunction<DisconnectUserExternalConnectionParams>;
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Used to generate the UserExternalConnectionFunctions map for a Functions instance.
|
|
119
|
+
*/
|
|
120
|
+
export declare const userExternalConnectionFunctionMap: import("../..").ModelFirebaseFunctionMapFactory<UserExternalConnectionFunctionTypeMap, UserExternalConnectionModelCrudFunctionsConfig>;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { type Maybe } from '@dereekb/util';
|
|
2
|
+
import { type GrantedReadRole, type GrantedUpdateRole } from '@dereekb/model';
|
|
3
|
+
import { AbstractFirestoreDocument, type CollectionReference, type FirestoreCollection, type FirestoreContext } from '../../common';
|
|
4
|
+
import { type UserRelated, type UserRelatedById } from '../user';
|
|
5
|
+
import { type UserExternalConnectionCapability, type UserExternalConnectionExternalAccountId, type UserExternalConnectionProviderType } from './userexternalconnection.id';
|
|
6
|
+
/**
|
|
7
|
+
* Provides access to the {@link UserExternalConnection} collection.
|
|
8
|
+
*
|
|
9
|
+
* NOTE: the private half of this model pair (`UserExternalConnectionPrivate`) is deliberately NOT
|
|
10
|
+
* declared here. It exists only in `@dereekb/firebase-server/model`, so client-shared code cannot
|
|
11
|
+
* name it.
|
|
12
|
+
*
|
|
13
|
+
* @dbxModelGroup UserExternalConnection
|
|
14
|
+
*/
|
|
15
|
+
export interface UserExternalConnectionFirestoreCollections {
|
|
16
|
+
readonly userExternalConnectionCollection: UserExternalConnectionFirestoreCollection;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Union of all UserExternalConnection model identity types.
|
|
20
|
+
*/
|
|
21
|
+
export type UserExternalConnectionTypes = typeof userExternalConnectionIdentity;
|
|
22
|
+
export declare const userExternalConnectionIdentity: import("../..").RootFirestoreModelIdentity<"userExternalConnection", "uec">;
|
|
23
|
+
/**
|
|
24
|
+
* Status of a single third-party connection.
|
|
25
|
+
*
|
|
26
|
+
* - `connected` — credentials are present and believed usable
|
|
27
|
+
* - `disconnected` — the user (or the server) revoked the connection. Retained only for history.
|
|
28
|
+
* - `error` — the connection exists but the credentials stopped working and need attention
|
|
29
|
+
*/
|
|
30
|
+
export type UserExternalConnectionEntryStatus = 'connected' | 'disconnected' | 'error';
|
|
31
|
+
export declare const USER_EXTERNAL_CONNECTION_ENTRY_STATUSES: UserExternalConnectionEntryStatus[];
|
|
32
|
+
/**
|
|
33
|
+
* Short code describing why an entry is in the `error` status.
|
|
34
|
+
*
|
|
35
|
+
* Deliberately a code and not a message: the UI decides the wording, and a stored message would
|
|
36
|
+
* leak provider internals into a client-readable document.
|
|
37
|
+
*/
|
|
38
|
+
export type UserExternalConnectionErrorCode = 'unauthorized' | 'expired' | 'revoked' | 'insufficient_scope' | 'provider_error' | 'unknown';
|
|
39
|
+
/**
|
|
40
|
+
* Per-provider connection state stored inside a {@link UserExternalConnection}.
|
|
41
|
+
*
|
|
42
|
+
* Every field here is DERIVED by the server from the credentials that were stored alongside it —
|
|
43
|
+
* see `userExternalConnectionEntryForOutcome()`. Nothing on this interface may be supplied directly
|
|
44
|
+
* by a caller, otherwise the summary could contradict the credentials it summarizes.
|
|
45
|
+
*
|
|
46
|
+
* @dbxModelSubObject
|
|
47
|
+
*/
|
|
48
|
+
export interface UserExternalConnectionEntry {
|
|
49
|
+
/**
|
|
50
|
+
* Current status of this connection.
|
|
51
|
+
*
|
|
52
|
+
* @dbxModelVariable status
|
|
53
|
+
*/
|
|
54
|
+
st: UserExternalConnectionEntryStatus;
|
|
55
|
+
/**
|
|
56
|
+
* Capabilities/scopes granted by the provider.
|
|
57
|
+
*
|
|
58
|
+
* @dbxModelVariable capabilities
|
|
59
|
+
*/
|
|
60
|
+
ca?: Maybe<UserExternalConnectionCapability[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Identifier of the connected account within the provider.
|
|
63
|
+
*
|
|
64
|
+
* @dbxModelVariable externalAccountId
|
|
65
|
+
*/
|
|
66
|
+
ea?: Maybe<UserExternalConnectionExternalAccountId>;
|
|
67
|
+
/**
|
|
68
|
+
* Human-readable label for the connected account (e.g. the provider-side email or username).
|
|
69
|
+
*
|
|
70
|
+
* @dbxModelVariable label
|
|
71
|
+
*/
|
|
72
|
+
l?: Maybe<string>;
|
|
73
|
+
/**
|
|
74
|
+
* Date the connection was first established.
|
|
75
|
+
*
|
|
76
|
+
* @dbxModelVariable connectedAt
|
|
77
|
+
*/
|
|
78
|
+
coa?: Maybe<Date>;
|
|
79
|
+
/**
|
|
80
|
+
* Date the current access credentials expire at, when known.
|
|
81
|
+
*
|
|
82
|
+
* @dbxModelVariable expiresAt
|
|
83
|
+
*/
|
|
84
|
+
exa?: Maybe<Date>;
|
|
85
|
+
/**
|
|
86
|
+
* Date this entry was last updated at.
|
|
87
|
+
*
|
|
88
|
+
* @dbxModelVariable updatedAt
|
|
89
|
+
*/
|
|
90
|
+
uat: Date;
|
|
91
|
+
/**
|
|
92
|
+
* Reason the entry is in the `error` status. Cleared on any non-error outcome.
|
|
93
|
+
*
|
|
94
|
+
* @dbxModelVariable errorCode
|
|
95
|
+
*/
|
|
96
|
+
er?: Maybe<UserExternalConnectionErrorCode>;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Map of provider type to the user's connection state for that provider.
|
|
100
|
+
*/
|
|
101
|
+
export type UserExternalConnectionEntryMap = Record<UserExternalConnectionProviderType, UserExternalConnectionEntry>;
|
|
102
|
+
/**
|
|
103
|
+
* The client-readable half of a user's third-party OAuth connection state.
|
|
104
|
+
*
|
|
105
|
+
* There is exactly ONE of these per user, keyed by uid — per-provider details live inside `e`
|
|
106
|
+
* rather than in separate documents. The server-only half holding the actual access/refresh
|
|
107
|
+
* tokens is `UserExternalConnectionPrivate` in `@dereekb/firebase-server/model`, which shares this
|
|
108
|
+
* document's id.
|
|
109
|
+
*
|
|
110
|
+
* The two documents are NEVER written independently. Every mutation goes through the paired
|
|
111
|
+
* transaction accessor in `@dereekb/firebase-server/model`, which derives everything on this
|
|
112
|
+
* document from the same input that produces the credentials. There is deliberately no sync,
|
|
113
|
+
* reconciliation, or drift-detection process — divergence is unrepresentable rather than detectable.
|
|
114
|
+
*
|
|
115
|
+
* @dbxModel
|
|
116
|
+
* @dbxModelRead owner
|
|
117
|
+
*/
|
|
118
|
+
export interface UserExternalConnection extends UserRelated, UserRelatedById {
|
|
119
|
+
/**
|
|
120
|
+
* Per-provider connection state, keyed by provider type.
|
|
121
|
+
*
|
|
122
|
+
* @dbxModelVariable entries
|
|
123
|
+
*/
|
|
124
|
+
e: UserExternalConnectionEntryMap;
|
|
125
|
+
/**
|
|
126
|
+
* DERIVED from `e`: every provider type whose entry status is `connected`.
|
|
127
|
+
*
|
|
128
|
+
* This exists solely so "which users are connected to X?" stays queryable after collapsing to a
|
|
129
|
+
* single document per user — Firestore cannot query across map keys, but it can `array-contains`
|
|
130
|
+
* this field. It is recomputed from `e` on every write and is never passed in by a caller.
|
|
131
|
+
*
|
|
132
|
+
* Entries in the `disconnected` or `error` status are excluded: a "usable connections" query that
|
|
133
|
+
* returned users whose credentials stopped working would be worse than useless, and
|
|
134
|
+
* `array-contains` cannot filter by status to compensate.
|
|
135
|
+
*
|
|
136
|
+
* @dbxModelVariable connectedProviderTypes
|
|
137
|
+
*/
|
|
138
|
+
c: UserExternalConnectionProviderType[];
|
|
139
|
+
/**
|
|
140
|
+
* Date this document was last updated at.
|
|
141
|
+
*
|
|
142
|
+
* @dbxModelVariable updatedAt
|
|
143
|
+
*/
|
|
144
|
+
uat: Date;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Roles for a UserExternalConnection. Users can read their own connection state; all writes go
|
|
148
|
+
* through the server.
|
|
149
|
+
*
|
|
150
|
+
* `connect` and `disconnect` are called out separately from `update` because they are the only
|
|
151
|
+
* operations a client can reach, so an app can withhold either one (a user allowed to drop a
|
|
152
|
+
* connection but not to add another, or the reverse) without also withholding the server-driven
|
|
153
|
+
* writes that share `update`.
|
|
154
|
+
*/
|
|
155
|
+
export type UserExternalConnectionRoles = GrantedReadRole | GrantedUpdateRole | 'connect' | 'disconnect';
|
|
156
|
+
export declare class UserExternalConnectionDocument extends AbstractFirestoreDocument<UserExternalConnection, UserExternalConnectionDocument, typeof userExternalConnectionIdentity> {
|
|
157
|
+
get modelIdentity(): import("../..").RootFirestoreModelIdentity<"userExternalConnection", "uec">;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Field conversions for a {@link UserExternalConnectionEntry}.
|
|
161
|
+
*/
|
|
162
|
+
export declare const userExternalConnectionEntryFields: {
|
|
163
|
+
st: import("../..").FirestoreModelFieldMapFunctionsConfig<UserExternalConnectionEntryStatus, UserExternalConnectionEntryStatus>;
|
|
164
|
+
ca: import("../..").FirestoreModelFieldMapFunctionsConfig<Maybe<string[]>, Maybe<string[]>>;
|
|
165
|
+
ea: import("../..").FirestoreModelFieldMapFunctionsConfig<Maybe<string>, Maybe<string>>;
|
|
166
|
+
l: import("../..").FirestoreModelFieldMapFunctionsConfig<Maybe<string>, Maybe<string>>;
|
|
167
|
+
coa: import("../..").FirestoreModelFieldMapFunctionsConfig<Maybe<Date>, Maybe<string>>;
|
|
168
|
+
exa: import("../..").FirestoreModelFieldMapFunctionsConfig<Maybe<Date>, Maybe<string>>;
|
|
169
|
+
uat: import("../..").FirestoreModelFieldMapFunctionsConfig<Date, string>;
|
|
170
|
+
er: import("../..").FirestoreModelFieldMapFunctionsConfig<Maybe<UserExternalConnectionErrorCode>, Maybe<UserExternalConnectionErrorCode>>;
|
|
171
|
+
};
|
|
172
|
+
export declare const userExternalConnectionConverter: import("../..").SnapshotConverterFunctions<UserExternalConnection, Partial<import("@dereekb/util").ReplaceType<UserExternalConnection, import("@dereekb/util").MaybeMap<object>, any>>>;
|
|
173
|
+
/**
|
|
174
|
+
* Copies the document id into `uid` on write, so the stored uid can never drift from the document id.
|
|
175
|
+
*/
|
|
176
|
+
export declare const userExternalConnectionAccessorFactory: import("../..").InterceptAccessorFactoryFunction<UserExternalConnection, import("../..").DocumentData>;
|
|
177
|
+
/**
|
|
178
|
+
* Returns the root Firestore collection reference for UserExternalConnection documents.
|
|
179
|
+
*
|
|
180
|
+
* @param context - The FirestoreContext used to resolve the collection.
|
|
181
|
+
* @returns A typed CollectionReference for the userExternalConnection collection.
|
|
182
|
+
*/
|
|
183
|
+
export declare function userExternalConnectionCollectionReference(context: FirestoreContext): CollectionReference<UserExternalConnection>;
|
|
184
|
+
export type UserExternalConnectionFirestoreCollection = FirestoreCollection<UserExternalConnection, UserExternalConnectionDocument>;
|
|
185
|
+
/**
|
|
186
|
+
* Creates the Firestore collection accessor for UserExternalConnection documents.
|
|
187
|
+
*
|
|
188
|
+
* @param firestoreContext - The FirestoreContext used to build the collection.
|
|
189
|
+
* @returns A UserExternalConnectionFirestoreCollection.
|
|
190
|
+
*/
|
|
191
|
+
export declare function userExternalConnectionFirestoreCollection(firestoreContext: FirestoreContext): UserExternalConnectionFirestoreCollection;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type FirestoreModelId, type FirestoreModelKey } from '../../common';
|
|
2
|
+
/**
|
|
3
|
+
* Document id for a {@link UserExternalConnection}.
|
|
4
|
+
*
|
|
5
|
+
* The document id IS the user's Firebase Auth uid. There is exactly one document per user.
|
|
6
|
+
*/
|
|
7
|
+
export type UserExternalConnectionId = FirestoreModelId;
|
|
8
|
+
/**
|
|
9
|
+
* Full Firestore model key path for a {@link UserExternalConnection} document.
|
|
10
|
+
*/
|
|
11
|
+
export type UserExternalConnectionKey = FirestoreModelKey;
|
|
12
|
+
/**
|
|
13
|
+
* String identifier for a third-party service a user can connect their account to.
|
|
14
|
+
*
|
|
15
|
+
* Used as the key of the per-provider entry map on a UserExternalConnection, so it must be a
|
|
16
|
+
* valid Firestore map key (no dots or slashes) and must be identical on the server paths that
|
|
17
|
+
* write the connection and the client paths that render it.
|
|
18
|
+
*/
|
|
19
|
+
export type UserExternalConnectionProviderType = string;
|
|
20
|
+
/**
|
|
21
|
+
* Known third-party services the workspace ships provider support for.
|
|
22
|
+
*/
|
|
23
|
+
export type KnownUserExternalConnectionProviderType = 'calcom' | 'zoom' | 'discord' | 'zoho';
|
|
24
|
+
/**
|
|
25
|
+
* Provider type for Cal.com.
|
|
26
|
+
*
|
|
27
|
+
* Declared here rather than in a server package because the string is the map key on BOTH halves of
|
|
28
|
+
* the connection pair: the server's OAuth controller writes it and the client renders from it, so
|
|
29
|
+
* they must be the same literal.
|
|
30
|
+
*/
|
|
31
|
+
export declare const CALCOM_USER_EXTERNAL_CONNECTION_PROVIDER_TYPE: KnownUserExternalConnectionProviderType;
|
|
32
|
+
/**
|
|
33
|
+
* Provider type for Zoom.
|
|
34
|
+
*/
|
|
35
|
+
export declare const ZOOM_USER_EXTERNAL_CONNECTION_PROVIDER_TYPE: KnownUserExternalConnectionProviderType;
|
|
36
|
+
/**
|
|
37
|
+
* Provider type for Discord.
|
|
38
|
+
*/
|
|
39
|
+
export declare const DISCORD_USER_EXTERNAL_CONNECTION_PROVIDER_TYPE: KnownUserExternalConnectionProviderType;
|
|
40
|
+
/**
|
|
41
|
+
* Provider type for Zoho.
|
|
42
|
+
*/
|
|
43
|
+
export declare const ZOHO_USER_EXTERNAL_CONNECTION_PROVIDER_TYPE: KnownUserExternalConnectionProviderType;
|
|
44
|
+
/**
|
|
45
|
+
* A capability/scope string granted by a third-party provider (e.g. an OAuth scope).
|
|
46
|
+
*/
|
|
47
|
+
export type UserExternalConnectionCapability = string;
|
|
48
|
+
/**
|
|
49
|
+
* Identifier for the connected account within the third-party provider.
|
|
50
|
+
*/
|
|
51
|
+
export type UserExternalConnectionExternalAccountId = string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type FirestoreQueryConstraint } from '../../common';
|
|
2
|
+
import { type UserExternalConnectionProviderType } from './userexternalconnection.id';
|
|
3
|
+
/**
|
|
4
|
+
* Query for the UserExternalConnection documents that are currently connected to the given provider.
|
|
5
|
+
*
|
|
6
|
+
* This is the single reason the derived `c` array exists: with one document per user, provider ids
|
|
7
|
+
* are map keys, and Firestore cannot query across map keys.
|
|
8
|
+
*
|
|
9
|
+
* Only `connected` entries are members of `c`, so this never returns a user whose credentials are in
|
|
10
|
+
* the `error` or `disconnected` state.
|
|
11
|
+
*
|
|
12
|
+
* @param providerType - The provider type to search for.
|
|
13
|
+
* @returns Firestore query constraints matching users connected to that provider.
|
|
14
|
+
*
|
|
15
|
+
* @dbxModelFirebaseIndex
|
|
16
|
+
* @dbxModelFirebaseIndexModel UserExternalConnection
|
|
17
|
+
* @dbxModelFirebaseIndexScope COLLECTION
|
|
18
|
+
*/
|
|
19
|
+
export declare function userExternalConnectionsWithConnectedProviderQuery(providerType: UserExternalConnectionProviderType): FirestoreQueryConstraint[];
|