pyric-admin 0.1.0-alpha.24 → 0.1.0-alpha.26
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/README.md +1 -1
- package/dist/admin.d.ts +64 -12
- package/dist/admin.d.ts.map +1 -1
- package/dist/admin.js +53 -8
- package/dist/admin.js.map +1 -1
- package/dist/app/index.d.ts +14 -1
- package/dist/app/index.d.ts.map +1 -1
- package/dist/app/index.js +58 -16
- package/dist/app/index.js.map +1 -1
- package/dist/app-check/index.d.ts +13 -1
- package/dist/app-check/index.d.ts.map +1 -1
- package/dist/app-check/index.js +14 -9
- package/dist/app-check/index.js.map +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +11 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/data-connect/index.d.ts +23 -1
- package/dist/data-connect/index.d.ts.map +1 -1
- package/dist/data-connect/index.js +44 -7
- package/dist/data-connect/index.js.map +1 -1
- package/dist/database/index.d.ts +24 -19
- package/dist/database/index.d.ts.map +1 -1
- package/dist/database/index.js +133 -99
- package/dist/database/index.js.map +1 -1
- package/dist/deferred.d.ts +37 -13
- package/dist/deferred.d.ts.map +1 -1
- package/dist/deferred.js +57 -15
- package/dist/deferred.js.map +1 -1
- package/dist/eventarc/index.d.ts +13 -1
- package/dist/eventarc/index.d.ts.map +1 -1
- package/dist/eventarc/index.js +15 -9
- package/dist/eventarc/index.js.map +1 -1
- package/dist/extensions/index.d.ts +13 -1
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/extensions/index.js +15 -9
- package/dist/extensions/index.js.map +1 -1
- package/dist/firestore/index.d.ts +6 -0
- package/dist/firestore/index.d.ts.map +1 -1
- package/dist/firestore/index.js +25 -3
- package/dist/firestore/index.js.map +1 -1
- package/dist/functions/index.d.ts +13 -1
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +15 -9
- package/dist/functions/index.js.map +1 -1
- package/dist/installations/index.d.ts +13 -1
- package/dist/installations/index.d.ts.map +1 -1
- package/dist/installations/index.js +15 -9
- package/dist/installations/index.js.map +1 -1
- package/dist/instance-id/index.d.ts +13 -1
- package/dist/instance-id/index.d.ts.map +1 -1
- package/dist/instance-id/index.js +15 -9
- package/dist/instance-id/index.js.map +1 -1
- package/dist/machine-learning/index.d.ts +19 -1
- package/dist/machine-learning/index.d.ts.map +1 -1
- package/dist/machine-learning/index.js +23 -9
- package/dist/machine-learning/index.js.map +1 -1
- package/dist/messaging/index.d.ts.map +1 -1
- package/dist/messaging/index.js +1 -0
- package/dist/messaging/index.js.map +1 -1
- package/dist/phone-number-verification/index.d.ts +12 -1
- package/dist/phone-number-verification/index.d.ts.map +1 -1
- package/dist/phone-number-verification/index.js +14 -9
- package/dist/phone-number-verification/index.js.map +1 -1
- package/dist/project-management/index.d.ts +21 -1
- package/dist/project-management/index.d.ts.map +1 -1
- package/dist/project-management/index.js +23 -9
- package/dist/project-management/index.js.map +1 -1
- package/dist/remote-config/index.d.ts +21 -1
- package/dist/remote-config/index.d.ts.map +1 -1
- package/dist/remote-config/index.js +24 -9
- package/dist/remote-config/index.js.map +1 -1
- package/dist/security-rules/index.d.ts +23 -1
- package/dist/security-rules/index.d.ts.map +1 -1
- package/dist/security-rules/index.js +27 -9
- package/dist/security-rules/index.js.map +1 -1
- package/dist/storage/deferred-members.d.ts +14 -0
- package/dist/storage/deferred-members.d.ts.map +1 -0
- package/dist/storage/deferred-members.js +55 -0
- package/dist/storage/deferred-members.js.map +1 -0
- package/dist/storage/delete-files.d.ts +13 -0
- package/dist/storage/delete-files.d.ts.map +1 -0
- package/dist/storage/delete-files.js +39 -0
- package/dist/storage/delete-files.js.map +1 -0
- package/dist/storage/file-query.d.ts +52 -0
- package/dist/storage/file-query.d.ts.map +1 -0
- package/dist/storage/file-query.js +94 -0
- package/dist/storage/file-query.js.map +1 -0
- package/dist/storage/get-files.d.ts +15 -0
- package/dist/storage/get-files.d.ts.map +1 -0
- package/dist/storage/get-files.js +33 -0
- package/dist/storage/get-files.js.map +1 -0
- package/dist/storage/index.d.ts +77 -13
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +359 -160
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/remote-names.d.ts +19 -0
- package/dist/storage/remote-names.d.ts.map +1 -0
- package/dist/storage/remote-names.js +26 -0
- package/dist/storage/remote-names.js.map +1 -0
- package/package.json +2 -2
- package/src/admin.ts +58 -8
- package/src/app/index.ts +71 -19
- package/src/app-check/index.ts +27 -9
- package/src/auth/auth.test.ts +27 -0
- package/src/auth/index.ts +8 -0
- package/src/data-connect/index.ts +70 -9
- package/src/database/database.test.ts +1 -0
- package/src/database/index.ts +182 -108
- package/src/deferred.ts +78 -15
- package/src/eventarc/index.ts +32 -9
- package/src/extensions/index.ts +32 -9
- package/src/firestore/index.ts +30 -3
- package/src/functions/index.ts +32 -9
- package/src/installations/index.ts +33 -10
- package/src/instance-id/index.ts +33 -9
- package/src/machine-learning/index.ts +46 -9
- package/src/messaging/index.ts +1 -0
- package/src/phone-number-verification/index.ts +31 -9
- package/src/project-management/index.ts +48 -8
- package/src/remote-config/index.ts +51 -8
- package/src/security-rules/index.ts +54 -9
- package/src/storage/deferred-members.ts +67 -0
- package/src/storage/delete-files.ts +40 -0
- package/src/storage/file-query.ts +135 -0
- package/src/storage/get-files.ts +40 -0
- package/src/storage/index.ts +484 -180
- package/src/storage/remote-names.ts +34 -0
- package/src/storage/storage.test.ts +78 -5
|
@@ -1,11 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `pyric-admin/data-connect`:
|
|
3
|
-
*
|
|
4
|
-
* The sandbox does not model Data Connect. Every value `firebase-admin/data-connect`
|
|
5
|
-
* exports is present so imports link; using one throws `PyricDeferredApiError`.
|
|
6
|
-
* See `../deferred.ts`.
|
|
2
|
+
* `pyric-admin/data-connect`: deferred service handle for `firebase-admin/data-connect`.
|
|
7
3
|
*/
|
|
8
|
-
import {
|
|
4
|
+
import { defineDeferredMembers } from 'pyric/app/internal';
|
|
5
|
+
import { DEFERRED_SERVICE_TOKEN, deferredAdminEntry, guardDeferredServiceApp, resolveActiveAdminApp, } from '../deferred.js';
|
|
9
6
|
export { PyricDeferredApiError } from '../deferred.js';
|
|
10
|
-
export
|
|
7
|
+
export class DataConnect {
|
|
8
|
+
#app;
|
|
9
|
+
connectorConfig;
|
|
10
|
+
/** @internal */
|
|
11
|
+
constructor(...args) {
|
|
12
|
+
this.#app = guardDeferredServiceApp('data-connect', 'DataConnect', args);
|
|
13
|
+
this.connectorConfig = args[2];
|
|
14
|
+
}
|
|
15
|
+
get app() {
|
|
16
|
+
return this.#app;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
defineDeferredMembers(DataConnect.prototype, 'pyric-admin/data-connect', 'DataConnect', {
|
|
20
|
+
async: [
|
|
21
|
+
'executeGraphql',
|
|
22
|
+
'executeGraphqlRead',
|
|
23
|
+
'executeMutation',
|
|
24
|
+
'executeQuery',
|
|
25
|
+
'insert',
|
|
26
|
+
'insertMany',
|
|
27
|
+
'upsert',
|
|
28
|
+
'upsertMany',
|
|
29
|
+
],
|
|
30
|
+
});
|
|
31
|
+
const instancesByApp = new WeakMap();
|
|
32
|
+
export function getDataConnect(connectorConfig, app) {
|
|
33
|
+
const resolved = resolveActiveAdminApp(app);
|
|
34
|
+
let byId = instancesByApp.get(resolved);
|
|
35
|
+
if (!byId) {
|
|
36
|
+
byId = new Map();
|
|
37
|
+
instancesByApp.set(resolved, byId);
|
|
38
|
+
}
|
|
39
|
+
const id = `${connectorConfig.location}-${connectorConfig.serviceId}`;
|
|
40
|
+
const existing = byId.get(id);
|
|
41
|
+
if (existing !== undefined)
|
|
42
|
+
return existing;
|
|
43
|
+
const created = new DataConnect(DEFERRED_SERVICE_TOKEN, resolved, connectorConfig);
|
|
44
|
+
byId.set(id, created);
|
|
45
|
+
return created;
|
|
46
|
+
}
|
|
47
|
+
export const { validateAdminArgs } = deferredAdminEntry('data-connect');
|
|
11
48
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/data-connect/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/data-connect/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAQvD,MAAM,OAAO,WAAW;IACb,IAAI,CAAgB;IACpB,eAAe,CAAkB;IAE1C,gBAAgB;IAChB,YAAY,GAAG,IAAe;QAC5B,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC,cAAc,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;QACzE,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAoB,CAAC;IACpD,CAAC;IAED,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;CAUF;AAED,qBAAqB,CAAC,WAAW,CAAC,SAAS,EAAE,0BAA0B,EAAE,aAAa,EAAE;IACtF,KAAK,EAAE;QACL,gBAAgB;QAChB,oBAAoB;QACpB,iBAAiB;QACjB,cAAc;QACd,QAAQ;QACR,YAAY;QACZ,QAAQ;QACR,YAAY;KACb;CACF,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,IAAI,OAAO,EAA2C,CAAC;AAE9E,MAAM,UAAU,cAAc,CAAC,eAAgC,EAAE,GAAmB;IAClF,MAAM,QAAQ,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;QACjB,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,EAAE,GAAG,GAAG,eAAe,CAAC,QAAQ,IAAI,eAAe,CAAC,SAAS,EAAE,CAAC;IACtE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC9B,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,sBAAsB,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;IACnF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACtB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,kBAAkB,CAAC,cAAc,CAAC,CAAC"}
|
package/dist/database/index.d.ts
CHANGED
|
@@ -40,11 +40,12 @@
|
|
|
40
40
|
* backing rule state to expose.
|
|
41
41
|
*
|
|
42
42
|
* Sandbox state lives on the underlying `Sandbox` via a `WeakMap`
|
|
43
|
-
* keyed by the `Sandbox` instance
|
|
44
|
-
* the
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
43
|
+
* keyed by the `Sandbox` instance, with one tree per RTDB instance
|
|
44
|
+
* (the instance a database URL names). `sandbox.reset()` wipes every
|
|
45
|
+
* tree via the sandbox's `session_boundary` event.
|
|
46
|
+
* Successive `getDatabase(app)` calls for the same sandbox and instance
|
|
47
|
+
* return the same handle (matches firebase-admin's one database per
|
|
48
|
+
* app and URL).
|
|
48
49
|
*
|
|
49
50
|
* - **Remote sandbox arm** (sandbox target whose `Sandbox` carries the
|
|
50
51
|
* `pyric/sandbox` remote brand — a Node-side handle onto the
|
|
@@ -52,7 +53,9 @@
|
|
|
52
53
|
* `connectRemoteSandbox()`) — every `Reference` data operation routes
|
|
53
54
|
* through the handle's worker-relay channel (`rtdb.get/set/update/
|
|
54
55
|
* remove/push` ops with `actAs: { mode: 'admin' }` pinned — firebase-
|
|
55
|
-
* admin's rules-bypass semantics
|
|
56
|
+
* admin's rules-bypass semantics, and the instance the database
|
|
57
|
+
* URL names on every op and value subscription), NOT into the
|
|
58
|
+
* process-local tree:
|
|
56
59
|
* a local tree on a remote handle would be private server-side data
|
|
57
60
|
* the browser never sees. Differences from the local arm, both
|
|
58
61
|
* deliberate upgrades:
|
|
@@ -71,6 +74,9 @@
|
|
|
71
74
|
* sends it with the `rtdb.push` op (the worker-protocol contract).
|
|
72
75
|
*/
|
|
73
76
|
import { type PyricAdminApp } from '../app/index.js';
|
|
77
|
+
/** `firebase-admin/database`'s `enableLogging`. The sandbox does not model
|
|
78
|
+
* the client logger, so it fails with an error naming it. */
|
|
79
|
+
export declare const enableLogging: import("pyric/app/internal").DeferredApi;
|
|
74
80
|
/** Mirror of `firebase-admin/database`'s `ServerValue` constant and sentinel constructors. */
|
|
75
81
|
export declare const ServerValue: {
|
|
76
82
|
readonly TIMESTAMP: object;
|
|
@@ -131,27 +137,26 @@ type AdminDatabase = Database;
|
|
|
131
137
|
*
|
|
132
138
|
* Signature mirrors `firebase-admin/database`'s `getDatabase(app?)`.
|
|
133
139
|
*
|
|
134
|
-
* - `getDatabase()
|
|
140
|
+
* - `getDatabase()`: default database for the DEFAULT app (resolved
|
|
135
141
|
* through `pyric-admin/app`'s registry, exactly like firebase-admin's
|
|
136
142
|
* no-arg `getDatabase()`; throws `app/no-app` when no default app has
|
|
137
143
|
* been initialized). Works for local and remote sandbox apps.
|
|
138
|
-
* - `getDatabase(app)
|
|
139
|
-
*
|
|
140
|
-
*
|
|
144
|
+
* - `getDatabase(app)`: the database the app's `databaseURL` option names,
|
|
145
|
+
* else the default instance.
|
|
146
|
+
* - `getDatabase(app, url)`: the database `url` names, as
|
|
147
|
+
* {@link getDatabaseWithUrl} selects it.
|
|
141
148
|
*
|
|
142
|
-
*
|
|
143
|
-
* per-`Sandbox` state described in the module-level
|
|
149
|
+
* Each RTDB instance has its own data: one `Database` per sandbox and
|
|
150
|
+
* instance, backed by the per-`Sandbox` state described in the module-level
|
|
151
|
+
* docs. A URL that does not parse as a database URL throws the SDK's error.
|
|
144
152
|
*/
|
|
145
|
-
export declare function getDatabase(app?: PyricAdminApp,
|
|
153
|
+
export declare function getDatabase(app?: PyricAdminApp, url?: string): AdminDatabase;
|
|
146
154
|
/**
|
|
147
|
-
* Returns the {@link AdminDatabase} service
|
|
148
|
-
* database URL.
|
|
155
|
+
* Returns the {@link AdminDatabase} service for the RTDB instance `url` names.
|
|
149
156
|
*
|
|
150
157
|
* This is the exact `firebase-admin/database` argument order used by the
|
|
151
|
-
* Firebase Functions SDK: `getDatabaseWithUrl(url, app?)`.
|
|
152
|
-
* Functions slice has one shared RTDB instance, so the URL selects that
|
|
153
|
-
* instance rather than creating a second sandbox database.
|
|
158
|
+
* Firebase Functions SDK: `getDatabaseWithUrl(url, app?)`.
|
|
154
159
|
*/
|
|
155
|
-
export declare function getDatabaseWithUrl(
|
|
160
|
+
export declare function getDatabaseWithUrl(url: string, app?: PyricAdminApp): AdminDatabase;
|
|
156
161
|
export {};
|
|
157
162
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/database/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/database/index.ts"],"names":[],"mappings":"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"}
|
package/dist/database/index.js
CHANGED
|
@@ -40,11 +40,12 @@
|
|
|
40
40
|
* backing rule state to expose.
|
|
41
41
|
*
|
|
42
42
|
* Sandbox state lives on the underlying `Sandbox` via a `WeakMap`
|
|
43
|
-
* keyed by the `Sandbox` instance
|
|
44
|
-
* the
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
43
|
+
* keyed by the `Sandbox` instance, with one tree per RTDB instance
|
|
44
|
+
* (the instance a database URL names). `sandbox.reset()` wipes every
|
|
45
|
+
* tree via the sandbox's `session_boundary` event.
|
|
46
|
+
* Successive `getDatabase(app)` calls for the same sandbox and instance
|
|
47
|
+
* return the same handle (matches firebase-admin's one database per
|
|
48
|
+
* app and URL).
|
|
48
49
|
*
|
|
49
50
|
* - **Remote sandbox arm** (sandbox target whose `Sandbox` carries the
|
|
50
51
|
* `pyric/sandbox` remote brand — a Node-side handle onto the
|
|
@@ -52,7 +53,9 @@
|
|
|
52
53
|
* `connectRemoteSandbox()`) — every `Reference` data operation routes
|
|
53
54
|
* through the handle's worker-relay channel (`rtdb.get/set/update/
|
|
54
55
|
* remove/push` ops with `actAs: { mode: 'admin' }` pinned — firebase-
|
|
55
|
-
* admin's rules-bypass semantics
|
|
56
|
+
* admin's rules-bypass semantics, and the instance the database
|
|
57
|
+
* URL names on every op and value subscription), NOT into the
|
|
58
|
+
* process-local tree:
|
|
56
59
|
* a local tree on a remote handle would be private server-side data
|
|
57
60
|
* the browser never sees. Differences from the local arm, both
|
|
58
61
|
* deliberate upgrades:
|
|
@@ -71,9 +74,15 @@
|
|
|
71
74
|
* sends it with the `rtdb.push` op (the worker-protocol contract).
|
|
72
75
|
*/
|
|
73
76
|
import { isRemoteSandbox, } from 'pyric/sandbox';
|
|
77
|
+
import { createDatabaseInstanceRegistry, isCustomDatabaseHost, isDefaultDatabaseInstance, parseDatabaseLocationUrl, parseDatabaseUrl, } from 'pyric/sandbox/internal';
|
|
74
78
|
import { ADMIN_APP_TARGET, getApp, } from '../app/index.js';
|
|
75
79
|
import { assertAdminAppActive } from '../app/lifecycle.js';
|
|
76
80
|
import { increment, serverTimestamp } from 'pyric/database';
|
|
81
|
+
import { valueAtChildPath, valueChildren } from 'pyric/database/internal';
|
|
82
|
+
import { deferredExport } from 'pyric/app/internal';
|
|
83
|
+
/** `firebase-admin/database`'s `enableLogging`. The sandbox does not model
|
|
84
|
+
* the client logger, so it fails with an error naming it. */
|
|
85
|
+
export const enableLogging = deferredExport('pyric-admin/database', 'enableLogging');
|
|
77
86
|
/** Mirror of `firebase-admin/database`'s `ServerValue` constant and sentinel constructors. */
|
|
78
87
|
export const ServerValue = {
|
|
79
88
|
TIMESTAMP: serverTimestamp(),
|
|
@@ -84,18 +93,20 @@ export const ServerValue = {
|
|
|
84
93
|
*
|
|
85
94
|
* Signature mirrors `firebase-admin/database`'s `getDatabase(app?)`.
|
|
86
95
|
*
|
|
87
|
-
* - `getDatabase()
|
|
96
|
+
* - `getDatabase()`: default database for the DEFAULT app (resolved
|
|
88
97
|
* through `pyric-admin/app`'s registry, exactly like firebase-admin's
|
|
89
98
|
* no-arg `getDatabase()`; throws `app/no-app` when no default app has
|
|
90
99
|
* been initialized). Works for local and remote sandbox apps.
|
|
91
|
-
* - `getDatabase(app)
|
|
92
|
-
*
|
|
93
|
-
*
|
|
100
|
+
* - `getDatabase(app)`: the database the app's `databaseURL` option names,
|
|
101
|
+
* else the default instance.
|
|
102
|
+
* - `getDatabase(app, url)`: the database `url` names, as
|
|
103
|
+
* {@link getDatabaseWithUrl} selects it.
|
|
94
104
|
*
|
|
95
|
-
*
|
|
96
|
-
* per-`Sandbox` state described in the module-level
|
|
105
|
+
* Each RTDB instance has its own data: one `Database` per sandbox and
|
|
106
|
+
* instance, backed by the per-`Sandbox` state described in the module-level
|
|
107
|
+
* docs. A URL that does not parse as a database URL throws the SDK's error.
|
|
97
108
|
*/
|
|
98
|
-
export function getDatabase(app,
|
|
109
|
+
export function getDatabase(app, url) {
|
|
99
110
|
if (app === undefined) {
|
|
100
111
|
// No-arg mirror of firebase-admin's `getDatabase()` — resolve the
|
|
101
112
|
// '[DEFAULT]' app from the registry (throws app/no-app on a miss).
|
|
@@ -103,59 +114,86 @@ export function getDatabase(app, _url) {
|
|
|
103
114
|
}
|
|
104
115
|
assertAdminAppActive(app);
|
|
105
116
|
if (app[ADMIN_APP_TARGET] === 'sandbox') {
|
|
106
|
-
return getSandboxDatabase(app.sandbox);
|
|
117
|
+
return getSandboxDatabase(app.sandbox, adminDatabaseInstance(app, url));
|
|
107
118
|
}
|
|
108
119
|
throw new TypeError('pyric-admin/database: getDatabase expected a PyricAdminApp ' +
|
|
109
120
|
'(initialize via `initializeApp` from pyric-admin/app).');
|
|
110
121
|
}
|
|
111
122
|
/**
|
|
112
|
-
* Returns the {@link AdminDatabase} service
|
|
113
|
-
* database URL.
|
|
123
|
+
* Returns the {@link AdminDatabase} service for the RTDB instance `url` names.
|
|
114
124
|
*
|
|
115
125
|
* This is the exact `firebase-admin/database` argument order used by the
|
|
116
|
-
* Firebase Functions SDK: `getDatabaseWithUrl(url, app?)`.
|
|
117
|
-
* Functions slice has one shared RTDB instance, so the URL selects that
|
|
118
|
-
* instance rather than creating a second sandbox database.
|
|
126
|
+
* Firebase Functions SDK: `getDatabaseWithUrl(url, app?)`.
|
|
119
127
|
*/
|
|
120
|
-
export function getDatabaseWithUrl(
|
|
121
|
-
|
|
128
|
+
export function getDatabaseWithUrl(url, app) {
|
|
129
|
+
if (typeof url !== 'string' || url === '') {
|
|
130
|
+
throw invalidDatabaseUrl();
|
|
131
|
+
}
|
|
132
|
+
return getDatabase(app, url);
|
|
133
|
+
}
|
|
134
|
+
/** firebase-admin's error for a missing or empty database URL. */
|
|
135
|
+
function invalidDatabaseUrl() {
|
|
136
|
+
return Object.assign(new Error('Database URL must be a valid, non-empty URL string.'), { code: 'database/invalid-argument' });
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The instance an admin database targets: `url`, else the app's `databaseURL`
|
|
140
|
+
* option, parsed as the production SDK parses it. `undefined` is the default
|
|
141
|
+
* instance, which the instance named `<projectId>-default-rtdb` or, for a
|
|
142
|
+
* project created before about 2020, `<projectId>` also is when the app's
|
|
143
|
+
* options name the project.
|
|
144
|
+
*/
|
|
145
|
+
function adminDatabaseInstance(app, url) {
|
|
146
|
+
if (url === '')
|
|
147
|
+
throw invalidDatabaseUrl();
|
|
148
|
+
const source = url ?? app.options.databaseURL;
|
|
149
|
+
if (source === undefined)
|
|
150
|
+
return undefined;
|
|
151
|
+
const instance = parseDatabaseUrl(source);
|
|
152
|
+
const projectId = app.options.projectId;
|
|
153
|
+
const isProjectDefault = isDefaultDatabaseInstance(instance.name, projectId);
|
|
154
|
+
return isProjectDefault ? undefined : instance;
|
|
122
155
|
}
|
|
123
|
-
/** One
|
|
124
|
-
* the same sandbox
|
|
125
|
-
* firebase-admin
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
156
|
+
/** One registry of instances per `Sandbox`. Successive `getDatabase(app)`
|
|
157
|
+
* calls for the same sandbox and instance return the same handle, and
|
|
158
|
+
* each instance keeps its own tree, as firebase-admin keeps one database
|
|
159
|
+
* per URL. */
|
|
160
|
+
const localInstancesBySandbox = new WeakMap();
|
|
161
|
+
function localInstances(sandbox) {
|
|
162
|
+
const existing = localInstancesBySandbox.get(sandbox);
|
|
163
|
+
if (existing !== undefined)
|
|
164
|
+
return existing;
|
|
165
|
+
const registry = createDatabaseInstanceRegistry({
|
|
166
|
+
create: () => ({ state: { root: {} } }),
|
|
167
|
+
});
|
|
168
|
+
localInstancesBySandbox.set(sandbox, registry);
|
|
169
|
+
// Wire `sandbox.reset()` → wipe every instance's tree. `session_boundary`
|
|
170
|
+
// fires before the env swap, so consumer code that observes a reset sees
|
|
135
171
|
// the freshly-cleared tree on the next read. `dispose` also fires a
|
|
136
172
|
// boundary; treat it the same (the sandbox is being torn down — any
|
|
137
173
|
// in-flight handle on the tree gets an empty view).
|
|
138
174
|
sandbox.onEvent((event) => {
|
|
139
175
|
if (event.kind === 'session_boundary') {
|
|
140
|
-
|
|
176
|
+
for (const [, entry] of registry.entries())
|
|
177
|
+
entry.state.root = {};
|
|
141
178
|
}
|
|
142
179
|
});
|
|
143
|
-
return
|
|
180
|
+
return registry;
|
|
144
181
|
}
|
|
145
|
-
/** Build (or reuse) the sandbox Database handle for `sandbox
|
|
182
|
+
/** Build (or reuse) the sandbox Database handle for `sandbox` and `instance`
|
|
183
|
+
* (`undefined` is the default instance).
|
|
146
184
|
*
|
|
147
185
|
* REMOTE handles dispatch here, BEFORE any local state is touched: a
|
|
148
186
|
* remote sandbox must never get a `SandboxState` (a private local tree)
|
|
149
187
|
* or a `sandbox.onEvent` wire-up (which throws on remote handles). */
|
|
150
|
-
function getSandboxDatabase(sandbox) {
|
|
188
|
+
function getSandboxDatabase(sandbox, instance) {
|
|
151
189
|
if (isRemoteSandbox(sandbox)) {
|
|
152
|
-
return getRemoteDatabase(sandbox);
|
|
190
|
+
return getRemoteDatabase(sandbox, instance);
|
|
153
191
|
}
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return
|
|
192
|
+
const registry = localInstances(sandbox);
|
|
193
|
+
const entry = registry.getOrCreate(registry.keyOf(instance));
|
|
194
|
+
const state = entry.state;
|
|
195
|
+
entry.db ??= buildDatabaseShell(instance, (handle, path) => buildSandboxRef(state, handle, path));
|
|
196
|
+
return entry.db;
|
|
159
197
|
}
|
|
160
198
|
/**
|
|
161
199
|
* The `Database`-level shell shared by the local and remote sandbox arms —
|
|
@@ -163,17 +201,22 @@ function buildSandboxDatabase(state) {
|
|
|
163
201
|
* modeled on either arm; connection toggles are no-ops (the sandbox IS the
|
|
164
202
|
* local emulator).
|
|
165
203
|
*/
|
|
166
|
-
function buildDatabaseShell(refFactory) {
|
|
204
|
+
function buildDatabaseShell(instance, refFactory) {
|
|
167
205
|
const db = {
|
|
168
206
|
ref(path) {
|
|
169
207
|
return refFactory(db, path ?? '/');
|
|
170
208
|
},
|
|
209
|
+
/** As the SDK parses it: the URL names a location, and for a
|
|
210
|
+
* `firebaseio.com` database its host must be this database's host. */
|
|
171
211
|
refFromURL(url) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
212
|
+
const location = parseDatabaseLocationUrl(url);
|
|
213
|
+
if (instance !== undefined) {
|
|
214
|
+
const expectedHost = parseDatabaseLocationUrl(instance.url).host;
|
|
215
|
+
if (!isCustomDatabaseHost(expectedHost) && location.host !== expectedHost) {
|
|
216
|
+
throw new Error(`FIREBASE FATAL ERROR: refFromURL: Host name does not match the current database: (found ${location.host} but expected ${expectedHost}) `);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return refFactory(db, location.path);
|
|
177
220
|
},
|
|
178
221
|
// Admin-only metadata methods — not modeled in the sandbox. The
|
|
179
222
|
// sandbox is rule-bypass by construction; surfacing rule JSON would
|
|
@@ -578,6 +621,9 @@ function buildSandboxSnap(refAt, path, val) {
|
|
|
578
621
|
const segs = pathSegments(path);
|
|
579
622
|
const key = segs.length === 0 ? null : segs[segs.length - 1];
|
|
580
623
|
const exists = val !== null;
|
|
624
|
+
// An array value still has children: `val()` renders integer keys as an
|
|
625
|
+
// array, and the SDK iterates them as '0', '1', ... (#1110).
|
|
626
|
+
const children = valueChildren(val);
|
|
581
627
|
const snap = {
|
|
582
628
|
key,
|
|
583
629
|
get ref() {
|
|
@@ -591,35 +637,21 @@ function buildSandboxSnap(refAt, path, val) {
|
|
|
591
637
|
},
|
|
592
638
|
child(p) {
|
|
593
639
|
const childSegs = pathSegments(p);
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
if (cur === null || typeof cur !== 'object' || Array.isArray(cur)) {
|
|
597
|
-
cur = null;
|
|
598
|
-
break;
|
|
599
|
-
}
|
|
600
|
-
cur = cur[s] ?? null;
|
|
601
|
-
}
|
|
602
|
-
return buildSandboxSnap(refAt, joinPath([...segs, ...childSegs]), cur);
|
|
640
|
+
const childVal = valueAtChildPath(val, childSegs);
|
|
641
|
+
return buildSandboxSnap(refAt, joinPath([...segs, ...childSegs]), childVal);
|
|
603
642
|
},
|
|
604
643
|
hasChild(p) {
|
|
605
644
|
return snap.child(p).exists();
|
|
606
645
|
},
|
|
607
646
|
hasChildren() {
|
|
608
|
-
return
|
|
609
|
-
typeof val === 'object' &&
|
|
610
|
-
!Array.isArray(val) &&
|
|
611
|
-
Object.keys(val).length > 0);
|
|
647
|
+
return children.length > 0;
|
|
612
648
|
},
|
|
613
649
|
numChildren() {
|
|
614
|
-
|
|
615
|
-
return 0;
|
|
616
|
-
return Object.keys(val).length;
|
|
650
|
+
return children.length;
|
|
617
651
|
},
|
|
618
652
|
forEach(cb) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
for (const [k, v] of Object.entries(val)) {
|
|
622
|
-
const childSnap = buildSandboxSnap(refAt, joinPath([...segs, k]), v);
|
|
653
|
+
for (const { key: childKey, value } of children) {
|
|
654
|
+
const childSnap = buildSandboxSnap(refAt, joinPath([...segs, childKey]), value);
|
|
623
655
|
if (cb(childSnap) === true)
|
|
624
656
|
return true;
|
|
625
657
|
}
|
|
@@ -655,22 +687,35 @@ function buildSandboxSnap(refAt, path, val) {
|
|
|
655
687
|
// `onEvent` throws on remote handles by design.
|
|
656
688
|
/** firebase-admin's rules-bypass lens, pinned on every relayed operation. */
|
|
657
689
|
const REMOTE_ADMIN_LENS = { mode: 'admin' };
|
|
658
|
-
/** One `Database` per remote handle
|
|
659
|
-
*
|
|
660
|
-
*
|
|
661
|
-
* in the
|
|
662
|
-
const
|
|
663
|
-
function getRemoteDatabase(sandbox) {
|
|
664
|
-
let
|
|
665
|
-
if (
|
|
666
|
-
|
|
690
|
+
/** One `Database` per remote handle and instance. Successive
|
|
691
|
+
* `getDatabase(app)` calls for an instance share the listener registry
|
|
692
|
+
* (matches the local arm's singleton-per-instance semantics). Keyed off
|
|
693
|
+
* the handle object; the data itself lives in the host. */
|
|
694
|
+
const remoteDbsBySandbox = new WeakMap();
|
|
695
|
+
function getRemoteDatabase(sandbox, instance) {
|
|
696
|
+
let registry = remoteDbsBySandbox.get(sandbox);
|
|
697
|
+
if (registry === undefined) {
|
|
698
|
+
registry = createDatabaseInstanceRegistry({ create: () => ({}) });
|
|
699
|
+
remoteDbsBySandbox.set(sandbox, registry);
|
|
700
|
+
}
|
|
701
|
+
const entry = registry.getOrCreate(registry.keyOf(instance));
|
|
702
|
+
if (entry.db !== undefined)
|
|
703
|
+
return entry.db;
|
|
667
704
|
const state = {
|
|
668
705
|
channel: sandbox.channel,
|
|
706
|
+
instance: instance?.name,
|
|
669
707
|
listeners: new Map(),
|
|
670
708
|
};
|
|
671
|
-
db = buildDatabaseShell((dbHandle, path) => buildRemoteRef(state, dbHandle, path));
|
|
672
|
-
|
|
673
|
-
|
|
709
|
+
entry.db = buildDatabaseShell(instance, (dbHandle, path) => buildRemoteRef(state, dbHandle, path));
|
|
710
|
+
return entry.db;
|
|
711
|
+
}
|
|
712
|
+
/** Relay `op` on the state's instance, under firebase-admin's rules-bypass lens. */
|
|
713
|
+
function relayRtdbOp(state, op) {
|
|
714
|
+
return state.channel.op({ ...op, instance: state.instance, actAs: REMOTE_ADMIN_LENS });
|
|
715
|
+
}
|
|
716
|
+
/** Subscribe to the value at `path` on the state's instance, under the admin lens. */
|
|
717
|
+
function subscribeRtdbValue(state, path, onSnap, onError) {
|
|
718
|
+
return state.channel.subscribe({ target: { service: 'rtdb', instance: state.instance, path }, actAs: REMOTE_ADMIN_LENS }, onSnap, onError);
|
|
674
719
|
}
|
|
675
720
|
/**
|
|
676
721
|
* Build a remote `Reference` at `path`. Same load-bearing surface as the
|
|
@@ -704,19 +749,17 @@ function buildRemoteRef(state, db, path) {
|
|
|
704
749
|
// ─── Data-plane methods (relayed worker ops) ─────────────────────
|
|
705
750
|
/** Set `value` at this path. `null` deletes. Relays `rtdb.set`. */
|
|
706
751
|
async set(value) {
|
|
707
|
-
await state
|
|
752
|
+
await relayRtdbOp(state, {
|
|
708
753
|
method: 'rtdb.set',
|
|
709
754
|
path: canonical,
|
|
710
755
|
value: value ?? null,
|
|
711
|
-
actAs: REMOTE_ADMIN_LENS,
|
|
712
756
|
});
|
|
713
757
|
},
|
|
714
758
|
/** Read this path (`rtdb.get`). Resolves to a `DataSnapshot`. */
|
|
715
759
|
async get() {
|
|
716
|
-
const wire = (await state
|
|
760
|
+
const wire = (await relayRtdbOp(state, {
|
|
717
761
|
method: 'rtdb.get',
|
|
718
762
|
path: canonical,
|
|
719
|
-
actAs: REMOTE_ADMIN_LENS,
|
|
720
763
|
}));
|
|
721
764
|
return snapFromWire(wire);
|
|
722
765
|
},
|
|
@@ -730,7 +773,7 @@ function buildRemoteRef(state, db, path) {
|
|
|
730
773
|
return new Promise((resolve, reject) => {
|
|
731
774
|
let detach = null;
|
|
732
775
|
let settled = false;
|
|
733
|
-
detach = state
|
|
776
|
+
detach = subscribeRtdbValue(state, canonical, (value) => {
|
|
734
777
|
if (settled)
|
|
735
778
|
return;
|
|
736
779
|
settled = true;
|
|
@@ -756,19 +799,17 @@ function buildRemoteRef(state, db, path) {
|
|
|
756
799
|
if (values === null || typeof values !== 'object') {
|
|
757
800
|
throw new TypeError('pyric-admin/database sandbox: update expected an object.');
|
|
758
801
|
}
|
|
759
|
-
await state
|
|
802
|
+
await relayRtdbOp(state, {
|
|
760
803
|
method: 'rtdb.update',
|
|
761
804
|
path: canonical,
|
|
762
805
|
values: values,
|
|
763
|
-
actAs: REMOTE_ADMIN_LENS,
|
|
764
806
|
});
|
|
765
807
|
},
|
|
766
808
|
/** Delete the subtree at this path (`rtdb.remove`). */
|
|
767
809
|
async remove() {
|
|
768
|
-
await state
|
|
810
|
+
await relayRtdbOp(state, {
|
|
769
811
|
method: 'rtdb.remove',
|
|
770
812
|
path: canonical,
|
|
771
|
-
actAs: REMOTE_ADMIN_LENS,
|
|
772
813
|
});
|
|
773
814
|
},
|
|
774
815
|
/**
|
|
@@ -785,14 +826,7 @@ function buildRemoteRef(state, db, path) {
|
|
|
785
826
|
const childPath = joinPath([...segs, id]);
|
|
786
827
|
const write = value === undefined
|
|
787
828
|
? Promise.resolve()
|
|
788
|
-
: state.
|
|
789
|
-
.op({
|
|
790
|
-
method: 'rtdb.push',
|
|
791
|
-
path: canonical,
|
|
792
|
-
key: id,
|
|
793
|
-
value,
|
|
794
|
-
actAs: REMOTE_ADMIN_LENS,
|
|
795
|
-
})
|
|
829
|
+
: relayRtdbOp(state, { method: 'rtdb.push', path: canonical, key: id, value })
|
|
796
830
|
.then(() => undefined);
|
|
797
831
|
// Surface completion without forcing the caller to await: `.then`'s
|
|
798
832
|
// rejection handler also keeps a fire-and-forget push from becoming
|
|
@@ -836,7 +870,7 @@ function buildRemoteRef(state, db, path) {
|
|
|
836
870
|
const cancelCallback = typeof cancelCallbackOrContext === 'function'
|
|
837
871
|
? cancelCallbackOrContext
|
|
838
872
|
: undefined;
|
|
839
|
-
const detach = state
|
|
873
|
+
const detach = subscribeRtdbValue(state, canonical, (value) => {
|
|
840
874
|
callback(snapFromWire(value));
|
|
841
875
|
}, (err) => {
|
|
842
876
|
detachListener(state, canonical, callback);
|