@series-inc/stowkit-cli 0.6.34 → 0.6.35

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.
@@ -34,25 +34,11 @@ export interface RegistryVersion {
34
34
  totalSize: number;
35
35
  assets: RegistryAsset[];
36
36
  }
37
- export interface RegistryPackage {
38
- description: string;
39
- author: string;
40
- tags: string[];
41
- latest: string;
42
- versions: Record<string, RegistryVersion>;
43
- /** Pack-level thumbnail filename (e.g. "thumbnail.webp"), uploaded during publish */
44
- thumbnail?: string;
45
- }
46
- export interface Registry {
47
- schemaVersion: number;
48
- packages: Record<string, RegistryPackage>;
49
- }
50
37
  export declare function readAssetsPackage(dir: string): Promise<AssetsPackage | null>;
51
38
  export declare function writeAssetsPackage(dir: string, pkg: AssetsPackage): Promise<void>;
52
39
  export declare function initAssetsPackage(dir: string, config: {
53
40
  name: string;
54
41
  }): Promise<AssetsPackage>;
55
- export declare function createEmptyRegistry(): Registry;
56
42
  /**
57
43
  * Given a set of requested stringIds and a version's asset list,
58
44
  * returns the full set of stringIds needed (transitive closure).
@@ -2,7 +2,7 @@ import * as fs from 'node:fs/promises';
2
2
  import * as path from 'node:path';
3
3
  // ─── Constants ───────────────────────────────────────────────────────────────
4
4
  const ASSETS_PACKAGE_FILE = 'assets-package.json';
5
- const DEFAULT_BUCKET = 'venus-shared-assets-test';
5
+ const DEFAULT_BUCKET = 'rungame-shared-assets-test';
6
6
  // ─── Read / Write / Init ─────────────────────────────────────────────────────
7
7
  export async function readAssetsPackage(dir) {
8
8
  try {
@@ -30,9 +30,6 @@ export async function initAssetsPackage(dir, config) {
30
30
  await writeAssetsPackage(dir, pkg);
31
31
  return pkg;
32
32
  }
33
- export function createEmptyRegistry() {
34
- return { schemaVersion: 1, packages: {} };
35
- }
36
33
  // ─── Dependency Resolution ───────────────────────────────────────────────────
37
34
  /**
38
35
  * Given a set of requested stringIds and a version's asset list,
@@ -0,0 +1,42 @@
1
+ import type { RegistryAsset } from './assets-package.js';
2
+ export interface FirestorePackageDoc {
3
+ description: string;
4
+ author: string;
5
+ tags: string[];
6
+ latest: string;
7
+ thumbnail: string | null;
8
+ }
9
+ export interface FirestoreVersionDoc {
10
+ publishedAt: string;
11
+ fileCount: number;
12
+ totalSize: number;
13
+ assets: RegistryAsset[];
14
+ }
15
+ /** Read-only client — uses public API key, no credentials needed */
16
+ export interface FirestoreReader {
17
+ projectId: string;
18
+ getPackage(name: string): Promise<FirestorePackageDoc | null>;
19
+ listPackages(): Promise<Array<{
20
+ name: string;
21
+ data: FirestorePackageDoc;
22
+ }>>;
23
+ getVersion(packageName: string, version: string): Promise<FirestoreVersionDoc | null>;
24
+ listVersionKeys(packageName: string): Promise<string[]>;
25
+ }
26
+ /** Read-write client — uses service account, needed for publishing */
27
+ export interface FirestoreClient extends FirestoreReader {
28
+ setPackage(name: string, data: FirestorePackageDoc): Promise<void>;
29
+ deletePackage(name: string): Promise<void>;
30
+ setVersion(packageName: string, version: string, data: FirestoreVersionDoc): Promise<void>;
31
+ }
32
+ /**
33
+ * Create a read-only Firestore client using the public API key.
34
+ * No service account or credentials needed — safe for CLI store commands,
35
+ * packer GUI browsing, and server read endpoints.
36
+ */
37
+ export declare function createFirestoreReader(): FirestoreReader;
38
+ /**
39
+ * Create a read-write Firestore client using a service account.
40
+ * Required for publishing — needs service_account.json in the project directory.
41
+ */
42
+ export declare function createFirestoreClient(projectDir: string): Promise<FirestoreClient>;
@@ -0,0 +1,240 @@
1
+ import { loadServiceAccount, getAccessToken, FIRESTORE_SCOPE, GCS_SCOPE } from './gcp-auth.js';
2
+ // ─── Constants ───────────────────────────────────────────────────────────────
3
+ const FIRESTORE_PROJECT_ID = 'run-shared-assets';
4
+ const FIRESTORE_API_KEY = 'AIzaSyCgat6uQgWI2_w2_5rK90RyuGbLs-Shp8Q';
5
+ function toFirestore(value) {
6
+ if (value === null || value === undefined)
7
+ return { nullValue: null };
8
+ if (typeof value === 'string')
9
+ return { stringValue: value };
10
+ if (typeof value === 'boolean')
11
+ return { booleanValue: value };
12
+ if (typeof value === 'number') {
13
+ if (Number.isInteger(value))
14
+ return { integerValue: String(value) };
15
+ return { doubleValue: value };
16
+ }
17
+ if (Array.isArray(value)) {
18
+ if (value.length === 0)
19
+ return { arrayValue: {} };
20
+ return { arrayValue: { values: value.map(toFirestore) } };
21
+ }
22
+ if (typeof value === 'object') {
23
+ const fields = {};
24
+ for (const [k, v] of Object.entries(value)) {
25
+ fields[k] = toFirestore(v);
26
+ }
27
+ return { mapValue: { fields } };
28
+ }
29
+ return { stringValue: String(value) };
30
+ }
31
+ function fromFirestore(value) {
32
+ if ('stringValue' in value)
33
+ return value.stringValue;
34
+ if ('integerValue' in value)
35
+ return Number(value.integerValue);
36
+ if ('doubleValue' in value)
37
+ return value.doubleValue;
38
+ if ('booleanValue' in value)
39
+ return value.booleanValue;
40
+ if ('nullValue' in value)
41
+ return null;
42
+ if ('arrayValue' in value) {
43
+ return (value.arrayValue.values ?? []).map(fromFirestore);
44
+ }
45
+ if ('mapValue' in value) {
46
+ const fields = value.mapValue.fields;
47
+ if (!fields)
48
+ return {};
49
+ const result = {};
50
+ for (const [k, v] of Object.entries(fields)) {
51
+ result[k] = fromFirestore(v);
52
+ }
53
+ return result;
54
+ }
55
+ return null;
56
+ }
57
+ function docToObject(fields) {
58
+ if (!fields)
59
+ return null;
60
+ const result = {};
61
+ for (const [k, v] of Object.entries(fields)) {
62
+ result[k] = fromFirestore(v);
63
+ }
64
+ return result;
65
+ }
66
+ function objectToFields(data) {
67
+ const fields = {};
68
+ for (const [k, v] of Object.entries(data)) {
69
+ fields[k] = toFirestore(v);
70
+ }
71
+ return fields;
72
+ }
73
+ // ─── REST helpers ────────────────────────────────────────────────────────────
74
+ const BASE = 'https://firestore.googleapis.com/v1';
75
+ function docPath(projectId, ...segments) {
76
+ return `${BASE}/projects/${projectId}/databases/(default)/documents/${segments.join('/')}`;
77
+ }
78
+ // ─── Shared read operations ─────────────────────────────────────────────────
79
+ function buildReadOps(projectId, fetchWithAuth) {
80
+ async function getPackage(name) {
81
+ const res = await fetchWithAuth(docPath(projectId, 'packages', name));
82
+ if (res.status === 404)
83
+ return null;
84
+ if (!res.ok) {
85
+ const text = await res.text();
86
+ throw new Error(`Firestore GET packages/${name} failed (${res.status}): ${text}`);
87
+ }
88
+ const doc = (await res.json());
89
+ return docToObject(doc.fields);
90
+ }
91
+ async function listPackages() {
92
+ const results = [];
93
+ let pageToken;
94
+ do {
95
+ const params = new URLSearchParams({ pageSize: '300' });
96
+ if (pageToken)
97
+ params.set('pageToken', pageToken);
98
+ const res = await fetchWithAuth(`${docPath(projectId, 'packages')}?${params}`);
99
+ if (!res.ok) {
100
+ const text = await res.text();
101
+ throw new Error(`Firestore LIST packages failed (${res.status}): ${text}`);
102
+ }
103
+ const body = (await res.json());
104
+ if (body.documents) {
105
+ for (const doc of body.documents) {
106
+ const docId = doc.name.split('/').pop();
107
+ const data = docToObject(doc.fields);
108
+ if (data)
109
+ results.push({ name: docId, data });
110
+ }
111
+ }
112
+ pageToken = body.nextPageToken;
113
+ } while (pageToken);
114
+ return results;
115
+ }
116
+ async function getVersion(packageName, version) {
117
+ const res = await fetchWithAuth(docPath(projectId, 'packages', packageName, 'versions', version));
118
+ if (res.status === 404)
119
+ return null;
120
+ if (!res.ok) {
121
+ const text = await res.text();
122
+ throw new Error(`Firestore GET packages/${packageName}/versions/${version} failed (${res.status}): ${text}`);
123
+ }
124
+ const doc = (await res.json());
125
+ return docToObject(doc.fields);
126
+ }
127
+ async function listVersionKeys(packageName) {
128
+ const results = [];
129
+ let pageToken;
130
+ do {
131
+ const params = new URLSearchParams({ pageSize: '300' });
132
+ if (pageToken)
133
+ params.set('pageToken', pageToken);
134
+ const res = await fetchWithAuth(`${docPath(projectId, 'packages', packageName, 'versions')}?${params}`);
135
+ if (!res.ok) {
136
+ const text = await res.text();
137
+ throw new Error(`Firestore LIST versions for ${packageName} failed (${res.status}): ${text}`);
138
+ }
139
+ const body = (await res.json());
140
+ if (body.documents) {
141
+ for (const doc of body.documents) {
142
+ results.push(doc.name.split('/').pop());
143
+ }
144
+ }
145
+ pageToken = body.nextPageToken;
146
+ } while (pageToken);
147
+ return results;
148
+ }
149
+ return { getPackage, listPackages, getVersion, listVersionKeys };
150
+ }
151
+ // ─── Public Reader (API key, no credentials) ────────────────────────────────
152
+ /**
153
+ * Create a read-only Firestore client using the public API key.
154
+ * No service account or credentials needed — safe for CLI store commands,
155
+ * packer GUI browsing, and server read endpoints.
156
+ */
157
+ export function createFirestoreReader() {
158
+ const projectId = FIRESTORE_PROJECT_ID;
159
+ const apiKey = FIRESTORE_API_KEY;
160
+ function fetchWithAuth(url) {
161
+ const sep = url.includes('?') ? '&' : '?';
162
+ return fetch(`${url}${sep}key=${apiKey}`);
163
+ }
164
+ return { projectId, ...buildReadOps(projectId, fetchWithAuth) };
165
+ }
166
+ // ─── Authenticated Writer (service account) ──────────────────────────────────
167
+ /**
168
+ * Create a read-write Firestore client using a service account.
169
+ * Required for publishing — needs service_account.json in the project directory.
170
+ */
171
+ export async function createFirestoreClient(projectDir) {
172
+ const sa = await loadServiceAccount(projectDir);
173
+ const projectId = sa.project_id;
174
+ let token = null;
175
+ let tokenExpiry = 0;
176
+ async function getToken() {
177
+ const now = Date.now();
178
+ if (token && now < tokenExpiry)
179
+ return token;
180
+ token = await getAccessToken(sa, [FIRESTORE_SCOPE, GCS_SCOPE]);
181
+ tokenExpiry = now + 55 * 60 * 1000;
182
+ return token;
183
+ }
184
+ function fetchWithAuth(url) {
185
+ return getToken().then(t => fetch(url, {
186
+ headers: { Authorization: `Bearer ${t}` },
187
+ }));
188
+ }
189
+ const readOps = buildReadOps(projectId, fetchWithAuth);
190
+ async function setPackage(name, data) {
191
+ const t = await getToken();
192
+ const url = docPath(projectId, 'packages', name);
193
+ const body = JSON.stringify({ fields: objectToFields(data) });
194
+ const res = await fetch(url, {
195
+ method: 'PATCH',
196
+ headers: { Authorization: `Bearer ${t}`, 'Content-Type': 'application/json' },
197
+ body,
198
+ });
199
+ if (!res.ok) {
200
+ const text = await res.text();
201
+ throw new Error(`Firestore PATCH packages/${name} failed (${res.status}): ${text}`);
202
+ }
203
+ }
204
+ async function deletePackage(name) {
205
+ const t = await getToken();
206
+ // Delete version subcollection docs first
207
+ const versions = await readOps.listVersionKeys(name);
208
+ for (const ver of versions) {
209
+ const url = docPath(projectId, 'packages', name, 'versions', ver);
210
+ await fetch(url, { method: 'DELETE', headers: { Authorization: `Bearer ${t}` } });
211
+ }
212
+ const url = docPath(projectId, 'packages', name);
213
+ const res = await fetch(url, { method: 'DELETE', headers: { Authorization: `Bearer ${t}` } });
214
+ if (!res.ok && res.status !== 404) {
215
+ const text = await res.text();
216
+ throw new Error(`Firestore DELETE packages/${name} failed (${res.status}): ${text}`);
217
+ }
218
+ }
219
+ async function setVersion(packageName, version, data) {
220
+ const t = await getToken();
221
+ const url = docPath(projectId, 'packages', packageName, 'versions', version);
222
+ const body = JSON.stringify({ fields: objectToFields(data) });
223
+ const res = await fetch(url, {
224
+ method: 'PATCH',
225
+ headers: { Authorization: `Bearer ${t}`, 'Content-Type': 'application/json' },
226
+ body,
227
+ });
228
+ if (!res.ok) {
229
+ const text = await res.text();
230
+ throw new Error(`Firestore PATCH packages/${packageName}/versions/${version} failed (${res.status}): ${text}`);
231
+ }
232
+ }
233
+ return {
234
+ projectId,
235
+ ...readOps,
236
+ setPackage,
237
+ deletePackage,
238
+ setVersion,
239
+ };
240
+ }
@@ -0,0 +1,9 @@
1
+ export interface ServiceAccount {
2
+ client_email: string;
3
+ private_key: string;
4
+ project_id: string;
5
+ }
6
+ export declare const GCS_SCOPE = "https://www.googleapis.com/auth/devstorage.read_write";
7
+ export declare const FIRESTORE_SCOPE = "https://www.googleapis.com/auth/datastore";
8
+ export declare function getAccessToken(sa: ServiceAccount, scopes: string[]): Promise<string>;
9
+ export declare function loadServiceAccount(projectDir: string): Promise<ServiceAccount>;
@@ -0,0 +1,69 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ import * as crypto from 'node:crypto';
4
+ // ─── Scopes ──────────────────────────────────────────────────────────────────
5
+ export const GCS_SCOPE = 'https://www.googleapis.com/auth/devstorage.read_write';
6
+ export const FIRESTORE_SCOPE = 'https://www.googleapis.com/auth/datastore';
7
+ // ─── JWT Auth ────────────────────────────────────────────────────────────────
8
+ function base64url(data) {
9
+ const buf = typeof data === 'string' ? Buffer.from(data) : data;
10
+ return buf.toString('base64url');
11
+ }
12
+ function createJWT(sa, scopes) {
13
+ const now = Math.floor(Date.now() / 1000);
14
+ const header = { alg: 'RS256', typ: 'JWT' };
15
+ const payload = {
16
+ iss: sa.client_email,
17
+ scope: scopes.join(' '),
18
+ aud: 'https://oauth2.googleapis.com/token',
19
+ iat: now,
20
+ exp: now + 3600,
21
+ };
22
+ const segments = `${base64url(JSON.stringify(header))}.${base64url(JSON.stringify(payload))}`;
23
+ const sign = crypto.createSign('RSA-SHA256');
24
+ sign.update(segments);
25
+ const signature = sign.sign(sa.private_key);
26
+ return `${segments}.${base64url(signature)}`;
27
+ }
28
+ export async function getAccessToken(sa, scopes) {
29
+ const jwt = createJWT(sa, scopes);
30
+ const res = await fetch('https://oauth2.googleapis.com/token', {
31
+ method: 'POST',
32
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
33
+ body: `grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=${jwt}`,
34
+ });
35
+ if (!res.ok) {
36
+ const text = await res.text();
37
+ throw new Error(`GCP auth failed (${res.status}): ${text}`);
38
+ }
39
+ const data = await res.json();
40
+ return data.access_token;
41
+ }
42
+ // ─── Credential Resolution ───────────────────────────────────────────────────
43
+ export async function loadServiceAccount(projectDir) {
44
+ // Search order: project dir, cwd, GOOGLE_APPLICATION_CREDENTIALS env
45
+ const candidates = [
46
+ path.join(projectDir, 'service_account.json'),
47
+ path.join(process.cwd(), 'service_account.json'),
48
+ ];
49
+ for (const candidate of candidates) {
50
+ try {
51
+ const text = await fs.readFile(candidate, 'utf-8');
52
+ return JSON.parse(text);
53
+ }
54
+ catch { /* not found */ }
55
+ }
56
+ // Fall back to GOOGLE_APPLICATION_CREDENTIALS
57
+ const envPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
58
+ if (envPath) {
59
+ try {
60
+ const text = await fs.readFile(envPath, 'utf-8');
61
+ return JSON.parse(text);
62
+ }
63
+ catch {
64
+ throw new Error(`Could not read service account from GOOGLE_APPLICATION_CREDENTIALS: ${envPath}`);
65
+ }
66
+ }
67
+ throw new Error('No GCS credentials found. Place service_account.json in project root, ' +
68
+ 'current directory, or set GOOGLE_APPLICATION_CREDENTIALS environment variable.');
69
+ }
package/dist/gcs.d.ts CHANGED
@@ -1,10 +1,5 @@
1
1
  export interface GCSClient {
2
2
  upload(objectPath: string, data: Uint8Array | string, contentType?: string): Promise<void>;
3
3
  download(objectPath: string): Promise<string | null>;
4
- downloadWithGeneration(objectPath: string): Promise<{
5
- data: string;
6
- generation: string;
7
- } | null>;
8
- uploadWithGeneration(objectPath: string, data: string, generation: string | null, contentType?: string): Promise<void>;
9
4
  }
