@dereekb/firebase-server 13.33.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/calcom/package.json +10 -10
- package/discord/package.json +10 -10
- package/index.cjs.js +28 -6
- package/index.esm.js +28 -6
- package/mailgun/package.json +9 -9
- package/mcp/package.json +11 -11
- package/model/index.cjs.js +468 -211
- package/model/index.esm.js +463 -214
- package/model/package.json +9 -9
- package/model/src/lib/index.d.ts +1 -0
- package/model/src/lib/system/index.d.ts +2 -0
- package/model/src/lib/system/system.private.d.ts +63 -0
- package/model/src/lib/system/system.private.module.d.ts +70 -0
- package/oidc/package.json +10 -10
- package/package.json +12 -12
- package/src/lib/firestore/snapshot/snapshot.field.encrypt.d.ts +21 -2
- package/test/package.json +11 -11
- package/twilio/package.json +8 -8
- package/zoho/README.md +30 -0
- package/zoho/index.cjs.js +200 -36
- package/zoho/index.esm.js +193 -35
- package/zoho/package.json +11 -10
- package/zoho/src/lib/index.d.ts +1 -0
- package/zoho/src/lib/zoho.accounts.firebase.d.ts +10 -3
- package/zoho/src/lib/zoho.accounts.firebase.module.d.ts +51 -0
- package/zoho/src/lib/zoho.accounts.firebase.system.d.ts +51 -8
package/zoho/index.cjs.js
CHANGED
|
@@ -1,57 +1,114 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var firebase = require('@dereekb/firebase');
|
|
4
|
+
var firebaseServer = require('@dereekb/firebase-server');
|
|
4
5
|
var util = require('@dereekb/util');
|
|
6
|
+
var nestjs = require('@dereekb/nestjs');
|
|
5
7
|
var zoho = require('@dereekb/zoho');
|
|
6
8
|
var model = require('@dereekb/firebase-server/model');
|
|
7
9
|
var date = require('@dereekb/date');
|
|
8
10
|
var common = require('@nestjs/common');
|
|
9
|
-
var nestjs = require('@dereekb/zoho/nestjs');
|
|
11
|
+
var nestjs$1 = require('@dereekb/zoho/nestjs');
|
|
10
12
|
var config = require('@nestjs/config');
|
|
11
|
-
var firebaseServer = require('@dereekb/firebase-server');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* {@link SystemState} type identifier for storing Zoho access tokens in Firestore.
|
|
15
16
|
*/ var ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE = 'zoho_access_token';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Creates the embedded-token converter, encrypting the `accessToken` at rest.
|
|
19
|
+
*
|
|
20
|
+
* This is a factory rather than a module-level const because `firestoreEncryptedField` resolves and
|
|
21
|
+
* validates the encryption key eagerly at construction — the secret must be known at runtime.
|
|
22
|
+
*
|
|
23
|
+
* ONLY `accessToken` is encrypted, deliberately. `firestoreEncryptedField` round-trips through
|
|
24
|
+
* `JSON.stringify`/`JSON.parse`, so anything placed inside the encrypted blob loses its type — and
|
|
25
|
+
* `expiresAt` is a `Date`. Encrypting the token string alone keeps every date outside the blob,
|
|
26
|
+
* which is what lets {@link zohoAccessTokenSystemStateDataConverterFactory} keep filtering expired
|
|
27
|
+
* entries without having to decrypt them first. Do not "improve" this by encrypting the whole
|
|
28
|
+
* token object or the `tokens` array.
|
|
29
|
+
*
|
|
30
|
+
* Accepted trade-off: `key`, `scope`, `apiDomain`, `expiresIn` and `expiresAt` remain plaintext at
|
|
31
|
+
* rest. None of them is a credential.
|
|
32
|
+
*
|
|
33
|
+
* @param config - The encryption configuration.
|
|
34
|
+
* @returns The embedded token field converter.
|
|
35
|
+
*/ function zohoAccessTokenSystemStateEmbeddedTokenConverterFactory(config) {
|
|
36
|
+
return firebase.firestoreSubObject({
|
|
37
|
+
objectField: {
|
|
38
|
+
fields: {
|
|
39
|
+
key: firebase.firestoreString(),
|
|
40
|
+
accessToken: firebaseServer.firestoreEncryptedField({
|
|
41
|
+
secret: config.encryptionSecret,
|
|
42
|
+
default: '',
|
|
43
|
+
// This is a cache of ~1h tokens, so an undecryptable entry is a cache MISS, not an error.
|
|
44
|
+
// The empty sentinel is dropped by the `tokens` filter, and the next Zoho call re-mints.
|
|
45
|
+
// This is also what makes a rotated secret survivable here (unlike uecp/jwks).
|
|
46
|
+
onDecodeFailure: function onDecodeFailure() {
|
|
47
|
+
return '';
|
|
48
|
+
}
|
|
49
|
+
}),
|
|
50
|
+
scope: firebase.firestoreString(),
|
|
51
|
+
apiDomain: firebase.firestoreString(),
|
|
52
|
+
expiresIn: firebase.firestoreNumber({
|
|
53
|
+
default: 3600
|
|
54
|
+
}),
|
|
55
|
+
expiresAt: firebase.firestoreDate()
|
|
56
|
+
}
|
|
27
57
|
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
30
60
|
/**
|
|
31
61
|
* Firestore field converter for {@link ZohoAccessTokenSystemStateData}.
|
|
32
62
|
*
|
|
33
63
|
* Automatically filters out expired tokens on read and enforces uniqueness by service key.
|
|
34
64
|
* Must be registered in the app's {@link SystemStateStoredDataConverterMap} under
|
|
35
65
|
* the {@link ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE} key.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
66
|
+
*/ /**
|
|
67
|
+
* Builds the {@link ZohoAccessTokenSystemStateData} converter around a given embedded-token converter.
|
|
68
|
+
*
|
|
69
|
+
* Shared by the encrypted factory and the deprecated plaintext const so the array's expiry filter and
|
|
70
|
+
* per-key dedup behavior can only ever be defined once.
|
|
71
|
+
*
|
|
72
|
+
* @param embeddedTokenConverter - The converter for each entry in the `tokens` array.
|
|
73
|
+
* @returns The stored-data field converter.
|
|
74
|
+
*/ function zohoAccessTokenSystemStateDataConverterForEmbeddedTokenConverter(embeddedTokenConverter) {
|
|
75
|
+
return firebase.firestoreSubObject({
|
|
76
|
+
objectField: {
|
|
77
|
+
fields: {
|
|
78
|
+
tokens: firebase.firestoreObjectArray({
|
|
79
|
+
firestoreField: embeddedTokenConverter,
|
|
80
|
+
filterUnique: util.filterUniqueFunction(function(x) {
|
|
81
|
+
return x.key;
|
|
82
|
+
}),
|
|
83
|
+
// `firestoreObjectArray` maps BEFORE it filters, so this runs on already-decoded entries.
|
|
84
|
+
// The `accessToken` check is what drops an entry whose decryption failed (onDecodeFailure
|
|
85
|
+
// leaves an empty string behind) — without it such an entry would surface as a token with
|
|
86
|
+
// an empty secret rather than as a cache miss.
|
|
87
|
+
filter: function filter(x) {
|
|
88
|
+
return Boolean(x === null || x === void 0 ? void 0 : x.accessToken) && ((x === null || x === void 0 ? void 0 : x.expiresAt) ? !util.isPast(x.expiresAt) : true // filter out empty/expired values
|
|
89
|
+
);
|
|
90
|
+
}
|
|
43
91
|
}),
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}),
|
|
49
|
-
lat: firebase.firestoreDate({
|
|
50
|
-
saveDefaultAsNow: true
|
|
51
|
-
})
|
|
92
|
+
lat: firebase.firestoreDate({
|
|
93
|
+
saveDefaultAsNow: true
|
|
94
|
+
})
|
|
95
|
+
}
|
|
52
96
|
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Creates the {@link ZohoAccessTokenSystemStateData} converter, encrypting each token's
|
|
101
|
+
* `accessToken` at rest.
|
|
102
|
+
*
|
|
103
|
+
* Register the result in a SERVER-ONLY converter map under {@link ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE} —
|
|
104
|
+
* see `systemStatePrivateFirestoreCollection()` in `@dereekb/firebase-server/model`. It must never be
|
|
105
|
+
* registered in an app's client-shared `SystemStateStoredDataConverterMap`.
|
|
106
|
+
*
|
|
107
|
+
* @param config - The encryption configuration.
|
|
108
|
+
* @returns The stored-data field converter.
|
|
109
|
+
*/ function zohoAccessTokenSystemStateDataConverterFactory(config) {
|
|
110
|
+
return zohoAccessTokenSystemStateDataConverterForEmbeddedTokenConverter(zohoAccessTokenSystemStateEmbeddedTokenConverterFactory(config));
|
|
111
|
+
}
|
|
55
112
|
/**
|
|
56
113
|
* Loads the {@link SystemStateDocument} that stores {@link ZohoAccessTokenSystemStateData},
|
|
57
114
|
* using {@link ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE} as the document ID.
|
|
@@ -67,6 +124,28 @@ var zohoAccessTokenSystemStateEmbeddedTokenConverter = firebase.firestoreSubObje
|
|
|
67
124
|
*/ function loadZohoAccessTokenSystemState(accessor) {
|
|
68
125
|
return accessor.loadDocumentForId(ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE);
|
|
69
126
|
}
|
|
127
|
+
// COMPAT: Deprecated aliases
|
|
128
|
+
/**
|
|
129
|
+
* @deprecated stores the access token in PLAINTEXT. Use
|
|
130
|
+
* {@link zohoAccessTokenSystemStateEmbeddedTokenConverterFactory} instead, which encrypts it at rest.
|
|
131
|
+
*/ var zohoAccessTokenSystemStateEmbeddedTokenConverter = firebase.firestoreSubObject({
|
|
132
|
+
objectField: {
|
|
133
|
+
fields: {
|
|
134
|
+
key: firebase.firestoreString(),
|
|
135
|
+
accessToken: firebase.firestoreString(),
|
|
136
|
+
scope: firebase.firestoreString(),
|
|
137
|
+
apiDomain: firebase.firestoreString(),
|
|
138
|
+
expiresIn: firebase.firestoreNumber({
|
|
139
|
+
default: 3600
|
|
140
|
+
}),
|
|
141
|
+
expiresAt: firebase.firestoreDate()
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
/**
|
|
146
|
+
* @deprecated stores access tokens in PLAINTEXT. Use {@link zohoAccessTokenSystemStateDataConverterFactory}
|
|
147
|
+
* instead, and register it on a server-only SystemStatePrivate collection.
|
|
148
|
+
*/ var zohoAccessTokenSystemStateDataConverter = zohoAccessTokenSystemStateDataConverterForEmbeddedTokenConverter(zohoAccessTokenSystemStateEmbeddedTokenConverter);
|
|
70
149
|
|
|
71
150
|
function _array_like_to_array$1(arr, len) {
|
|
72
151
|
if (len == null || len > arr.length) len = arr.length;
|
|
@@ -105,7 +184,7 @@ function _async_to_generator$2(fn) {
|
|
|
105
184
|
});
|
|
106
185
|
};
|
|
107
186
|
}
|
|
108
|
-
function _define_property$
|
|
187
|
+
function _define_property$5(obj, key, value) {
|
|
109
188
|
if (key in obj) {
|
|
110
189
|
Object.defineProperty(obj, key, {
|
|
111
190
|
value: value,
|
|
@@ -134,7 +213,7 @@ function _object_spread$2(target) {
|
|
|
134
213
|
}));
|
|
135
214
|
}
|
|
136
215
|
ownKeys.forEach(function(key) {
|
|
137
|
-
_define_property$
|
|
216
|
+
_define_property$5(target, key, source[key]);
|
|
138
217
|
});
|
|
139
218
|
}
|
|
140
219
|
return target;
|
|
@@ -275,12 +354,19 @@ function _ts_generator$2(thisArg, body) {
|
|
|
275
354
|
* Tokens are stored in a single {@link SystemState} document (type {@link ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE})
|
|
276
355
|
* and token updates/clears use Firestore transactions for concurrency safety.
|
|
277
356
|
*
|
|
357
|
+
* The access token is a credential, so pass a SERVER-ONLY collection — a
|
|
358
|
+
* `systemStatePrivateFirestoreCollection()` from `@dereekb/firebase-server/model`, whose converter
|
|
359
|
+
* map registers {@link zohoAccessTokenSystemStateDataConverterFactory} and encrypts the token at
|
|
360
|
+
* rest. Passing an app's client-shared `systemStateCollection` still type-checks (for backwards
|
|
361
|
+
* compatibility) but requires the converter to be registered in the client-shared map, which drags
|
|
362
|
+
* `@dereekb/firebase-server` into browser builds.
|
|
363
|
+
*
|
|
278
364
|
* @param systemStateCollection - The Firestore collection for system state documents.
|
|
279
365
|
* @returns A cache service backed by Firestore system state documents.
|
|
280
366
|
*
|
|
281
367
|
* @example
|
|
282
368
|
* ```ts
|
|
283
|
-
* const cacheService = firebaseZohoAccountsAccessTokenCacheService(
|
|
369
|
+
* const cacheService = firebaseZohoAccountsAccessTokenCacheService(systemStatePrivateCollection);
|
|
284
370
|
* const cache = cacheService.loadZohoAccessTokenCache('my-zoho-service');
|
|
285
371
|
* const token = await cache.loadCachedToken();
|
|
286
372
|
* ```
|
|
@@ -456,6 +542,78 @@ function _ts_generator$2(thisArg, body) {
|
|
|
456
542
|
return service;
|
|
457
543
|
}
|
|
458
544
|
|
|
545
|
+
function _define_property$4(obj, key, value) {
|
|
546
|
+
if (key in obj) {
|
|
547
|
+
Object.defineProperty(obj, key, {
|
|
548
|
+
value: value,
|
|
549
|
+
enumerable: true,
|
|
550
|
+
configurable: true,
|
|
551
|
+
writable: true
|
|
552
|
+
});
|
|
553
|
+
} else {
|
|
554
|
+
obj[key] = value;
|
|
555
|
+
}
|
|
556
|
+
return obj;
|
|
557
|
+
}
|
|
558
|
+
// MARK: Environment Variable Keys
|
|
559
|
+
/**
|
|
560
|
+
* Environment variable name for the Zoho access token cache encryption secret
|
|
561
|
+
* (hex-encoded AES-256 key).
|
|
562
|
+
*
|
|
563
|
+
* There is NO key rotation — `firestoreEncryptedField` resolves and validates the key once at
|
|
564
|
+
* converter construction and closes over it. Unlike the `uecp` and `oidcJwksKey` secrets, however,
|
|
565
|
+
* rotating this one is SURVIVABLE: the Zoho converter supplies an `onDecodeFailure` handler, so
|
|
566
|
+
* every entry written under the old key simply degrades to a cache miss and the next Zoho call
|
|
567
|
+
* re-mints a token. Rotation costs one extra token request per service key, not an outage.
|
|
568
|
+
*/ var ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET_ENV_KEY = 'ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET';
|
|
569
|
+
/**
|
|
570
|
+
* Deterministic secret used when running in a testing environment and no real secret is configured,
|
|
571
|
+
* so specs never need a live credential.
|
|
572
|
+
*
|
|
573
|
+
* Deliberately distinct from the OIDC JWKS and UserExternalConnection testing secrets so a leaked
|
|
574
|
+
* emulator blob is attributable. ("Zoho Access Token Cache Test Key", hex-encoded.)
|
|
575
|
+
*/ var TESTING_ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET = '5a6f686f2041636365737320546f6b656e2043616368652054657374204b6579';
|
|
576
|
+
// MARK: Config
|
|
577
|
+
/**
|
|
578
|
+
* Reads the Zoho access token encryption secret from the environment.
|
|
579
|
+
*
|
|
580
|
+
* @param configService - The Nest config service used to read the encryption secret.
|
|
581
|
+
* @param envService - Used to detect a testing environment for the secret fallback.
|
|
582
|
+
* @returns The validated encryption secret.
|
|
583
|
+
* @throws {Error} When the configured secret is invalid outside a testing environment.
|
|
584
|
+
*/ function zohoAccessTokenEncryptionSecretFactory(configService, envService) {
|
|
585
|
+
var _configService_get;
|
|
586
|
+
var encryptionSecret = (_configService_get = configService.get(ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET_ENV_KEY)) !== null && _configService_get !== void 0 ? _configService_get : '';
|
|
587
|
+
if (!nestjs.isValidAES256GCMEncryptionSecret(encryptionSecret)) {
|
|
588
|
+
if (envService.isTestingEnv) {
|
|
589
|
+
encryptionSecret = TESTING_ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET;
|
|
590
|
+
} else {
|
|
591
|
+
throw new Error("zohoAccessTokenEncryptionSecretFactory: The secret provided by ".concat(ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET_ENV_KEY, " is not valid. Expected a 64-character hexadecimal string."));
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return encryptionSecret;
|
|
595
|
+
}
|
|
596
|
+
// MARK: Converter Map Entry
|
|
597
|
+
/**
|
|
598
|
+
* Builds the converter map entry for the Zoho access token cache, for use in a SERVER-ONLY
|
|
599
|
+
* SystemState converter map.
|
|
600
|
+
*
|
|
601
|
+
* @param config - The encryption configuration.
|
|
602
|
+
* @returns A partial converter map containing only the Zoho access token entry.
|
|
603
|
+
*
|
|
604
|
+
* @example
|
|
605
|
+
* ```typescript
|
|
606
|
+
* const collections = systemStatePrivateFirestoreCollection({
|
|
607
|
+
* firestoreContext,
|
|
608
|
+
* converters: {
|
|
609
|
+
* ...zohoAccessTokenSystemStatePrivateConverterEntry({ encryptionSecret })
|
|
610
|
+
* }
|
|
611
|
+
* });
|
|
612
|
+
* ```
|
|
613
|
+
*/ function zohoAccessTokenSystemStatePrivateConverterEntry(config) {
|
|
614
|
+
return _define_property$4({}, ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE, zohoAccessTokenSystemStateDataConverterFactory(config));
|
|
615
|
+
}
|
|
616
|
+
|
|
459
617
|
function _type_of$3(obj) {
|
|
460
618
|
"@swc/helpers - typeof";
|
|
461
619
|
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
@@ -1114,7 +1272,7 @@ exports.ZohoUserExternalConnectionOAuthService = __decorate([
|
|
|
1114
1272
|
__param(1, common.Inject(model.UserExternalConnectionStateCoder)),
|
|
1115
1273
|
__param(2, common.Inject(model.UserExternalConnectionServerActions)),
|
|
1116
1274
|
__param(3, common.Inject(model.UserExternalConnectionAccessor)),
|
|
1117
|
-
__param(4, common.Inject(nestjs.ZohoAccountsOAuthApi))
|
|
1275
|
+
__param(4, common.Inject(nestjs$1.ZohoAccountsOAuthApi))
|
|
1118
1276
|
], exports.ZohoUserExternalConnectionOAuthService);
|
|
1119
1277
|
|
|
1120
1278
|
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
@@ -1626,6 +1784,8 @@ function _unsupported_iterable_to_array(o, minLen) {
|
|
|
1626
1784
|
}
|
|
1627
1785
|
|
|
1628
1786
|
exports.DEFAULT_ZOHO_OAUTH_SCOPES = DEFAULT_ZOHO_OAUTH_SCOPES;
|
|
1787
|
+
exports.TESTING_ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET = TESTING_ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET;
|
|
1788
|
+
exports.ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET_ENV_KEY = ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET_ENV_KEY;
|
|
1629
1789
|
exports.ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE = ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE;
|
|
1630
1790
|
exports.ZOHO_EXTRA_ACCOUNTS_SERVER_KEY = ZOHO_EXTRA_ACCOUNTS_SERVER_KEY;
|
|
1631
1791
|
exports.ZOHO_EXTRA_API_DOMAIN_KEY = ZOHO_EXTRA_API_DOMAIN_KEY;
|
|
@@ -1639,8 +1799,12 @@ exports.appZohoUserExternalConnectionOAuthModuleMetadata = appZohoUserExternalCo
|
|
|
1639
1799
|
exports.firebaseZohoAccountsAccessTokenCacheService = firebaseZohoAccountsAccessTokenCacheService;
|
|
1640
1800
|
exports.loadZohoAccessTokenSystemState = loadZohoAccessTokenSystemState;
|
|
1641
1801
|
exports.userExternalConnectionZohoAccessTokenCache = userExternalConnectionZohoAccessTokenCache;
|
|
1802
|
+
exports.zohoAccessTokenEncryptionSecretFactory = zohoAccessTokenEncryptionSecretFactory;
|
|
1642
1803
|
exports.zohoAccessTokenFromUserExternalConnectionCredentials = zohoAccessTokenFromUserExternalConnectionCredentials;
|
|
1643
1804
|
exports.zohoAccessTokenSystemStateDataConverter = zohoAccessTokenSystemStateDataConverter;
|
|
1805
|
+
exports.zohoAccessTokenSystemStateDataConverterFactory = zohoAccessTokenSystemStateDataConverterFactory;
|
|
1644
1806
|
exports.zohoAccessTokenSystemStateEmbeddedTokenConverter = zohoAccessTokenSystemStateEmbeddedTokenConverter;
|
|
1807
|
+
exports.zohoAccessTokenSystemStateEmbeddedTokenConverterFactory = zohoAccessTokenSystemStateEmbeddedTokenConverterFactory;
|
|
1808
|
+
exports.zohoAccessTokenSystemStatePrivateConverterEntry = zohoAccessTokenSystemStatePrivateConverterEntry;
|
|
1645
1809
|
exports.zohoUserExternalConnectionCredentials = zohoUserExternalConnectionCredentials;
|
|
1646
1810
|
exports.zohoUserExternalConnectionOAuthServiceConfigFactory = zohoUserExternalConnectionOAuthServiceConfigFactory;
|
package/zoho/index.esm.js
CHANGED
|
@@ -1,55 +1,112 @@
|
|
|
1
1
|
import { firestoreSubObject, firestoreDate, firestoreNumber, firestoreString, firestoreObjectArray, ZOHO_USER_EXTERNAL_CONNECTION_PROVIDER_TYPE } from '@dereekb/firebase';
|
|
2
|
+
import { firestoreEncryptedField, FirebaseServerEnvService } from '@dereekb/firebase-server';
|
|
2
3
|
import { isPast, filterUniqueFunction, MS_IN_SECOND } from '@dereekb/util';
|
|
4
|
+
import { isValidAES256GCMEncryptionSecret } from '@dereekb/nestjs';
|
|
3
5
|
import { ZOHO_ACCOUNTS_PROFILE_READ_SCOPE, zohoAccountsConfigApiUrl, zohoAccountsAuthorizeUrlFactory, zohoOAuthScopesFromScopeString, zohoAccountsApiUrlKeyForApiUrl, ZOHO_ACCOUNTS_API_URLS, ZOHO_OAUTH_SCOPE_DELIMITER } from '@dereekb/zoho';
|
|
4
6
|
import { userExternalConnectionOAuthControllerPath, userExternalConnectionOAuthRoutesForGlobalRouteExclude, userExternalConnectionOAuthServiceConfigFactory, UserExternalConnectionOAuthServiceConfig, UserExternalConnectionStateCoder, UserExternalConnectionServerActions, UserExternalConnectionAccessor, AbstractUserExternalConnectionOAuthService, mergeRefreshedUserExternalConnectionCredentials, AbstractUserExternalConnectionOAuthController } from '@dereekb/firebase-server/model';
|
|
5
7
|
import { safeToJsDate } from '@dereekb/date';
|
|
6
8
|
import { Injectable, Inject, Controller } from '@nestjs/common';
|
|
7
9
|
import { ZohoAccountsOAuthApi } from '@dereekb/zoho/nestjs';
|
|
8
10
|
import { ConfigModule } from '@nestjs/config';
|
|
9
|
-
import { FirebaseServerEnvService } from '@dereekb/firebase-server';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* {@link SystemState} type identifier for storing Zoho access tokens in Firestore.
|
|
13
14
|
*/ var ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE = 'zoho_access_token';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Creates the embedded-token converter, encrypting the `accessToken` at rest.
|
|
17
|
+
*
|
|
18
|
+
* This is a factory rather than a module-level const because `firestoreEncryptedField` resolves and
|
|
19
|
+
* validates the encryption key eagerly at construction — the secret must be known at runtime.
|
|
20
|
+
*
|
|
21
|
+
* ONLY `accessToken` is encrypted, deliberately. `firestoreEncryptedField` round-trips through
|
|
22
|
+
* `JSON.stringify`/`JSON.parse`, so anything placed inside the encrypted blob loses its type — and
|
|
23
|
+
* `expiresAt` is a `Date`. Encrypting the token string alone keeps every date outside the blob,
|
|
24
|
+
* which is what lets {@link zohoAccessTokenSystemStateDataConverterFactory} keep filtering expired
|
|
25
|
+
* entries without having to decrypt them first. Do not "improve" this by encrypting the whole
|
|
26
|
+
* token object or the `tokens` array.
|
|
27
|
+
*
|
|
28
|
+
* Accepted trade-off: `key`, `scope`, `apiDomain`, `expiresIn` and `expiresAt` remain plaintext at
|
|
29
|
+
* rest. None of them is a credential.
|
|
30
|
+
*
|
|
31
|
+
* @param config - The encryption configuration.
|
|
32
|
+
* @returns The embedded token field converter.
|
|
33
|
+
*/ function zohoAccessTokenSystemStateEmbeddedTokenConverterFactory(config) {
|
|
34
|
+
return firestoreSubObject({
|
|
35
|
+
objectField: {
|
|
36
|
+
fields: {
|
|
37
|
+
key: firestoreString(),
|
|
38
|
+
accessToken: firestoreEncryptedField({
|
|
39
|
+
secret: config.encryptionSecret,
|
|
40
|
+
default: '',
|
|
41
|
+
// This is a cache of ~1h tokens, so an undecryptable entry is a cache MISS, not an error.
|
|
42
|
+
// The empty sentinel is dropped by the `tokens` filter, and the next Zoho call re-mints.
|
|
43
|
+
// This is also what makes a rotated secret survivable here (unlike uecp/jwks).
|
|
44
|
+
onDecodeFailure: function onDecodeFailure() {
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
}),
|
|
48
|
+
scope: firestoreString(),
|
|
49
|
+
apiDomain: firestoreString(),
|
|
50
|
+
expiresIn: firestoreNumber({
|
|
51
|
+
default: 3600
|
|
52
|
+
}),
|
|
53
|
+
expiresAt: firestoreDate()
|
|
54
|
+
}
|
|
25
55
|
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
28
58
|
/**
|
|
29
59
|
* Firestore field converter for {@link ZohoAccessTokenSystemStateData}.
|
|
30
60
|
*
|
|
31
61
|
* Automatically filters out expired tokens on read and enforces uniqueness by service key.
|
|
32
62
|
* Must be registered in the app's {@link SystemStateStoredDataConverterMap} under
|
|
33
63
|
* the {@link ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE} key.
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
64
|
+
*/ /**
|
|
65
|
+
* Builds the {@link ZohoAccessTokenSystemStateData} converter around a given embedded-token converter.
|
|
66
|
+
*
|
|
67
|
+
* Shared by the encrypted factory and the deprecated plaintext const so the array's expiry filter and
|
|
68
|
+
* per-key dedup behavior can only ever be defined once.
|
|
69
|
+
*
|
|
70
|
+
* @param embeddedTokenConverter - The converter for each entry in the `tokens` array.
|
|
71
|
+
* @returns The stored-data field converter.
|
|
72
|
+
*/ function zohoAccessTokenSystemStateDataConverterForEmbeddedTokenConverter(embeddedTokenConverter) {
|
|
73
|
+
return firestoreSubObject({
|
|
74
|
+
objectField: {
|
|
75
|
+
fields: {
|
|
76
|
+
tokens: firestoreObjectArray({
|
|
77
|
+
firestoreField: embeddedTokenConverter,
|
|
78
|
+
filterUnique: filterUniqueFunction(function(x) {
|
|
79
|
+
return x.key;
|
|
80
|
+
}),
|
|
81
|
+
// `firestoreObjectArray` maps BEFORE it filters, so this runs on already-decoded entries.
|
|
82
|
+
// The `accessToken` check is what drops an entry whose decryption failed (onDecodeFailure
|
|
83
|
+
// leaves an empty string behind) — without it such an entry would surface as a token with
|
|
84
|
+
// an empty secret rather than as a cache miss.
|
|
85
|
+
filter: function filter(x) {
|
|
86
|
+
return Boolean(x === null || x === void 0 ? void 0 : x.accessToken) && ((x === null || x === void 0 ? void 0 : x.expiresAt) ? !isPast(x.expiresAt) : true // filter out empty/expired values
|
|
87
|
+
);
|
|
88
|
+
}
|
|
41
89
|
}),
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}),
|
|
47
|
-
lat: firestoreDate({
|
|
48
|
-
saveDefaultAsNow: true
|
|
49
|
-
})
|
|
90
|
+
lat: firestoreDate({
|
|
91
|
+
saveDefaultAsNow: true
|
|
92
|
+
})
|
|
93
|
+
}
|
|
50
94
|
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Creates the {@link ZohoAccessTokenSystemStateData} converter, encrypting each token's
|
|
99
|
+
* `accessToken` at rest.
|
|
100
|
+
*
|
|
101
|
+
* Register the result in a SERVER-ONLY converter map under {@link ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE} —
|
|
102
|
+
* see `systemStatePrivateFirestoreCollection()` in `@dereekb/firebase-server/model`. It must never be
|
|
103
|
+
* registered in an app's client-shared `SystemStateStoredDataConverterMap`.
|
|
104
|
+
*
|
|
105
|
+
* @param config - The encryption configuration.
|
|
106
|
+
* @returns The stored-data field converter.
|
|
107
|
+
*/ function zohoAccessTokenSystemStateDataConverterFactory(config) {
|
|
108
|
+
return zohoAccessTokenSystemStateDataConverterForEmbeddedTokenConverter(zohoAccessTokenSystemStateEmbeddedTokenConverterFactory(config));
|
|
109
|
+
}
|
|
53
110
|
/**
|
|
54
111
|
* Loads the {@link SystemStateDocument} that stores {@link ZohoAccessTokenSystemStateData},
|
|
55
112
|
* using {@link ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE} as the document ID.
|
|
@@ -65,6 +122,28 @@ var zohoAccessTokenSystemStateEmbeddedTokenConverter = firestoreSubObject({
|
|
|
65
122
|
*/ function loadZohoAccessTokenSystemState(accessor) {
|
|
66
123
|
return accessor.loadDocumentForId(ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE);
|
|
67
124
|
}
|
|
125
|
+
// COMPAT: Deprecated aliases
|
|
126
|
+
/**
|
|
127
|
+
* @deprecated stores the access token in PLAINTEXT. Use
|
|
128
|
+
* {@link zohoAccessTokenSystemStateEmbeddedTokenConverterFactory} instead, which encrypts it at rest.
|
|
129
|
+
*/ var zohoAccessTokenSystemStateEmbeddedTokenConverter = firestoreSubObject({
|
|
130
|
+
objectField: {
|
|
131
|
+
fields: {
|
|
132
|
+
key: firestoreString(),
|
|
133
|
+
accessToken: firestoreString(),
|
|
134
|
+
scope: firestoreString(),
|
|
135
|
+
apiDomain: firestoreString(),
|
|
136
|
+
expiresIn: firestoreNumber({
|
|
137
|
+
default: 3600
|
|
138
|
+
}),
|
|
139
|
+
expiresAt: firestoreDate()
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
/**
|
|
144
|
+
* @deprecated stores access tokens in PLAINTEXT. Use {@link zohoAccessTokenSystemStateDataConverterFactory}
|
|
145
|
+
* instead, and register it on a server-only SystemStatePrivate collection.
|
|
146
|
+
*/ var zohoAccessTokenSystemStateDataConverter = zohoAccessTokenSystemStateDataConverterForEmbeddedTokenConverter(zohoAccessTokenSystemStateEmbeddedTokenConverter);
|
|
68
147
|
|
|
69
148
|
function _array_like_to_array$1(arr, len) {
|
|
70
149
|
if (len == null || len > arr.length) len = arr.length;
|
|
@@ -103,7 +182,7 @@ function _async_to_generator$2(fn) {
|
|
|
103
182
|
});
|
|
104
183
|
};
|
|
105
184
|
}
|
|
106
|
-
function _define_property$
|
|
185
|
+
function _define_property$5(obj, key, value) {
|
|
107
186
|
if (key in obj) {
|
|
108
187
|
Object.defineProperty(obj, key, {
|
|
109
188
|
value: value,
|
|
@@ -132,7 +211,7 @@ function _object_spread$2(target) {
|
|
|
132
211
|
}));
|
|
133
212
|
}
|
|
134
213
|
ownKeys.forEach(function(key) {
|
|
135
|
-
_define_property$
|
|
214
|
+
_define_property$5(target, key, source[key]);
|
|
136
215
|
});
|
|
137
216
|
}
|
|
138
217
|
return target;
|
|
@@ -273,12 +352,19 @@ function _ts_generator$2(thisArg, body) {
|
|
|
273
352
|
* Tokens are stored in a single {@link SystemState} document (type {@link ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE})
|
|
274
353
|
* and token updates/clears use Firestore transactions for concurrency safety.
|
|
275
354
|
*
|
|
355
|
+
* The access token is a credential, so pass a SERVER-ONLY collection — a
|
|
356
|
+
* `systemStatePrivateFirestoreCollection()` from `@dereekb/firebase-server/model`, whose converter
|
|
357
|
+
* map registers {@link zohoAccessTokenSystemStateDataConverterFactory} and encrypts the token at
|
|
358
|
+
* rest. Passing an app's client-shared `systemStateCollection` still type-checks (for backwards
|
|
359
|
+
* compatibility) but requires the converter to be registered in the client-shared map, which drags
|
|
360
|
+
* `@dereekb/firebase-server` into browser builds.
|
|
361
|
+
*
|
|
276
362
|
* @param systemStateCollection - The Firestore collection for system state documents.
|
|
277
363
|
* @returns A cache service backed by Firestore system state documents.
|
|
278
364
|
*
|
|
279
365
|
* @example
|
|
280
366
|
* ```ts
|
|
281
|
-
* const cacheService = firebaseZohoAccountsAccessTokenCacheService(
|
|
367
|
+
* const cacheService = firebaseZohoAccountsAccessTokenCacheService(systemStatePrivateCollection);
|
|
282
368
|
* const cache = cacheService.loadZohoAccessTokenCache('my-zoho-service');
|
|
283
369
|
* const token = await cache.loadCachedToken();
|
|
284
370
|
* ```
|
|
@@ -454,6 +540,78 @@ function _ts_generator$2(thisArg, body) {
|
|
|
454
540
|
return service;
|
|
455
541
|
}
|
|
456
542
|
|
|
543
|
+
function _define_property$4(obj, key, value) {
|
|
544
|
+
if (key in obj) {
|
|
545
|
+
Object.defineProperty(obj, key, {
|
|
546
|
+
value: value,
|
|
547
|
+
enumerable: true,
|
|
548
|
+
configurable: true,
|
|
549
|
+
writable: true
|
|
550
|
+
});
|
|
551
|
+
} else {
|
|
552
|
+
obj[key] = value;
|
|
553
|
+
}
|
|
554
|
+
return obj;
|
|
555
|
+
}
|
|
556
|
+
// MARK: Environment Variable Keys
|
|
557
|
+
/**
|
|
558
|
+
* Environment variable name for the Zoho access token cache encryption secret
|
|
559
|
+
* (hex-encoded AES-256 key).
|
|
560
|
+
*
|
|
561
|
+
* There is NO key rotation — `firestoreEncryptedField` resolves and validates the key once at
|
|
562
|
+
* converter construction and closes over it. Unlike the `uecp` and `oidcJwksKey` secrets, however,
|
|
563
|
+
* rotating this one is SURVIVABLE: the Zoho converter supplies an `onDecodeFailure` handler, so
|
|
564
|
+
* every entry written under the old key simply degrades to a cache miss and the next Zoho call
|
|
565
|
+
* re-mints a token. Rotation costs one extra token request per service key, not an outage.
|
|
566
|
+
*/ var ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET_ENV_KEY = 'ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET';
|
|
567
|
+
/**
|
|
568
|
+
* Deterministic secret used when running in a testing environment and no real secret is configured,
|
|
569
|
+
* so specs never need a live credential.
|
|
570
|
+
*
|
|
571
|
+
* Deliberately distinct from the OIDC JWKS and UserExternalConnection testing secrets so a leaked
|
|
572
|
+
* emulator blob is attributable. ("Zoho Access Token Cache Test Key", hex-encoded.)
|
|
573
|
+
*/ var TESTING_ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET = '5a6f686f2041636365737320546f6b656e2043616368652054657374204b6579';
|
|
574
|
+
// MARK: Config
|
|
575
|
+
/**
|
|
576
|
+
* Reads the Zoho access token encryption secret from the environment.
|
|
577
|
+
*
|
|
578
|
+
* @param configService - The Nest config service used to read the encryption secret.
|
|
579
|
+
* @param envService - Used to detect a testing environment for the secret fallback.
|
|
580
|
+
* @returns The validated encryption secret.
|
|
581
|
+
* @throws {Error} When the configured secret is invalid outside a testing environment.
|
|
582
|
+
*/ function zohoAccessTokenEncryptionSecretFactory(configService, envService) {
|
|
583
|
+
var _configService_get;
|
|
584
|
+
var encryptionSecret = (_configService_get = configService.get(ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET_ENV_KEY)) !== null && _configService_get !== void 0 ? _configService_get : '';
|
|
585
|
+
if (!isValidAES256GCMEncryptionSecret(encryptionSecret)) {
|
|
586
|
+
if (envService.isTestingEnv) {
|
|
587
|
+
encryptionSecret = TESTING_ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET;
|
|
588
|
+
} else {
|
|
589
|
+
throw new Error("zohoAccessTokenEncryptionSecretFactory: The secret provided by ".concat(ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET_ENV_KEY, " is not valid. Expected a 64-character hexadecimal string."));
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return encryptionSecret;
|
|
593
|
+
}
|
|
594
|
+
// MARK: Converter Map Entry
|
|
595
|
+
/**
|
|
596
|
+
* Builds the converter map entry for the Zoho access token cache, for use in a SERVER-ONLY
|
|
597
|
+
* SystemState converter map.
|
|
598
|
+
*
|
|
599
|
+
* @param config - The encryption configuration.
|
|
600
|
+
* @returns A partial converter map containing only the Zoho access token entry.
|
|
601
|
+
*
|
|
602
|
+
* @example
|
|
603
|
+
* ```typescript
|
|
604
|
+
* const collections = systemStatePrivateFirestoreCollection({
|
|
605
|
+
* firestoreContext,
|
|
606
|
+
* converters: {
|
|
607
|
+
* ...zohoAccessTokenSystemStatePrivateConverterEntry({ encryptionSecret })
|
|
608
|
+
* }
|
|
609
|
+
* });
|
|
610
|
+
* ```
|
|
611
|
+
*/ function zohoAccessTokenSystemStatePrivateConverterEntry(config) {
|
|
612
|
+
return _define_property$4({}, ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE, zohoAccessTokenSystemStateDataConverterFactory(config));
|
|
613
|
+
}
|
|
614
|
+
|
|
457
615
|
function _type_of$3(obj) {
|
|
458
616
|
"@swc/helpers - typeof";
|
|
459
617
|
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
@@ -1623,4 +1781,4 @@ function _unsupported_iterable_to_array(o, minLen) {
|
|
|
1623
1781
|
};
|
|
1624
1782
|
}
|
|
1625
1783
|
|
|
1626
|
-
export { DEFAULT_ZOHO_OAUTH_SCOPES, ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE, ZOHO_EXTRA_ACCOUNTS_SERVER_KEY, ZOHO_EXTRA_API_DOMAIN_KEY, ZOHO_EXTRA_LOCATION_KEY, ZOHO_OAUTH_CALLBACK_ACCOUNTS_SERVER_PARAM, ZOHO_OAUTH_CALLBACK_LOCATION_PARAM, ZOHO_USER_EXTERNAL_CONNECTION_OAUTH_CONTROLLER_PATH, ZOHO_USER_EXTERNAL_CONNECTION_OAUTH_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE, ZohoUserExternalConnectionOAuthController, ZohoUserExternalConnectionOAuthService, ZohoUserExternalConnectionOAuthServiceConfig, appZohoUserExternalConnectionOAuthModuleMetadata, firebaseZohoAccountsAccessTokenCacheService, loadZohoAccessTokenSystemState, userExternalConnectionZohoAccessTokenCache, zohoAccessTokenFromUserExternalConnectionCredentials, zohoAccessTokenSystemStateDataConverter, zohoAccessTokenSystemStateEmbeddedTokenConverter, zohoUserExternalConnectionCredentials, zohoUserExternalConnectionOAuthServiceConfigFactory };
|
|
1784
|
+
export { DEFAULT_ZOHO_OAUTH_SCOPES, TESTING_ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET, ZOHO_ACCESS_TOKEN_ENCRYPTION_SECRET_ENV_KEY, ZOHO_ACCESS_TOKEN_SYSTEM_STATE_TYPE, ZOHO_EXTRA_ACCOUNTS_SERVER_KEY, ZOHO_EXTRA_API_DOMAIN_KEY, ZOHO_EXTRA_LOCATION_KEY, ZOHO_OAUTH_CALLBACK_ACCOUNTS_SERVER_PARAM, ZOHO_OAUTH_CALLBACK_LOCATION_PARAM, ZOHO_USER_EXTERNAL_CONNECTION_OAUTH_CONTROLLER_PATH, ZOHO_USER_EXTERNAL_CONNECTION_OAUTH_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE, ZohoUserExternalConnectionOAuthController, ZohoUserExternalConnectionOAuthService, ZohoUserExternalConnectionOAuthServiceConfig, appZohoUserExternalConnectionOAuthModuleMetadata, firebaseZohoAccountsAccessTokenCacheService, loadZohoAccessTokenSystemState, userExternalConnectionZohoAccessTokenCache, zohoAccessTokenEncryptionSecretFactory, zohoAccessTokenFromUserExternalConnectionCredentials, zohoAccessTokenSystemStateDataConverter, zohoAccessTokenSystemStateDataConverterFactory, zohoAccessTokenSystemStateEmbeddedTokenConverter, zohoAccessTokenSystemStateEmbeddedTokenConverterFactory, zohoAccessTokenSystemStatePrivateConverterEntry, zohoUserExternalConnectionCredentials, zohoUserExternalConnectionOAuthServiceConfigFactory };
|
package/zoho/package.json
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server/zoho",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.34.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@dereekb/analytics": "13.
|
|
6
|
-
"@dereekb/date": "13.
|
|
7
|
-
"@dereekb/model": "13.
|
|
8
|
-
"@dereekb/nestjs": "13.
|
|
9
|
-
"@dereekb/rxjs": "13.
|
|
10
|
-
"@dereekb/firebase": "13.
|
|
11
|
-
"@dereekb/firebase-server": "13.
|
|
12
|
-
"@dereekb/util": "13.
|
|
13
|
-
"@dereekb/zoho": "13.
|
|
5
|
+
"@dereekb/analytics": "13.34.0",
|
|
6
|
+
"@dereekb/date": "13.34.0",
|
|
7
|
+
"@dereekb/model": "13.34.0",
|
|
8
|
+
"@dereekb/nestjs": "13.34.0",
|
|
9
|
+
"@dereekb/rxjs": "13.34.0",
|
|
10
|
+
"@dereekb/firebase": "13.34.0",
|
|
11
|
+
"@dereekb/firebase-server": "13.34.0",
|
|
12
|
+
"@dereekb/util": "13.34.0",
|
|
13
|
+
"@dereekb/zoho": "13.34.0",
|
|
14
14
|
"@nestjs/common": "^11.1.19",
|
|
15
|
+
"@nestjs/config": "^4.0.4",
|
|
15
16
|
"express": "^5.2.1"
|
|
16
17
|
},
|
|
17
18
|
"exports": {
|
package/zoho/src/lib/index.d.ts
CHANGED