@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.
- package/dist/assets-package.d.ts +0 -14
- package/dist/assets-package.js +1 -4
- package/dist/firestore.d.ts +42 -0
- package/dist/firestore.js +240 -0
- package/dist/gcp-auth.d.ts +9 -0
- package/dist/gcp-auth.js +69 -0
- package/dist/gcs.d.ts +0 -5
- package/dist/gcs.js +2 -112
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/init.js +121 -10
- package/dist/publish.js +17 -40
- package/dist/server.js +42 -35
- package/dist/store.d.ts +6 -7
- package/dist/store.js +36 -39
- package/package.json +3 -2
- package/templates/engine/index.html +12 -0
- package/templates/engine/package.json +32 -0
- package/templates/engine/prefabs/build.json +1 -0
- package/templates/engine/scripts/copy-decoders.mjs +40 -0
- package/templates/engine/src/Game.ts +84 -0
- package/templates/engine/src/Prefabs.ts +28 -0
- package/templates/engine/src/main.ts +16 -0
- package/templates/engine/src/styles/main.css +22 -0
- package/templates/engine/stubs/capacitor.ts +6 -0
- package/templates/engine/tsconfig.json +27 -0
- package/templates/engine/vite-env.d.ts +1 -0
- package/templates/engine/vite.config.ts +39 -0
package/dist/init.js
CHANGED
|
@@ -95,6 +95,7 @@ export async function initProject(projectDir, opts) {
|
|
|
95
95
|
const stowkitIgnores = [
|
|
96
96
|
'# StowKit',
|
|
97
97
|
'*.stowcache',
|
|
98
|
+
'.stow-thumbnails/',
|
|
98
99
|
'public/cdn-assets/',
|
|
99
100
|
'Open Packer.bat',
|
|
100
101
|
'open-packer.sh',
|
|
@@ -104,6 +105,16 @@ export async function initProject(projectDir, opts) {
|
|
|
104
105
|
if (!existing.includes('*.stowcache')) {
|
|
105
106
|
await fs.writeFile(gitignorePath, existing.trimEnd() + '\n\n' + stowkitIgnores + '\n');
|
|
106
107
|
}
|
|
108
|
+
else {
|
|
109
|
+
// Existing project — ensure newer ignore entries are present
|
|
110
|
+
let updated = existing;
|
|
111
|
+
if (!existing.includes('.stow-thumbnails/')) {
|
|
112
|
+
updated = updated.trimEnd() + '\n.stow-thumbnails/\n';
|
|
113
|
+
}
|
|
114
|
+
if (updated !== existing) {
|
|
115
|
+
await fs.writeFile(gitignorePath, updated);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
107
118
|
}
|
|
108
119
|
catch {
|
|
109
120
|
await fs.writeFile(gitignorePath, stowkitIgnores + '\n');
|
|
@@ -118,35 +129,135 @@ export async function initProject(projectDir, opts) {
|
|
|
118
129
|
console.log(` Config: .felicityproject`);
|
|
119
130
|
console.log(` AI skills: .claude/skills/stowkit/SKILL.md, .cursor/rules/stowkit.mdc`);
|
|
120
131
|
console.log(` Launcher: Open Packer.bat (Windows) / open-packer.sh (macOS/Linux)`);
|
|
132
|
+
// Add engine-specific gitignore entries
|
|
133
|
+
if (withEngine) {
|
|
134
|
+
const engineIgnores = [
|
|
135
|
+
'# Engine (generated by postinstall)',
|
|
136
|
+
'node_modules/',
|
|
137
|
+
'dist/',
|
|
138
|
+
'public/basis/',
|
|
139
|
+
'public/stowkit/',
|
|
140
|
+
'public/stowkit_reader.wasm',
|
|
141
|
+
].join('\n');
|
|
142
|
+
try {
|
|
143
|
+
const existing = await fs.readFile(gitignorePath, 'utf-8');
|
|
144
|
+
if (!existing.includes('public/stowkit_reader.wasm')) {
|
|
145
|
+
await fs.writeFile(gitignorePath, existing.trimEnd() + '\n\n' + engineIgnores + '\n');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// gitignore should exist from earlier step, but handle gracefully
|
|
150
|
+
}
|
|
151
|
+
}
|
|
121
152
|
// Install engine if selected
|
|
122
153
|
if (withEngine) {
|
|
123
154
|
await installEngine(absDir);
|
|
124
155
|
}
|
|
125
156
|
console.log('');
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
157
|
+
if (withEngine) {
|
|
158
|
+
console.log('Ready to go! Run:');
|
|
159
|
+
console.log(' npm run dev');
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
console.log('Drop your assets (PNG, JPG, FBX, WAV, etc.) into assets/');
|
|
163
|
+
console.log('Then run: stowkit build');
|
|
164
|
+
console.log('Or double-click "Open Packer.bat" to launch the packer GUI.');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function copyTemplateFiles(templateDir, targetDir) {
|
|
168
|
+
const copied = [];
|
|
169
|
+
const entries = await fs.readdir(templateDir, { withFileTypes: true });
|
|
170
|
+
for (const entry of entries) {
|
|
171
|
+
const src = path.join(templateDir, entry.name);
|
|
172
|
+
const dest = path.join(targetDir, entry.name);
|
|
173
|
+
if (entry.isDirectory()) {
|
|
174
|
+
const sub = await copyTemplateFiles(src, dest);
|
|
175
|
+
copied.push(...sub);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
// Skip package.json — handled separately via merge logic
|
|
179
|
+
if (entry.name === 'package.json')
|
|
180
|
+
continue;
|
|
181
|
+
try {
|
|
182
|
+
await fs.access(dest);
|
|
183
|
+
// File exists — don't clobber
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
187
|
+
await fs.copyFile(src, dest);
|
|
188
|
+
copied.push(path.relative(targetDir, dest));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return copied;
|
|
193
|
+
}
|
|
194
|
+
async function mergePackageJson(templatePkgPath, targetDir) {
|
|
195
|
+
const templatePkg = JSON.parse(await fs.readFile(templatePkgPath, 'utf-8'));
|
|
196
|
+
const targetPkgPath = path.join(targetDir, 'package.json');
|
|
197
|
+
let targetPkg;
|
|
198
|
+
try {
|
|
199
|
+
targetPkg = JSON.parse(await fs.readFile(targetPkgPath, 'utf-8'));
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// No existing package.json — use template as-is
|
|
203
|
+
templatePkg.name = path.basename(targetDir);
|
|
204
|
+
await fs.writeFile(targetPkgPath, JSON.stringify(templatePkg, null, 2) + '\n');
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
// Merge scripts (don't overwrite existing scripts)
|
|
208
|
+
targetPkg.scripts = { ...templatePkg.scripts, ...targetPkg.scripts };
|
|
209
|
+
// Ensure postinstall includes copy-decoders
|
|
210
|
+
if (!targetPkg.scripts.postinstall?.includes('copy-decoders')) {
|
|
211
|
+
targetPkg.scripts.postinstall = templatePkg.scripts.postinstall;
|
|
212
|
+
}
|
|
213
|
+
// Merge dependencies
|
|
214
|
+
for (const depType of ['dependencies', 'devDependencies', 'peerDependencies']) {
|
|
215
|
+
if (templatePkg[depType]) {
|
|
216
|
+
targetPkg[depType] = { ...templatePkg[depType], ...(targetPkg[depType] ?? {}) };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Ensure type: module
|
|
220
|
+
if (!targetPkg.type)
|
|
221
|
+
targetPkg.type = 'module';
|
|
222
|
+
await fs.writeFile(targetPkgPath, JSON.stringify(targetPkg, null, 2) + '\n');
|
|
129
223
|
}
|
|
130
224
|
async function installEngine(absDir) {
|
|
225
|
+
const thisDir = path.dirname(fileURLToPath(import.meta.url));
|
|
226
|
+
const templateDir = path.resolve(thisDir, '../templates/engine');
|
|
227
|
+
// Verify template directory exists
|
|
228
|
+
try {
|
|
229
|
+
await fs.access(templateDir);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
console.error('Engine template not found. Please update @series-inc/stowkit-cli.');
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
131
235
|
console.log('');
|
|
132
|
-
console.log('
|
|
236
|
+
console.log('Setting up 3D engine project...');
|
|
237
|
+
// Copy template files (skip existing, skip package.json)
|
|
238
|
+
const copied = await copyTemplateFiles(templateDir, absDir);
|
|
239
|
+
if (copied.length > 0) {
|
|
240
|
+
console.log(` Created: ${copied.join(', ')}`);
|
|
241
|
+
}
|
|
242
|
+
// Merge or create package.json
|
|
243
|
+
await mergePackageJson(path.join(templateDir, 'package.json'), absDir);
|
|
244
|
+
// Install dependencies
|
|
245
|
+
console.log(' Installing dependencies...');
|
|
133
246
|
const { execSync } = await import('node:child_process');
|
|
134
247
|
try {
|
|
135
|
-
execSync('npm install
|
|
248
|
+
execSync('npm install', {
|
|
136
249
|
cwd: absDir,
|
|
137
250
|
stdio: 'inherit',
|
|
138
251
|
});
|
|
139
252
|
}
|
|
140
253
|
catch {
|
|
141
|
-
console.error('Failed to install
|
|
142
|
-
console.error(' npm install @series-inc/rundot-3d-engine three');
|
|
254
|
+
console.error('Failed to install dependencies. Run `npm install` manually.');
|
|
143
255
|
return;
|
|
144
256
|
}
|
|
257
|
+
// Copy engine skill files
|
|
145
258
|
await copyEngineSkillFiles(absDir);
|
|
146
259
|
console.log('');
|
|
147
|
-
console.log(' 3D Engine
|
|
148
|
-
console.log(' @series-inc/rundot-3d-engine');
|
|
149
|
-
console.log(' three');
|
|
260
|
+
console.log(' 3D Engine project ready!');
|
|
150
261
|
console.log(' AI skills: .claude/skills/stowkit-engine/SKILL.md, .cursor/rules/stowkit-engine.mdc');
|
|
151
262
|
}
|
|
152
263
|
async function copySkillFiles(absDir) {
|
package/dist/publish.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as fs from 'node:fs/promises';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import { readProjectConfig, scanDirectory } from './node-fs.js';
|
|
4
|
-
import { readAssetsPackage, initAssetsPackage,
|
|
4
|
+
import { readAssetsPackage, initAssetsPackage, } from './assets-package.js';
|
|
5
|
+
import { createFirestoreClient } from './firestore.js';
|
|
5
6
|
import { createGCSClient } from './gcs.js';
|
|
6
7
|
import { readStowmeta } from './app/stowmeta-io.js';
|
|
7
8
|
// ─── Build full dependency graph from srcArtDir ──────────────────────────────
|
|
@@ -145,7 +146,7 @@ export async function publishPackage(projectDir, opts = {}) {
|
|
|
145
146
|
const bucketUri = opts.bucket
|
|
146
147
|
?? pkg.bucket
|
|
147
148
|
?? process.env.STOWKIT_BUCKET
|
|
148
|
-
?? 'gs://
|
|
149
|
+
?? 'gs://rungame-shared-assets-test';
|
|
149
150
|
log(`Bucket: ${bucketUri}`);
|
|
150
151
|
// Step 4: Scan srcArtDir
|
|
151
152
|
const scan = await scanDirectory(project.srcArtDir);
|
|
@@ -252,29 +253,15 @@ export async function publishPackage(projectDir, opts = {}) {
|
|
|
252
253
|
}
|
|
253
254
|
// Step 7: Create GCS client and auth
|
|
254
255
|
const emitProgress = opts.onProgress ?? (() => { });
|
|
255
|
-
const totalUploads = filesToUpload.length + Object.keys(thumbMap).length + 2; // +2 for assets-package.json and
|
|
256
|
+
const totalUploads = filesToUpload.length + Object.keys(thumbMap).length + 2; // +2 for assets-package.json and Firestore writes
|
|
256
257
|
let totalDone = 0;
|
|
257
258
|
log('Authenticating with GCS...');
|
|
258
259
|
emitProgress({ phase: 'auth', done: 0, total: totalUploads, message: 'Authenticating with GCS...' });
|
|
259
260
|
const gcs = await createGCSClient(projectDir, bucketUri);
|
|
260
|
-
// Step 8:
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
let generation;
|
|
265
|
-
if (registryResult) {
|
|
266
|
-
registry = JSON.parse(registryResult.data);
|
|
267
|
-
generation = registryResult.generation;
|
|
268
|
-
vlog(`Registry loaded (generation: ${generation})`);
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
271
|
-
registry = createEmptyRegistry();
|
|
272
|
-
generation = null;
|
|
273
|
-
vlog('No existing registry — will create new one');
|
|
274
|
-
}
|
|
275
|
-
// Step 9: Check version not already published
|
|
276
|
-
const existingPkg = registry.packages[pkg.name];
|
|
277
|
-
if (existingPkg?.versions[pkg.version] && !opts.force) {
|
|
261
|
+
// Step 8: Create Firestore client and check version
|
|
262
|
+
const firestore = await createFirestoreClient(projectDir);
|
|
263
|
+
const existingVer = await firestore.getVersion(pkg.name, pkg.version);
|
|
264
|
+
if (existingVer && !opts.force) {
|
|
278
265
|
throw new Error(`Version ${pkg.version} of "${pkg.name}" is already published. ` +
|
|
279
266
|
`Bump the version in assets-package.json or use --force to overwrite.`);
|
|
280
267
|
}
|
|
@@ -338,27 +325,17 @@ export async function publishPackage(projectDir, opts = {}) {
|
|
|
338
325
|
}
|
|
339
326
|
catch { /* file doesn't exist, try next */ }
|
|
340
327
|
}
|
|
341
|
-
// Step 13:
|
|
328
|
+
// Step 13: Write to Firestore
|
|
342
329
|
log('Updating registry...');
|
|
343
|
-
if (!registry.packages[pkg.name]) {
|
|
344
|
-
registry.packages[pkg.name] = {
|
|
345
|
-
description: pkg.description,
|
|
346
|
-
author: pkg.author,
|
|
347
|
-
tags: pkg.tags ?? [],
|
|
348
|
-
latest: pkg.version,
|
|
349
|
-
versions: {},
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
const regPkg = registry.packages[pkg.name];
|
|
353
|
-
regPkg.versions[pkg.version] = versionEntry;
|
|
354
|
-
regPkg.latest = pkg.version;
|
|
355
|
-
regPkg.description = pkg.description;
|
|
356
|
-
regPkg.author = pkg.author;
|
|
357
|
-
regPkg.tags = pkg.tags ?? [];
|
|
358
|
-
if (packThumbnail)
|
|
359
|
-
regPkg.thumbnail = packThumbnail;
|
|
360
330
|
emitProgress({ phase: 'registry', done: totalDone, total: totalUploads, message: 'Updating registry...' });
|
|
361
|
-
await
|
|
331
|
+
await firestore.setVersion(pkg.name, pkg.version, versionEntry);
|
|
332
|
+
await firestore.setPackage(pkg.name, {
|
|
333
|
+
description: pkg.description,
|
|
334
|
+
author: pkg.author,
|
|
335
|
+
tags: pkg.tags ?? [],
|
|
336
|
+
latest: pkg.version,
|
|
337
|
+
thumbnail: packThumbnail ?? null,
|
|
338
|
+
});
|
|
362
339
|
totalDone++;
|
|
363
340
|
emitProgress({ phase: 'registry', done: totalDone, total: totalUploads, message: 'Done' });
|
|
364
341
|
log(`\nPublished ${pkg.name}@${pkg.version} successfully!`);
|
package/dist/server.js
CHANGED
|
@@ -1780,21 +1780,28 @@ async function handleRequest(req, res, staticApps) {
|
|
|
1780
1780
|
// GET /api/asset-store/registry — fetch the public registry
|
|
1781
1781
|
if (pathname === '/api/asset-store/registry' && req.method === 'GET') {
|
|
1782
1782
|
try {
|
|
1783
|
-
const
|
|
1784
|
-
const
|
|
1785
|
-
const
|
|
1786
|
-
const
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1783
|
+
const { createFirestoreReader } = await import('./firestore.js');
|
|
1784
|
+
const reader = createFirestoreReader();
|
|
1785
|
+
const allPackages = await reader.listPackages();
|
|
1786
|
+
const packages = {};
|
|
1787
|
+
for (const { name, data: pkg } of allPackages) {
|
|
1788
|
+
const versionKeys = await reader.listVersionKeys(name);
|
|
1789
|
+
const versions = {};
|
|
1790
|
+
for (const ver of versionKeys) {
|
|
1791
|
+
const versionDoc = await reader.getVersion(name, ver);
|
|
1792
|
+
if (versionDoc)
|
|
1793
|
+
versions[ver] = versionDoc;
|
|
1790
1794
|
}
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
+
packages[name] = {
|
|
1796
|
+
description: pkg.description,
|
|
1797
|
+
author: pkg.author,
|
|
1798
|
+
tags: pkg.tags ?? [],
|
|
1799
|
+
latest: pkg.latest,
|
|
1800
|
+
thumbnail: pkg.thumbnail,
|
|
1801
|
+
versions,
|
|
1802
|
+
};
|
|
1795
1803
|
}
|
|
1796
|
-
|
|
1797
|
-
json(res, registry);
|
|
1804
|
+
json(res, { schemaVersion: 1, packages });
|
|
1798
1805
|
}
|
|
1799
1806
|
catch (err) {
|
|
1800
1807
|
json(res, { error: err.message }, 500);
|
|
@@ -1810,9 +1817,11 @@ async function handleRequest(req, res, staticApps) {
|
|
|
1810
1817
|
const bucketParam = url.searchParams.get('bucket') ?? undefined;
|
|
1811
1818
|
const limitParam = url.searchParams.get('limit');
|
|
1812
1819
|
const limit = limitParam ? parseInt(limitParam, 10) : undefined;
|
|
1813
|
-
const {
|
|
1814
|
-
const
|
|
1815
|
-
|
|
1820
|
+
const { createFirestoreReader } = await import('./firestore.js');
|
|
1821
|
+
const { searchAssets } = await import('./store.js');
|
|
1822
|
+
const reader = createFirestoreReader();
|
|
1823
|
+
const bucket = (bucketParam ?? 'rungame-shared-assets-test').replace(/^gs:\/\//, '').replace(/\/$/, '');
|
|
1824
|
+
let results = await searchAssets(reader, bucket, query, { type, package: pkg });
|
|
1816
1825
|
if (limit && limit > 0)
|
|
1817
1826
|
results = results.slice(0, limit);
|
|
1818
1827
|
json(res, results);
|
|
@@ -1826,9 +1835,11 @@ async function handleRequest(req, res, staticApps) {
|
|
|
1826
1835
|
if (pathname === '/api/asset-store/packages' && req.method === 'GET') {
|
|
1827
1836
|
try {
|
|
1828
1837
|
const bucketParam = url.searchParams.get('bucket') ?? undefined;
|
|
1829
|
-
const {
|
|
1830
|
-
const
|
|
1831
|
-
const
|
|
1838
|
+
const { createFirestoreReader } = await import('./firestore.js');
|
|
1839
|
+
const { listStorePackages } = await import('./store.js');
|
|
1840
|
+
const reader = createFirestoreReader();
|
|
1841
|
+
const bucket = (bucketParam ?? 'rungame-shared-assets-test').replace(/^gs:\/\//, '').replace(/\/$/, '');
|
|
1842
|
+
const packages = await listStorePackages(reader, bucket);
|
|
1832
1843
|
json(res, packages);
|
|
1833
1844
|
}
|
|
1834
1845
|
catch (err) {
|
|
@@ -1840,22 +1851,22 @@ async function handleRequest(req, res, staticApps) {
|
|
|
1840
1851
|
if (pathname.startsWith('/api/asset-store/package/') && req.method === 'GET') {
|
|
1841
1852
|
try {
|
|
1842
1853
|
const packageName = decodeURIComponent(pathname.slice('/api/asset-store/package/'.length));
|
|
1843
|
-
const
|
|
1844
|
-
const
|
|
1845
|
-
const
|
|
1846
|
-
const pkg = registry.packages[packageName];
|
|
1854
|
+
const { createFirestoreReader } = await import('./firestore.js');
|
|
1855
|
+
const reader = createFirestoreReader();
|
|
1856
|
+
const pkg = await reader.getPackage(packageName);
|
|
1847
1857
|
if (!pkg) {
|
|
1848
1858
|
json(res, { error: `Package "${packageName}" not found` }, 404);
|
|
1849
1859
|
return;
|
|
1850
1860
|
}
|
|
1851
|
-
const ver =
|
|
1861
|
+
const ver = await reader.getVersion(packageName, pkg.latest);
|
|
1862
|
+
const versionKeys = await reader.listVersionKeys(packageName);
|
|
1852
1863
|
json(res, {
|
|
1853
1864
|
name: packageName,
|
|
1854
1865
|
description: pkg.description,
|
|
1855
1866
|
author: pkg.author,
|
|
1856
1867
|
tags: pkg.tags ?? [],
|
|
1857
1868
|
latest: pkg.latest,
|
|
1858
|
-
versions:
|
|
1869
|
+
versions: versionKeys,
|
|
1859
1870
|
assets: ver?.assets ?? [],
|
|
1860
1871
|
});
|
|
1861
1872
|
}
|
|
@@ -1877,21 +1888,17 @@ async function handleRequest(req, res, staticApps) {
|
|
|
1877
1888
|
json(res, { error: 'Missing packageName, version, or stringIds' }, 400);
|
|
1878
1889
|
return;
|
|
1879
1890
|
}
|
|
1880
|
-
const bucketName = (bucketParam ?? '
|
|
1891
|
+
const bucketName = (bucketParam ?? 'rungame-shared-assets-test').replace(/^gs:\/\//, '').replace(/\/$/, '');
|
|
1881
1892
|
const baseUrl = `https://storage.googleapis.com/${bucketName}`;
|
|
1882
|
-
//
|
|
1883
|
-
const
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
return;
|
|
1887
|
-
}
|
|
1888
|
-
const registry = await regRes.json();
|
|
1889
|
-
const pkg = registry.packages[packageName];
|
|
1893
|
+
// Resolve deps via Firestore
|
|
1894
|
+
const { createFirestoreReader } = await import('./firestore.js');
|
|
1895
|
+
const reader = createFirestoreReader();
|
|
1896
|
+
const pkg = await reader.getPackage(packageName);
|
|
1890
1897
|
if (!pkg) {
|
|
1891
1898
|
json(res, { error: `Package "${packageName}" not found` }, 404);
|
|
1892
1899
|
return;
|
|
1893
1900
|
}
|
|
1894
|
-
const ver =
|
|
1901
|
+
const ver = await reader.getVersion(packageName, version);
|
|
1895
1902
|
if (!ver) {
|
|
1896
1903
|
json(res, { error: `Version "${version}" not found` }, 404);
|
|
1897
1904
|
return;
|
package/dist/store.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function fetchRegistry(bucket?: string): Promise<Registry>;
|
|
1
|
+
import type { FirestoreReader } from './firestore.js';
|
|
3
2
|
export interface SearchResult {
|
|
4
3
|
stringId: string;
|
|
5
4
|
type: string;
|
|
@@ -27,15 +26,15 @@ export interface PackageInfo {
|
|
|
27
26
|
/** Full URL to pack-level thumbnail, if uploaded */
|
|
28
27
|
thumbnailUrl?: string;
|
|
29
28
|
}
|
|
30
|
-
export declare function searchAssets(
|
|
29
|
+
export declare function searchAssets(firestore: FirestoreReader, bucket: string, query: string, opts?: {
|
|
31
30
|
type?: string;
|
|
32
31
|
package?: string;
|
|
33
|
-
}): SearchResult[]
|
|
34
|
-
export declare function
|
|
35
|
-
export declare function resolveAssetDeps(
|
|
32
|
+
}): Promise<SearchResult[]>;
|
|
33
|
+
export declare function listStorePackages(firestore: FirestoreReader, bucket: string): Promise<PackageInfo[]>;
|
|
34
|
+
export declare function resolveAssetDeps(firestore: FirestoreReader, packageName: string, stringIds: string[], version?: string): Promise<{
|
|
36
35
|
resolvedIds: string[];
|
|
37
36
|
files: string[];
|
|
38
|
-
}
|
|
37
|
+
}>;
|
|
39
38
|
export declare function storeSearch(query: string, opts?: {
|
|
40
39
|
type?: string;
|
|
41
40
|
json?: boolean;
|
package/dist/store.js
CHANGED
|
@@ -1,31 +1,21 @@
|
|
|
1
1
|
import { resolveTransitiveDeps, resolveFiles } from './assets-package.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export async function fetchRegistry(bucket) {
|
|
5
|
-
const bucketName = (bucket ?? DEFAULT_BUCKET).replace(/^gs:\/\//, '').replace(/\/$/, '');
|
|
6
|
-
const url = `https://storage.googleapis.com/${bucketName}/registry.json?t=${Date.now()}`;
|
|
7
|
-
const res = await fetch(url);
|
|
8
|
-
if (!res.ok) {
|
|
9
|
-
if (res.status === 404)
|
|
10
|
-
return { schemaVersion: 1, packages: {} };
|
|
11
|
-
throw new Error(`Failed to fetch registry: ${res.status}`);
|
|
12
|
-
}
|
|
13
|
-
return res.json();
|
|
14
|
-
}
|
|
2
|
+
import { createFirestoreReader } from './firestore.js';
|
|
3
|
+
const DEFAULT_BUCKET = 'rungame-shared-assets-test';
|
|
15
4
|
// ─── Search ──────────────────────────────────────────────────────────────────
|
|
16
|
-
export function searchAssets(
|
|
5
|
+
export async function searchAssets(firestore, bucket, query, opts) {
|
|
17
6
|
// Support comma-separated terms: "coral, sea, ocean" matches any term
|
|
18
7
|
const terms = query.split(',').map(t => t.toLowerCase().trim()).filter(Boolean);
|
|
19
8
|
const scored = [];
|
|
20
|
-
const bucket = DEFAULT_BUCKET;
|
|
21
9
|
// When searching within a specific package, skip package-level metadata
|
|
22
10
|
// (name, description, tags) — it matches every asset and adds noise.
|
|
23
11
|
const skipPkgMeta = !!opts?.package;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
12
|
+
let packages = await firestore.listPackages();
|
|
13
|
+
if (opts?.package) {
|
|
14
|
+
packages = packages.filter(p => p.name === opts.package);
|
|
15
|
+
}
|
|
16
|
+
for (const { name: pkgName, data: pkg } of packages) {
|
|
27
17
|
const verStr = pkg.latest;
|
|
28
|
-
const ver =
|
|
18
|
+
const ver = await firestore.getVersion(pkgName, verStr);
|
|
29
19
|
if (!ver)
|
|
30
20
|
continue;
|
|
31
21
|
for (const asset of ver.assets) {
|
|
@@ -186,33 +176,36 @@ function scoreAsset(terms, asset, pkg, pkgName, skipPkgMeta = false) {
|
|
|
186
176
|
return total;
|
|
187
177
|
}
|
|
188
178
|
// ─── List Packages ───────────────────────────────────────────────────────────
|
|
189
|
-
export function
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
179
|
+
export async function listStorePackages(firestore, bucket) {
|
|
180
|
+
const packages = await firestore.listPackages();
|
|
181
|
+
const results = [];
|
|
182
|
+
for (const { name, data: pkg } of packages) {
|
|
183
|
+
const ver = await firestore.getVersion(name, pkg.latest);
|
|
184
|
+
const versionKeys = await firestore.listVersionKeys(name);
|
|
193
185
|
const thumbnailUrl = pkg.thumbnail
|
|
194
186
|
? `https://storage.googleapis.com/${bucket}/packages/${name}/${pkg.latest}/${pkg.thumbnail}`
|
|
195
187
|
: undefined;
|
|
196
|
-
|
|
188
|
+
results.push({
|
|
197
189
|
name,
|
|
198
190
|
description: pkg.description,
|
|
199
191
|
author: pkg.author,
|
|
200
192
|
tags: pkg.tags ?? [],
|
|
201
193
|
latest: pkg.latest,
|
|
202
|
-
versions:
|
|
194
|
+
versions: versionKeys,
|
|
203
195
|
assetCount: ver?.assets.length ?? 0,
|
|
204
196
|
totalSize: ver?.totalSize ?? 0,
|
|
205
197
|
thumbnailUrl,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
return results;
|
|
208
201
|
}
|
|
209
202
|
// ─── Resolve Dependencies ────────────────────────────────────────────────────
|
|
210
|
-
export function resolveAssetDeps(
|
|
211
|
-
const pkg =
|
|
203
|
+
export async function resolveAssetDeps(firestore, packageName, stringIds, version) {
|
|
204
|
+
const pkg = await firestore.getPackage(packageName);
|
|
212
205
|
if (!pkg)
|
|
213
206
|
throw new Error(`Package "${packageName}" not found`);
|
|
214
207
|
const verStr = version ?? pkg.latest;
|
|
215
|
-
const ver =
|
|
208
|
+
const ver = await firestore.getVersion(packageName, verStr);
|
|
216
209
|
if (!ver)
|
|
217
210
|
throw new Error(`Version "${verStr}" not found`);
|
|
218
211
|
const resolvedIds = resolveTransitiveDeps(stringIds, ver.assets);
|
|
@@ -221,8 +214,9 @@ export function resolveAssetDeps(registry, packageName, stringIds, version) {
|
|
|
221
214
|
}
|
|
222
215
|
// ─── CLI Commands ────────────────────────────────────────────────────────────
|
|
223
216
|
export async function storeSearch(query, opts) {
|
|
224
|
-
const
|
|
225
|
-
|
|
217
|
+
const bucket = (opts?.bucket ?? DEFAULT_BUCKET).replace(/^gs:\/\//, '').replace(/\/$/, '');
|
|
218
|
+
const firestore = createFirestoreReader();
|
|
219
|
+
let results = await searchAssets(firestore, bucket, query, { type: opts?.type });
|
|
226
220
|
if (opts?.limit && opts.limit > 0)
|
|
227
221
|
results = results.slice(0, opts.limit);
|
|
228
222
|
if (opts?.json) {
|
|
@@ -244,8 +238,9 @@ export async function storeSearch(query, opts) {
|
|
|
244
238
|
console.log('');
|
|
245
239
|
}
|
|
246
240
|
export async function storeList(opts) {
|
|
247
|
-
const
|
|
248
|
-
const
|
|
241
|
+
const bucket = (opts?.bucket ?? DEFAULT_BUCKET).replace(/^gs:\/\//, '').replace(/\/$/, '');
|
|
242
|
+
const firestore = createFirestoreReader();
|
|
243
|
+
const packages = await listStorePackages(firestore, bucket);
|
|
249
244
|
if (opts?.json) {
|
|
250
245
|
console.log(JSON.stringify(packages, null, 2));
|
|
251
246
|
return;
|
|
@@ -264,13 +259,15 @@ export async function storeList(opts) {
|
|
|
264
259
|
console.log('');
|
|
265
260
|
}
|
|
266
261
|
export async function storeInfo(packageName, opts) {
|
|
267
|
-
const
|
|
268
|
-
const
|
|
262
|
+
const bucket = (opts?.bucket ?? DEFAULT_BUCKET).replace(/^gs:\/\//, '').replace(/\/$/, '');
|
|
263
|
+
const firestore = createFirestoreReader();
|
|
264
|
+
const pkg = await firestore.getPackage(packageName);
|
|
269
265
|
if (!pkg) {
|
|
270
266
|
console.error(`Package "${packageName}" not found.`);
|
|
271
267
|
process.exit(1);
|
|
272
268
|
}
|
|
273
|
-
const ver =
|
|
269
|
+
const ver = await firestore.getVersion(packageName, pkg.latest);
|
|
270
|
+
const versionKeys = await firestore.listVersionKeys(packageName);
|
|
274
271
|
if (opts?.json) {
|
|
275
272
|
console.log(JSON.stringify({
|
|
276
273
|
name: packageName,
|
|
@@ -278,7 +275,7 @@ export async function storeInfo(packageName, opts) {
|
|
|
278
275
|
author: pkg.author,
|
|
279
276
|
tags: pkg.tags ?? [],
|
|
280
277
|
latest: pkg.latest,
|
|
281
|
-
versions:
|
|
278
|
+
versions: versionKeys,
|
|
282
279
|
assets: ver?.assets ?? [],
|
|
283
280
|
}, null, 2));
|
|
284
281
|
return;
|
|
@@ -290,7 +287,7 @@ export async function storeInfo(packageName, opts) {
|
|
|
290
287
|
console.log(` Author: ${pkg.author}`);
|
|
291
288
|
if (pkg.tags?.length)
|
|
292
289
|
console.log(` Tags: ${pkg.tags.join(', ')}`);
|
|
293
|
-
console.log(` Versions: ${
|
|
290
|
+
console.log(` Versions: ${versionKeys.join(', ')}`);
|
|
294
291
|
if (ver) {
|
|
295
292
|
console.log(`\nAssets (${ver.assets.length}):\n`);
|
|
296
293
|
for (const a of ver.assets) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@series-inc/stowkit-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.35",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"stowkit": "./dist/cli.js"
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
12
12
|
"skill.md",
|
|
13
|
-
"wasm"
|
|
13
|
+
"wasm",
|
|
14
|
+
"templates"
|
|
14
15
|
],
|
|
15
16
|
"scripts": {
|
|
16
17
|
"build": "tsc",
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
|
|
6
|
+
<title>My Game</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<canvas id="renderCanvas"></canvas>
|
|
10
|
+
<script type="module" src="/src/main.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-stowkit-game",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"postinstall": "node scripts/copy-decoders.mjs",
|
|
8
|
+
"dev": "vite",
|
|
9
|
+
"build": "vite build",
|
|
10
|
+
"preview": "vite preview"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@dimforge/rapier3d": "^0.11.2",
|
|
14
|
+
"@dimforge/rapier3d-compat": "^0.11.2",
|
|
15
|
+
"@series-inc/rundot-3d-engine": "^0.6.20",
|
|
16
|
+
"@series-inc/stowkit-three-loader": "^0.1.50",
|
|
17
|
+
"three": "^0.180.0",
|
|
18
|
+
"three-stdlib": "^2.36.0"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"@series-inc/rundot-game-sdk": "^5.5.4"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@series-inc/stowkit-reader": "^0.1.45",
|
|
25
|
+
"@types/node": "^20.11.16",
|
|
26
|
+
"@types/three": "^0.180.0",
|
|
27
|
+
"typescript": "^5.3.3",
|
|
28
|
+
"vite": "^5.0.12",
|
|
29
|
+
"vite-plugin-top-level-await": "^1.6.0",
|
|
30
|
+
"vite-plugin-wasm": "^3.5.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "prefabs": [] }
|