10
5
  export declare function createGCSClient(projectDir: string, bucketUri: string): Promise<GCSClient>;
package/dist/gcs.js CHANGED
@@ -1,69 +1,4 @@
1
- import * as fs from 'node:fs/promises';
2
- import * as path from 'node:path';
3
- import * as crypto from 'node:crypto';
4
- // ─── JWT Auth ────────────────────────────────────────────────────────────────
5
- function base64url(data) {
6
- const buf = typeof data === 'string' ? Buffer.from(data) : data;
7
- return buf.toString('base64url');
8
- }
9
- function createJWT(sa) {
10
- const now = Math.floor(Date.now() / 1000);
11
- const header = { alg: 'RS256', typ: 'JWT' };
12
- const payload = {
13
- iss: sa.client_email,
14
- scope: 'https://www.googleapis.com/auth/devstorage.read_write',
15
- aud: 'https://oauth2.googleapis.com/token',
16
- iat: now,
17
- exp: now + 3600,
18
- };
19
- const segments = `${base64url(JSON.stringify(header))}.${base64url(JSON.stringify(payload))}`;
20
- const sign = crypto.createSign('RSA-SHA256');
21
- sign.update(segments);
22
- const signature = sign.sign(sa.private_key);
23
- return `${segments}.${base64url(signature)}`;
24
- }
25
- async function getAccessToken(sa) {
26
- const jwt = createJWT(sa);
27
- const res = await fetch('https://oauth2.googleapis.com/token', {
28
- method: 'POST',
29
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
30
- body: `grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=${jwt}`,
31
- });
32
- if (!res.ok) {
33
- const text = await res.text();
34
- throw new Error(`GCS auth failed (${res.status}): ${text}`);
35
- }
36
- const data = await res.json();
37
- return data.access_token;
38
- }
39
- // ─── Credential Resolution ───────────────────────────────────────────────────
40
- async function loadServiceAccount(projectDir) {
41
- // Search order: project dir, cwd, GOOGLE_APPLICATION_CREDENTIALS env
42
- const candidates = [
43
- path.join(projectDir, 'service_account.json'),
44
- path.join(process.cwd(), 'service_account.json'),
45
- ];
46
- for (const candidate of candidates) {
47
- try {
48
- const text = await fs.readFile(candidate, 'utf-8');
49
- return JSON.parse(text);
50
- }
51
- catch { /* not found */ }
52
- }
53
- // Fall back to GOOGLE_APPLICATION_CREDENTIALS
54
- const envPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
55
- if (envPath) {
56
- try {
57
- const text = await fs.readFile(envPath, 'utf-8');
58
- return JSON.parse(text);
59
- }
60
- catch {
61
- throw new Error(`Could not read service account from GOOGLE_APPLICATION_CREDENTIALS: ${envPath}`);
62
- }
63
- }
64
- throw new Error('No GCS credentials found. Place service_account.json in project root, ' +
65
- 'current directory, or set GOOGLE_APPLICATION_CREDENTIALS environment variable.');
66
- }
1
+ import { loadServiceAccount, getAccessToken, GCS_SCOPE } from './gcp-auth.js';
67
2
  // ─── Bucket Name Parsing ─────────────────────────────────────────────────────
