pyric-admin 0.1.0-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,159 @@
1
+ /**
2
+ * `pyric-admin/storage` — sandbox mirror for the Admin Storage shape.
3
+ *
4
+ * Mirrors the useful `firebase-admin/storage` shape for local and remote
5
+ * sandbox apps selected at {@link initializeApp} time. Dispatch reads the
6
+ * {@link ADMIN_APP_TARGET} brand on the `PyricAdminApp` handle.
7
+ *
8
+ * - **Remote sandbox path** — a handle branded by `@pyric/cli`'
9
+ * `connectRemoteSandbox()`/`remoteSandbox()` relays every data
10
+ * operation over the bridge to the browser-hosted SharedWorker's
11
+ * object store (admin lens pinned — rules bypass). Single bucket;
12
+ * 8 MiB per-op byte cap; `getSignedUrl` stays the local stub. See
13
+ * the remote arm section below.
14
+ *
15
+ * - **Sandbox path** — returns an in-process {@link Storage} backed
16
+ * by an in-memory `Map<bucketName, Map<path, FileEntry>>`. State
17
+ * lives on the {@link Sandbox} via a `WeakMap`, so `sandbox.reset()`
18
+ * wipes it alongside Firestore / Auth state. Multi-bucket isolation
19
+ * is real — buckets are independent maps.
20
+ *
21
+ * Supported sandbox surface (the minimum a session-archive flow
22
+ * needs):
23
+ * - `storage.bucket(name?)` → {@link Bucket}-shaped handle
24
+ * - `bucket.file(path)` → {@link File}-shaped handle
25
+ * - `file.save(data, options?)` — `Buffer | string | Uint8Array`
26
+ * - `file.download(options?)` → `[Buffer]`
27
+ * - `file.delete()` — idempotent
28
+ * - `file.exists()` → `[boolean]`
29
+ * - `file.getSignedUrl(options)` → `['pyric-sandbox-storage://…']`
30
+ *
31
+ * **Deferred in the sandbox backend** (throws `"not implemented in
32
+ * pyric-admin/storage sandbox backend"`): streaming uploads
33
+ * (`createWriteStream`), resumable uploads, signed cookies, IAM
34
+ * policies, lifecycle rules, ACLs, copy/move, notifications.
35
+ */
36
+ import { type PyricAdminApp } from '../app/index.js';
37
+ /**
38
+ * `pyric-admin/storage`'s sandbox `Storage` handle. It exposes the subset
39
+ * documented in the module header.
40
+ *
41
+ * The shared `bucket(name?)` shape is the contract — consumers code
42
+ * against it without caring whether the local or remote sandbox is live.
43
+ */
44
+ export interface Storage {
45
+ /**
46
+ * Get a {@link Bucket} handle. When `name` is omitted, returns the
47
+ * sandbox default bucket (`'pyric-default'`).
48
+ */
49
+ bucket(name?: string): Bucket;
50
+ }
51
+ /**
52
+ * A storage bucket handle implemented by both local and remote sandbox
53
+ * paths. Only the documented subset is supported.
54
+ */
55
+ export interface Bucket {
56
+ /** Name of the bucket. Stable across `file()` lookups. */
57
+ readonly name: string;
58
+ /** Get a {@link File} handle for `path`. The file may or may not exist. */
59
+ file(path: string): File;
60
+ }
61
+ /**
62
+ * A file handle within a bucket. Method shapes mirror
63
+ * `@google-cloud/storage`'s `File` (return tuples for download / exists
64
+ * / getSignedUrl, etc.) so common consumer code retains the familiar shape.
65
+ *
66
+ * The sandbox backend implements the methods documented here. Any
67
+ * other `File` method from `@google-cloud/storage` (`createWriteStream`,
68
+ * `createReadStream`, `copy`, `move`, `setMetadata` beyond the basic
69
+ * `save` options, etc.) throws on the sandbox path — see module header.
70
+ */
71
+ export interface File {
72
+ /** Name (path) of the file within its bucket. */
73
+ readonly name: string;
74
+ /** Bucket the file belongs to. Same handle the `file()` call came from. */
75
+ readonly bucket: Bucket;
76
+ /**
77
+ * Persist `data` at this file's path. Replaces any existing content
78
+ * (no append semantics). `options.metadata` is stored alongside the
79
+ * bytes and surfaces on later reads via the in-memory state — the
80
+ * sandbox doesn't expose a full `Metadata` API yet, but the payload
81
+ * round-trips so future expansion is non-breaking.
82
+ */
83
+ save(data: Buffer | string | Uint8Array, options?: SaveOptions): Promise<void>;
84
+ /**
85
+ * Read the file's bytes. Returns a `[Buffer]` tuple to mirror
86
+ * `@google-cloud/storage`'s `File.download` (which returns
87
+ * `[Buffer, ...]`). Throws if the file does not exist.
88
+ */
89
+ download(options?: DownloadOptions): Promise<[Buffer]>;
90
+ /**
91
+ * Remove the file from its bucket. Idempotent — deleting a missing
92
+ * file is a no-op (matches `@google-cloud/storage`'s
93
+ * `ignoreNotFound: true`, which is the only mode the sandbox models).
94
+ */
95
+ delete(): Promise<void>;
96
+ /** `[true]` if the file exists, `[false]` otherwise. Tuple shape mirrors `@google-cloud/storage`. */
97
+ exists(): Promise<[boolean]>;
98
+ /**
99
+ * Return a stub signed URL of the form
100
+ * `pyric-sandbox-storage://${path}?expires=${expires}`. The sandbox
101
+ * does NOT serve the URL — it's a deterministic placeholder so
102
+ * agent code that round-trips signed URLs (logs, fixtures, replay)
103
+ * sees a stable shape.
104
+ */
105
+ getSignedUrl(options: GetSignedUrlOptions): Promise<[string]>;
106
+ }
107
+ /** Options bag for {@link File.save}. Subset of `@google-cloud/storage`'s `SaveOptions`. */
108
+ export interface SaveOptions {
109
+ /**
110
+ * Arbitrary metadata stored alongside the file. The sandbox stores
111
+ * it verbatim; consumers that need to round-trip `contentType`,
112
+ * `metadata.custom`, etc. get it back via internal admin tooling
113
+ * (not exposed on `File` itself yet).
114
+ */
115
+ metadata?: Record<string, unknown>;
116
+ /**
117
+ * Content type hint stored on the sandbox entry.
118
+ * Convenience shortcut for `metadata.contentType`.
119
+ */
120
+ contentType?: string;
121
+ /**
122
+ * `resumable: false` is the only mode the sandbox models (single-
123
+ * shot writes). The sandbox throws when set to `true` since resumable
124
+ * uploads are deferred.
125
+ */
126
+ resumable?: boolean;
127
+ }
128
+ /** Options bag for {@link File.download}. Subset of `@google-cloud/storage`'s `DownloadOptions`. */
129
+ export interface DownloadOptions {
130
+ /** The sandbox accepts but ignores `validation`. */
131
+ validation?: 'md5' | 'crc32c' | boolean;
132
+ }
133
+ /** Options bag for {@link File.getSignedUrl}. Mirrors `@google-cloud/storage`'s shape. */
134
+ export interface GetSignedUrlOptions {
135
+ /** `'read' | 'write' | 'delete' | 'resumable'`. Sandbox stamps it into the URL only as a hint. */
136
+ action: 'read' | 'write' | 'delete' | 'resumable';
137
+ /**
138
+ * Expiration. Accepts ms-since-epoch (number), ISO date string, or
139
+ * `Date`. Sandbox normalizes to ms-since-epoch and embeds in the
140
+ * stub URL's `expires=` query.
141
+ */
142
+ expires: number | string | Date;
143
+ }
144
+ /**
145
+ * Input accepted by {@link getStorage}. The branded `PyricAdminApp` is
146
+ * the canonical shape; calling without an argument resolves the default
147
+ * app from the `pyric-admin/app` registry (mirroring
148
+ * `firebase-admin/storage`, where `getStorage()` resolves the default App),
149
+ * and throws the captured `app/no-app` error when nothing is initialized.
150
+ */
151
+ export type StorageApp = PyricAdminApp;
152
+ /**
153
+ * Get the {@link Storage} service for the given app.
154
+ *
155
+ * Returns a sandbox-backed `Storage` whose state
156
+ * lives on the `Sandbox`. `sandbox.reset()` wipes it.
157
+ */
158
+ export declare function getStorage(app?: StorageApp): Storage;
159
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AASH,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AAKzB;;;;;;GAMG;AACH,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,IAAI;IACnB,iDAAiD;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,UAAU,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IACvD;;;;OAIG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,qGAAqG;IACrG,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7B;;;;;;OAMG;IACH,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;CAC/D;AAED,4FAA4F;AAC5F,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,oGAAoG;AACpG,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,UAAU,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;CACzC;AAED,0FAA0F;AAC1F,MAAM,WAAW,mBAAmB;IAClC,kGAAkG;IAClG,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;IAClD;;;;OAIG;IACH,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACjC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,aAAa,CAAC;AAEvC;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,CAAC,EAAE,UAAU,GAAG,OAAO,CAuBpD"}
@@ -0,0 +1,413 @@
1
+ /**
2
+ * `pyric-admin/storage` — sandbox mirror for the Admin Storage shape.
3
+ *
4
+ * Mirrors the useful `firebase-admin/storage` shape for local and remote
5
+ * sandbox apps selected at {@link initializeApp} time. Dispatch reads the
6
+ * {@link ADMIN_APP_TARGET} brand on the `PyricAdminApp` handle.
7
+ *
8
+ * - **Remote sandbox path** — a handle branded by `@pyric/cli`'
9
+ * `connectRemoteSandbox()`/`remoteSandbox()` relays every data
10
+ * operation over the bridge to the browser-hosted SharedWorker's
11
+ * object store (admin lens pinned — rules bypass). Single bucket;
12
+ * 8 MiB per-op byte cap; `getSignedUrl` stays the local stub. See
13
+ * the remote arm section below.
14
+ *
15
+ * - **Sandbox path** — returns an in-process {@link Storage} backed
16
+ * by an in-memory `Map<bucketName, Map<path, FileEntry>>`. State
17
+ * lives on the {@link Sandbox} via a `WeakMap`, so `sandbox.reset()`
18
+ * wipes it alongside Firestore / Auth state. Multi-bucket isolation
19
+ * is real — buckets are independent maps.
20
+ *
21
+ * Supported sandbox surface (the minimum a session-archive flow
22
+ * needs):
23
+ * - `storage.bucket(name?)` → {@link Bucket}-shaped handle
24
+ * - `bucket.file(path)` → {@link File}-shaped handle
25
+ * - `file.save(data, options?)` — `Buffer | string | Uint8Array`
26
+ * - `file.download(options?)` → `[Buffer]`
27
+ * - `file.delete()` — idempotent
28
+ * - `file.exists()` → `[boolean]`
29
+ * - `file.getSignedUrl(options)` → `['pyric-sandbox-storage://…']`
30
+ *
31
+ * **Deferred in the sandbox backend** (throws `"not implemented in
32
+ * pyric-admin/storage sandbox backend"`): streaming uploads
33
+ * (`createWriteStream`), resumable uploads, signed cookies, IAM
34
+ * policies, lifecycle rules, ACLs, copy/move, notifications.
35
+ */
36
+ import { isRemoteSandbox, } from 'pyric/sandbox';
37
+ import { ADMIN_APP_TARGET, getApp, } from '../app/index.js';
38
+ import { assertAdminAppActive } from '../app/lifecycle.js';
39
+ /**
40
+ * Get the {@link Storage} service for the given app.
41
+ *
42
+ * Returns a sandbox-backed `Storage` whose state
43
+ * lives on the `Sandbox`. `sandbox.reset()` wipes it.
44
+ */
45
+ export function getStorage(app) {
46
+ // No-arg call resolves the default app; nothing initialized → captured
47
+ // `app/no-app` FirebaseAppError (see pyric-admin/app getApp).
48
+ const resolved = app === undefined ? getApp() : app;
49
+ assertAdminAppActive(resolved);
50
+ if (resolved[ADMIN_APP_TARGET] === 'sandbox') {
51
+ // Remote brand checked BEFORE the local arm (same dispatch order as
52
+ // auth/database): the local arm's WeakMap state + `onEvent` reset hook
53
+ // must never touch a remote handle — local state keyed off a remote
54
+ // handle would be a private server-side store the browser never sees,
55
+ // and `onEvent` throws on remote handles by design.
56
+ if (isRemoteSandbox(resolved.sandbox)) {
57
+ return getRemoteStorage(resolved.sandbox);
58
+ }
59
+ return getSandboxStorage(resolved);
60
+ }
61
+ // Defensive: the union is closed at the type level. A runtime value
62
+ // that lands here means a caller forged a handle without going
63
+ // through `initializeApp`.
64
+ throw new TypeError('pyric-admin/storage: getStorage expected a PyricAdminApp from `initializeApp`; ' +
65
+ 'received a value with no recognized ADMIN_APP_TARGET brand.');
66
+ }
67
+ // ─── Sandbox path ───────────────────────────────────────────────────────
68
+ /**
69
+ * Default sandbox bucket name. Matches the `pyric-default` used by the
70
+ * `pyric/storage` modular sandbox so consumers that switch between
71
+ * surfaces don't see an unexpected bucket name change.
72
+ */
73
+ const DEFAULT_SANDBOX_BUCKET = 'pyric-default';
74
+ /**
75
+ * State + reset-handler registry keyed on `Sandbox` so a single
76
+ * Sandbox shares its storage across every `getStorage` call. The
77
+ * `WeakMap` lets a discarded `Sandbox` (and its state) be GC'd
78
+ * naturally.
79
+ */
80
+ const SANDBOX_STATE = new WeakMap();
81
+ /**
82
+ * Reset-subscription bookkeeping. We subscribe to `sandbox.onEvent`
83
+ * once per Sandbox and re-create the bucket map on
84
+ * `session_boundary` events with `phase: 'reset'`. Without this,
85
+ * `sandbox.reset()` would wipe Firestore but leave storage untouched —
86
+ * a leak the sandbox model deliberately avoids.
87
+ */
88
+ const RESET_HOOKED = new WeakSet();
89
+ function ensureBucketMap(sandbox) {
90
+ let map = SANDBOX_STATE.get(sandbox);
91
+ if (!map) {
92
+ map = new Map();
93
+ SANDBOX_STATE.set(sandbox, map);
94
+ }
95
+ if (!RESET_HOOKED.has(sandbox)) {
96
+ RESET_HOOKED.add(sandbox);
97
+ sandbox.onEvent((event) => {
98
+ if (event.kind === 'session_boundary' && event.phase === 'reset') {
99
+ // Replace the map in place so existing Storage / Bucket / File
100
+ // handles keep working but observe an empty state.
101
+ const existing = SANDBOX_STATE.get(sandbox);
102
+ if (existing)
103
+ existing.clear();
104
+ }
105
+ });
106
+ }
107
+ return map;
108
+ }
109
+ function getSandboxStorage(app) {
110
+ const sandbox = app.sandbox;
111
+ const buckets = ensureBucketMap(sandbox);
112
+ return new SandboxStorage(buckets);
113
+ }
114
+ /**
115
+ * Sandbox `Storage` implementation. Holds a reference to the per-
116
+ * sandbox bucket map; each `bucket()` call returns a fresh `Bucket`
117
+ * handle bound to the same underlying map, mirroring how
118
+ * `@google-cloud/storage` returns lightweight per-call handles.
119
+ */
120
+ class SandboxStorage {
121
+ buckets;
122
+ constructor(buckets) {
123
+ this.buckets = buckets;
124
+ }
125
+ bucket(name) {
126
+ const bucketName = name ?? DEFAULT_SANDBOX_BUCKET;
127
+ let files = this.buckets.get(bucketName);
128
+ if (!files) {
129
+ files = new Map();
130
+ this.buckets.set(bucketName, files);
131
+ }
132
+ return new SandboxBucket(bucketName, files);
133
+ }
134
+ }
135
+ class SandboxBucket {
136
+ name;
137
+ files;
138
+ constructor(name, files) {
139
+ this.name = name;
140
+ this.files = files;
141
+ }
142
+ file(path) {
143
+ return new SandboxFile(path, this, this.files);
144
+ }
145
+ }
146
+ class SandboxFile {
147
+ name;
148
+ bucket;
149
+ files;
150
+ constructor(name, bucket, files) {
151
+ this.name = name;
152
+ this.bucket = bucket;
153
+ this.files = files;
154
+ }
155
+ async save(data, options = {}) {
156
+ if (options.resumable === true) {
157
+ throw new Error('not implemented in pyric-admin/storage sandbox backend: resumable uploads');
158
+ }
159
+ const bytes = toBytes(data);
160
+ const metadata = options.metadata ?? {};
161
+ const entry = {
162
+ data: bytes,
163
+ metadata,
164
+ ...(options.contentType !== undefined ? { contentType: options.contentType } : {}),
165
+ };
166
+ this.files.set(this.name, entry);
167
+ }
168
+ async download(_options = {}) {
169
+ const entry = this.files.get(this.name);
170
+ if (!entry) {
171
+ // Mirror the gcs/firebase-admin error message shape so consumer
172
+ // catch-blocks that string-match `No such object` keep working.
173
+ throw new Error(`No such object: ${this.bucket.name}/${this.name}`);
174
+ }
175
+ return [Buffer.from(entry.data)];
176
+ }
177
+ async delete() {
178
+ this.files.delete(this.name);
179
+ }
180
+ async exists() {
181
+ return [this.files.has(this.name)];
182
+ }
183
+ async getSignedUrl(options) {
184
+ return [stubSignedUrl(this.bucket.name, this.name, options)];
185
+ }
186
+ // ─── Deferred surface (declared so TS callers see a clear error) ────
187
+ /** @deprecated Streaming writes are deferred — see module header. */
188
+ createWriteStream() {
189
+ throw new Error('not implemented in pyric-admin/storage sandbox backend: createWriteStream');
190
+ }
191
+ /** @deprecated Streaming reads are deferred — see module header. */
192
+ createReadStream() {
193
+ throw new Error('not implemented in pyric-admin/storage sandbox backend: createReadStream');
194
+ }
195
+ }
196
+ // ─── Remote sandbox arm (remote sandbox, slice 2) ───────────────────────
197
+ //
198
+ // The app's `Sandbox` is a Node-side handle onto the browser-hosted
199
+ // SharedWorker sandbox. Every data operation relays over the handle's
200
+ // worker channel with `actAs: { mode: 'admin' }` pinned — firebase-admin's
201
+ // rules-bypass semantics against the ONE object store the app + Studio +
202
+ // agents share (the host resolves the lens to `pyric/storage/internal`'s
203
+ // admin plane). There is deliberately NO local state here: a `WeakMap`
204
+ // bucket map keyed off a remote handle would be private server-side data
205
+ // the browser never sees, and the local arm's `onEvent` reset hook throws
206
+ // on remote handles by design.
207
+ //
208
+ // Divergences from the local arm, all LOUD:
209
+ // - single bucket: the worker's `pyric/storage` store is single-bucket
210
+ // ("the data store is shared" — bucket names only round-trip in
211
+ // metadata), so `bucket('non-default')` throws instead of silently
212
+ // merging buckets. The default bucket name matches the local arm.
213
+ // - byte payloads are capped at 8 MiB per op (whole-object buffering
214
+ // over four relay hops; streaming stays unsupported on both sandbox arms).
215
+ // `getSignedUrl` does NOT relay: it stays the byte-identical local stub.
216
+ /** firebase-admin's rules-bypass lens, pinned on every relayed operation. */
217
+ const STORAGE_REMOTE_ADMIN_LENS = { mode: 'admin' };
218
+ /**
219
+ * Raw per-op byte cap for relayed storage payloads. MUST mirror
220
+ * `@pyric/cli`' `MAX_STORAGE_OP_BYTES` (serve/worker/protocol.ts) — the
221
+ * worker host enforces the same cap on its end. Inlined (like the RTDB
222
+ * push-id generator) because `pyric-admin` deliberately does not depend on
223
+ * `@pyric/cli`.
224
+ */
225
+ const MAX_REMOTE_STORAGE_OP_BYTES = 8 * 1024 * 1024;
226
+ /** One remote `Storage` per remote handle (handles only — never data). */
227
+ const remoteStorageBySandbox = new WeakMap();
228
+ function getRemoteStorage(sandbox) {
229
+ let storage = remoteStorageBySandbox.get(sandbox);
230
+ if (!storage) {
231
+ storage = new RemoteStorage(sandbox.channel);
232
+ remoteStorageBySandbox.set(sandbox, storage);
233
+ }
234
+ return storage;
235
+ }
236
+ class RemoteStorage {
237
+ channel;
238
+ constructor(channel) {
239
+ this.channel = channel;
240
+ }
241
+ bucket(name) {
242
+ // The worker's object store is single-bucket. A non-default name can't
243
+ // be faithfully relayed — throw loudly instead of silently merging
244
+ // buckets (the local arm has REAL multi-bucket isolation; this is the
245
+ // sharpest local/remote divergence, so it must be explicit).
246
+ if (name !== undefined && name !== DEFAULT_SANDBOX_BUCKET) {
247
+ throw new Error(`pyric-admin/storage: the remote (browser) sandbox has a single bucket — ` +
248
+ `bucket('${name}') cannot be isolated. Use bucket() (the default ` +
249
+ `'${DEFAULT_SANDBOX_BUCKET}' bucket) instead.`);
250
+ }
251
+ return new RemoteBucket(DEFAULT_SANDBOX_BUCKET, this.channel);
252
+ }
253
+ }
254
+ class RemoteBucket {
255
+ name;
256
+ channel;
257
+ constructor(name, channel) {
258
+ this.name = name;
259
+ this.channel = channel;
260
+ }
261
+ file(path) {
262
+ return new RemoteFile(path, this, this.channel);
263
+ }
264
+ }
265
+ class RemoteFile {
266
+ name;
267
+ bucket;
268
+ channel;
269
+ constructor(name, bucket, channel) {
270
+ this.name = name;
271
+ this.bucket = bucket;
272
+ this.channel = channel;
273
+ }
274
+ async save(data, options = {}) {
275
+ if (options.resumable === true) {
276
+ throw new Error('not implemented in pyric-admin/storage remote sandbox backend: resumable uploads');
277
+ }
278
+ const bytes = toBytes(data);
279
+ if (bytes.byteLength > MAX_REMOTE_STORAGE_OP_BYTES) {
280
+ throw payloadTooLarge(bytes.byteLength, `save() payload for '${this.name}'`);
281
+ }
282
+ await this.channel.op({
283
+ method: 'storage.putBytes',
284
+ path: this.name,
285
+ dataB64: Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('base64'),
286
+ ...(options.contentType !== undefined ? { contentType: options.contentType } : {}),
287
+ ...(options.metadata !== undefined ? { metadata: options.metadata } : {}),
288
+ actAs: STORAGE_REMOTE_ADMIN_LENS,
289
+ });
290
+ }
291
+ async download(_options = {}) {
292
+ let wire;
293
+ try {
294
+ wire = (await this.channel.op({
295
+ method: 'storage.getBytes',
296
+ path: this.name,
297
+ actAs: STORAGE_REMOTE_ADMIN_LENS,
298
+ }));
299
+ }
300
+ catch (err) {
301
+ if (isObjectNotFound(err)) {
302
+ // Mirror the gcs/firebase-admin (and local arm) message shape so
303
+ // consumer catch-blocks that string-match `No such object` work
304
+ // identically across arms.
305
+ throw new Error(`No such object: ${this.bucket.name}/${this.name}`);
306
+ }
307
+ throw err;
308
+ }
309
+ return [Buffer.from(wire.dataB64, 'base64')];
310
+ }
311
+ async delete() {
312
+ try {
313
+ await this.channel.op({
314
+ method: 'storage.deleteObject',
315
+ path: this.name,
316
+ actAs: STORAGE_REMOTE_ADMIN_LENS,
317
+ });
318
+ }
319
+ catch (err) {
320
+ // The worker store's delete is already idempotent, but swallow a
321
+ // not-found defensively so the local arm's idempotent-delete contract
322
+ // holds even if `pyric/storage` adopts stricter delete semantics later.
323
+ if (isObjectNotFound(err))
324
+ return;
325
+ throw err;
326
+ }
327
+ }
328
+ async exists() {
329
+ try {
330
+ await this.channel.op({
331
+ method: 'storage.getMetadata',
332
+ path: this.name,
333
+ actAs: STORAGE_REMOTE_ADMIN_LENS,
334
+ });
335
+ return [true];
336
+ }
337
+ catch (err) {
338
+ if (isObjectNotFound(err))
339
+ return [false];
340
+ throw err;
341
+ }
342
+ }
343
+ /** Local stub — byte-identical to the local arm's (no data needed, so it
344
+ * never relays). The sandbox does NOT serve the URL. */
345
+ async getSignedUrl(options) {
346
+ return [stubSignedUrl(this.bucket.name, this.name, options)];
347
+ }
348
+ // ─── Deferred surface (remediating throws, remote-flavored) ─────────
349
+ createWriteStream() {
350
+ throw new Error('not implemented in pyric-admin/storage remote sandbox backend: createWriteStream — ' +
351
+ 'streams cannot span the bridge relay; use file.save(buffer) (≤ 8 MiB) instead.');
352
+ }
353
+ createReadStream() {
354
+ throw new Error('not implemented in pyric-admin/storage remote sandbox backend: createReadStream — ' +
355
+ 'streams cannot span the bridge relay; use file.download() (≤ 8 MiB) instead.');
356
+ }
357
+ }
358
+ /** Is this relayed error the worker's `storage/object-not-found`? */
359
+ function isObjectNotFound(err) {
360
+ return err?.code === 'storage/object-not-found';
361
+ }
362
+ /** Over-cap rejection (code `payload-too-large`) — mirrors the worker host's
363
+ * message shape and names the streaming gap. */
364
+ function payloadTooLarge(sizeBytes, what) {
365
+ const err = new Error(`pyric-admin/storage: ${what} is ${sizeBytes} bytes — over the ` +
366
+ `${MAX_REMOTE_STORAGE_OP_BYTES / (1024 * 1024)} MiB remote storage op cap. ` +
367
+ 'Streaming/resumable transfers are not supported on the sandbox backend; ' +
368
+ 'split the object or keep it under the cap.');
369
+ err.code = 'payload-too-large';
370
+ return err;
371
+ }
372
+ // ─── Helpers ────────────────────────────────────────────────────────────
373
+ /**
374
+ * The deterministic sandbox signed-URL stub, shared by the local and remote
375
+ * arms so their output is byte-identical (the URL is never served — it's a
376
+ * stable placeholder for logs/fixtures/replay).
377
+ */
378
+ function stubSignedUrl(bucketName, path, options) {
379
+ const expiresMs = normalizeExpires(options.expires);
380
+ return `pyric-sandbox-storage://${bucketName}/${path}?expires=${expiresMs}&action=${options.action}`;
381
+ }
382
+ /**
383
+ * Normalize `Buffer | string | Uint8Array` into a fresh `Uint8Array`.
384
+ * We copy on ingest so callers can mutate their input buffer without
385
+ * corrupting stored state — mirrors how `firebase-admin/storage` /
386
+ * `@google-cloud/storage` treat `save` inputs.
387
+ */
388
+ function toBytes(data) {
389
+ if (typeof data === 'string') {
390
+ return new TextEncoder().encode(data);
391
+ }
392
+ // Both Buffer (Node) and Uint8Array land here — copy into a new
393
+ // Uint8Array so the stored bytes are independent of the caller's
394
+ // reference. `slice()` produces a copy in both cases.
395
+ return new Uint8Array(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
396
+ }
397
+ /**
398
+ * Normalize the `expires` field into ms-since-epoch. Mirrors the
399
+ * accepted shapes from `@google-cloud/storage`'s `GetSignedUrlOptions`.
400
+ * The sandbox doesn't enforce expiration — the value is only embedded
401
+ * in the stub URL so consumers can round-trip it.
402
+ */
403
+ function normalizeExpires(expires) {
404
+ if (typeof expires === 'number')
405
+ return expires;
406
+ if (expires instanceof Date)
407
+ return expires.getTime();
408
+ // String form — accept anything `Date` parses. Bogus input becomes
409
+ // `NaN`, which is still embeddable in the URL; we don't enforce
410
+ // strictness because the value only feeds the deterministic sandbox stub.
411
+ return new Date(expires).getTime();
412
+ }
413
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EACL,eAAe,GAIhB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,MAAM,GAGP,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AA8H3D;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,GAAgB;IACzC,uEAAuE;IACvE,8DAA8D;IAC9D,MAAM,QAAQ,GAAkB,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAE,GAAqB,CAAC;IACtF,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC/B,IAAI,QAAQ,CAAC,gBAAgB,CAAC,KAAK,SAAS,EAAE,CAAC;QAC7C,oEAAoE;QACpE,uEAAuE;QACvE,oEAAoE;QACpE,sEAAsE;QACtE,oDAAoD;QACpD,IAAI,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACtC,OAAO,gBAAgB,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;IACD,oEAAoE;IACpE,+DAA+D;IAC/D,2BAA2B;IAC3B,MAAM,IAAI,SAAS,CACjB,iFAAiF;QAC/E,6DAA6D,CAChE,CAAC;AACJ,CAAC;AAED,2EAA2E;AAE3E;;;;GAIG;AACH,MAAM,sBAAsB,GAAG,eAAe,CAAC;AAY/C;;;;;GAKG;AACH,MAAM,aAAa,GAAG,IAAI,OAAO,EAAsB,CAAC;AAExD;;;;;;GAMG;AACH,MAAM,YAAY,GAAG,IAAI,OAAO,EAAW,CAAC;AAE5C,SAAS,eAAe,CAAC,OAAgB;IACvC,IAAI,GAAG,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;QAChB,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC1B,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACxB,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;gBACjE,+DAA+D;gBAC/D,mDAAmD;gBACnD,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC5C,IAAI,QAAQ;oBAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAoB;IAC7C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IAC5B,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACzC,OAAO,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC;AAED;;;;;GAKG;AACH,MAAM,cAAc;IACW;IAA7B,YAA6B,OAAkB;QAAlB,YAAO,GAAP,OAAO,CAAW;IAAG,CAAC;IAEnD,MAAM,CAAC,IAAa;QAClB,MAAM,UAAU,GAAG,IAAI,IAAI,sBAAsB,CAAC;QAClD,IAAI,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,IAAI,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC;CACF;AAED,MAAM,aAAa;IAEN;IACQ;IAFnB,YACW,IAAY,EACJ,KAA6B;QADrC,SAAI,GAAJ,IAAI,CAAQ;QACJ,UAAK,GAAL,KAAK,CAAwB;IAC7C,CAAC;IAEJ,IAAI,CAAC,IAAY;QACf,OAAO,IAAI,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC;CACF;AAED,MAAM,WAAW;IAEJ;IACA;IACQ;IAHnB,YACW,IAAY,EACZ,MAAc,EACN,KAA6B;QAFrC,SAAI,GAAJ,IAAI,CAAQ;QACZ,WAAM,GAAN,MAAM,CAAQ;QACN,UAAK,GAAL,KAAK,CAAwB;IAC7C,CAAC;IAEJ,KAAK,CAAC,IAAI,CACR,IAAkC,EAClC,UAAuB,EAAE;QAEzB,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CACb,2EAA2E,CAC5E,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;QACxC,MAAM,KAAK,GAAc;YACvB,IAAI,EAAE,KAAK;YACX,QAAQ;YACR,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACnF,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,WAA4B,EAAE;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,gEAAgE;YAChE,gEAAgE;YAChE,MAAM,IAAI,KAAK,CACb,mBAAmB,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CACnD,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,MAAM;QACV,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,MAAM;QACV,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,OAA4B;QAC7C,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED,uEAAuE;IAEvE,qEAAqE;IACrE,iBAAiB;QACf,MAAM,IAAI,KAAK,CACb,2EAA2E,CAC5E,CAAC;IACJ,CAAC;IAED,oEAAoE;IACpE,gBAAgB;QACd,MAAM,IAAI,KAAK,CACb,0EAA0E,CAC3E,CAAC;IACJ,CAAC;CACF;AAED,2EAA2E;AAC3E,EAAE;AACF,oEAAoE;AACpE,sEAAsE;AACtE,2EAA2E;AAC3E,yEAAyE;AACzE,yEAAyE;AACzE,uEAAuE;AACvE,yEAAyE;AACzE,0EAA0E;AAC1E,+BAA+B;AAC/B,EAAE;AACF,4CAA4C;AAC5C,yEAAyE;AACzE,oEAAoE;AACpE,uEAAuE;AACvE,sEAAsE;AACtE,uEAAuE;AACvE,+EAA+E;AAC/E,yEAAyE;AAEzE,6EAA6E;AAC7E,MAAM,yBAAyB,GAAG,EAAE,IAAI,EAAE,OAAO,EAAW,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,2BAA2B,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAEpD,0EAA0E;AAC1E,MAAM,sBAAsB,GAAG,IAAI,OAAO,EAAoB,CAAC;AAE/D,SAAS,gBAAgB,CAAC,OAAsB;IAC9C,IAAI,OAAO,GAAG,sBAAsB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAClD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,IAAI,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7C,sBAAsB,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AASD,MAAM,aAAa;IACY;IAA7B,YAA6B,OAA6B;QAA7B,YAAO,GAAP,OAAO,CAAsB;IAAG,CAAC;IAE9D,MAAM,CAAC,IAAa;QAClB,uEAAuE;QACvE,mEAAmE;QACnE,sEAAsE;QACtE,6DAA6D;QAC7D,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,sBAAsB,EAAE,CAAC;YAC1D,MAAM,IAAI,KAAK,CACb,0EAA0E;gBACxE,WAAW,IAAI,mDAAmD;gBAClE,IAAI,sBAAsB,oBAAoB,CACjD,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,YAAY,CAAC,sBAAsB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAChE,CAAC;CACF;AAED,MAAM,YAAY;IAEL;IACQ;IAFnB,YACW,IAAY,EACJ,OAA6B;QADrC,SAAI,GAAJ,IAAI,CAAQ;QACJ,YAAO,GAAP,OAAO,CAAsB;IAC7C,CAAC;IAEJ,IAAI,CAAC,IAAY;QACf,OAAO,IAAI,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAClD,CAAC;CACF;AAED,MAAM,UAAU;IAEH;IACA;IACQ;IAHnB,YACW,IAAY,EACZ,MAAc,EACN,OAA6B;QAFrC,SAAI,GAAJ,IAAI,CAAQ;QACZ,WAAM,GAAN,MAAM,CAAQ;QACN,YAAO,GAAP,OAAO,CAAsB;IAC7C,CAAC;IAEJ,KAAK,CAAC,IAAI,CACR,IAAkC,EAClC,UAAuB,EAAE;QAEzB,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CACb,kFAAkF,CACnF,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,KAAK,CAAC,UAAU,GAAG,2BAA2B,EAAE,CAAC;YACnD,MAAM,eAAe,CAAC,KAAK,CAAC,UAAU,EAAE,uBAAuB,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QAC/E,CAAC;QACD,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACpB,MAAM,EAAE,kBAAkB;YAC1B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACzF,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClF,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,KAAK,EAAE,yBAAyB;SACjC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,WAA4B,EAAE;QAC3C,IAAI,IAA0B,CAAC;QAC/B,IAAI,CAAC;YACH,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC5B,MAAM,EAAE,kBAAkB;gBAC1B,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,yBAAyB;aACjC,CAAC,CAAyB,CAAC;QAC9B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC1B,iEAAiE;gBACjE,gEAAgE;gBAChE,2BAA2B;gBAC3B,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACtE,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,MAAM;QACV,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpB,MAAM,EAAE,sBAAsB;gBAC9B,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,yBAAyB;aACjC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,iEAAiE;YACjE,sEAAsE;YACtE,wEAAwE;YACxE,IAAI,gBAAgB,CAAC,GAAG,CAAC;gBAAE,OAAO;YAClC,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM;QACV,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpB,MAAM,EAAE,qBAAqB;gBAC7B,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,KAAK,EAAE,yBAAyB;aACjC,CAAC,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,gBAAgB,CAAC,GAAG,CAAC;gBAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YAC1C,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED;6DACyD;IACzD,KAAK,CAAC,YAAY,CAAC,OAA4B;QAC7C,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED,uEAAuE;IAEvE,iBAAiB;QACf,MAAM,IAAI,KAAK,CACb,qFAAqF;YACnF,gFAAgF,CACnF,CAAC;IACJ,CAAC;IAED,gBAAgB;QACd,MAAM,IAAI,KAAK,CACb,oFAAoF;YAClF,8EAA8E,CACjF,CAAC;IACJ,CAAC;CACF;AAED,qEAAqE;AACrE,SAAS,gBAAgB,CAAC,GAAY;IACpC,OAAQ,GAA0B,EAAE,IAAI,KAAK,0BAA0B,CAAC;AAC1E,CAAC;AAED;iDACiD;AACjD,SAAS,eAAe,CAAC,SAAiB,EAAE,IAAY;IACtD,MAAM,GAAG,GAAG,IAAI,KAAK,CACnB,wBAAwB,IAAI,OAAO,SAAS,oBAAoB;QAC9D,GAAG,2BAA2B,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,8BAA8B;QAC5E,0EAA0E;QAC1E,4CAA4C,CACnB,CAAC;IAC9B,GAAG,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAC/B,OAAO,GAAG,CAAC;AACb,CAAC;AAED,2EAA2E;AAE3E;;;;GAIG;AACH,SAAS,aAAa,CACpB,UAAkB,EAClB,IAAY,EACZ,OAA4B;IAE5B,MAAM,SAAS,GAAG,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,OAAO,2BAA2B,UAAU,IAAI,IAAI,YAAY,SAAS,WAAW,OAAO,CAAC,MAAM,EAAE,CAAC;AACvG,CAAC;AAED;;;;;GAKG;AACH,SAAS,OAAO,CAAC,IAAkC;IACjD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC;IACD,gEAAgE;IAChE,iEAAiE;IACjE,sDAAsD;IACtD,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;AAC/F,CAAC;AAED;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,OAA+B;IACvD,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,IAAI,OAAO,YAAY,IAAI;QAAE,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IACtD,mEAAmE;IACnE,gEAAgE;IAChE,0EAA0E;IAC1E,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;AACrC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "pyric-admin",
3
+ "version": "0.1.0-alpha.10",
4
+ "license": "Apache-2.0",
5
+ "homepage": "https://pyric.dev",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/davideast/pyric.git",
9
+ "directory": "packages/pyric-admin"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/davideast/pyric/issues"
13
+ },
14
+ "description": "Firebase Admin-shaped adapters for the Pyric development sandbox.",
15
+ "type": "module",
16
+ "exports": {
17
+ "./app": {
18
+ "types": "./dist/app/index.d.ts",
19
+ "import": "./dist/app/index.js"
20
+ },
21
+ "./firestore": {
22
+ "types": "./dist/firestore/index.d.ts",
23
+ "import": "./dist/firestore/index.js"
24
+ },
25
+ "./auth": {
26
+ "types": "./dist/auth/index.d.ts",
27
+ "import": "./dist/auth/index.js"
28
+ },
29
+ "./database": {
30
+ "types": "./dist/database/index.d.ts",
31
+ "import": "./dist/database/index.js"
32
+ },
33
+ "./storage": {
34
+ "types": "./dist/storage/index.d.ts",
35
+ "import": "./dist/storage/index.js"
36
+ }
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "README.md",
41
+ "LICENSE"
42
+ ],
43
+ "scripts": {
44
+ "build": "tsc",
45
+ "test": "bun test",
46
+ "typecheck": "bun x tsc -p tsconfig.json --noEmit"
47
+ },
48
+ "dependencies": {
49
+ "pyric": "^0.1.0-alpha.10"
50
+ },
51
+ "devDependencies": {
52
+ "@types/bun": "latest",
53
+ "firebase-admin": "^13.0.0",
54
+ "typescript": "^5.7.0",
55
+ "fake-indexeddb": "^6.0.0"
56
+ },
57
+ "engines": {
58
+ "node": ">=22"
59
+ },
60
+ "publishConfig": {
61
+ "access": "public"
62
+ }
63
+ }