68
3
  function parseBucket(bucketUri) {
69
4
  // Accept "gs://bucket-name" or just "bucket-name"
@@ -74,7 +9,7 @@ function parseBucket(bucketUri) {
74
9
  // ─── GCS Client Factory ─────────────────────────────────────────────────────
75
10
  export async function createGCSClient(projectDir, bucketUri) {
76
11
  const sa = await loadServiceAccount(projectDir);
77
- const token = await getAccessToken(sa);
12
+ const token = await getAccessToken(sa, [GCS_SCOPE]);
78
13
  const bucket = parseBucket(bucketUri);
79
14
  const apiBase = `https://storage.googleapis.com`;
80
15
  return {
@@ -109,50 +44,5 @@ export async function createGCSClient(projectDir, bucketUri) {
109
44
  }
110
45
  return res.text();
111
46
  },
112
- async downloadWithGeneration(objectPath) {
113
- const encoded = encodeURIComponent(objectPath);
114
- const url = `${apiBase}/storage/v1/b/${bucket}/o/${encoded}?alt=media`;
115
- const res = await fetch(url, {
116
- headers: { Authorization: `Bearer ${token}` },
117
- });
118
- if (res.status === 404)
119
- return null;
120
- if (!res.ok) {
121
- const text = await res.text();
122
- throw new Error(`GCS download failed for ${objectPath} (${res.status}): ${text}`);
123
- }
124
- const data = await res.text();
125
- const generation = res.headers.get('x-goog-generation') ?? '0';
126
- return { data, generation };
127
- },
128
- async uploadWithGeneration(objectPath, data, generation, contentType = 'application/json') {
129
- const encoded = encodeURIComponent(objectPath);
130
- let url = `${apiBase}/upload/storage/v1/b/${bucket}/o?uploadType=media&name=${encoded}`;
131
- const headers = {
132
- Authorization: `Bearer ${token}`,
133
- 'Content-Type': contentType,
134
- };
135
- // Optimistic concurrency: if we have a generation, require it to match
136
- if (generation) {
137
- headers['x-goog-if-generation-match'] = generation;
138
- }
139
- else {
140
- // Object should not exist yet
141
- headers['x-goog-if-generation-match'] = '0';
142
- }
143
- const res = await fetch(url, {
144
- method: 'POST',
145
- headers,
146
- body: data,
147
- });
148
- if (res.status === 412) {
149
- throw new Error(`Registry was modified by another publish while uploading. ` +
150
- `Please retry the publish command.`);
151
- }
152
- if (!res.ok) {
153
- const text = await res.text();
154
- throw new Error(`GCS upload failed for ${objectPath} (${res.status}): ${text}`);
155
- }
156
- },
157
47
  };
158
48
  }
package/dist/index.d.ts CHANGED
@@ -30,5 +30,7 @@ export { syncRuntimeAssets } from './sync-runtime-assets.js';
30
30
  export { publishPackage } from './publish.js';
31
31
  export type { PublishOptions, PublishResult } from './publish.js';
32
32
  export * from './assets-package.js';
33
- export { fetchRegistry, searchAssets, listPackages, resolveAssetDeps } from './store.js';
33
+ export { searchAssets, listStorePackages, resolveAssetDeps } from './store.js';
34
34
  export type { SearchResult, PackageInfo } from './store.js';
35
+ export { createFirestoreReader, createFirestoreClient } from './firestore.js';
36
+ export type { FirestoreReader, FirestoreClient, FirestorePackageDoc, FirestoreVersionDoc } from './firestore.js';
package/dist/index.js CHANGED
@@ -39,4 +39,6 @@ export { syncRuntimeAssets } from './sync-runtime-assets.js';
39
39
  export { publishPackage } from './publish.js';
40
40
  export * from './assets-package.js';
41
41
  // Store
42
- export { fetchRegistry, searchAssets, listPackages, resolveAssetDeps } from './store.js';
42
+ export { searchAssets, listStorePackages, resolveAssetDeps } from './store.js';
43
+ // Firestore
44
+ export { createFirestoreReader, createFirestoreClient } from './firestore.js';