@quadient/skillshare 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/index.js +12 -12
- package/dist/pages.js +51 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -209,3 +209,11 @@ bun run publish:tui -- --otp 123456
|
|
|
209
209
|
```
|
|
210
210
|
|
|
211
211
|
The publish script builds `tui/`, validates the packed files with `npm pack --dry-run`, and only then runs `npm publish`.
|
|
212
|
+
|
|
213
|
+
## Pages applications
|
|
214
|
+
|
|
215
|
+
`skillshare pages upload <directory> --slug <slug>` accepts static sites or
|
|
216
|
+
Wrangler JSON/JSONC projects. The CLI bundles Worker dependencies locally; install
|
|
217
|
+
them and build frontend assets before uploading. Inspect process status and logs
|
|
218
|
+
with `skillshare pages runtime <slug>`. Pages are served below `/p/<slug>/`, so use
|
|
219
|
+
that base path when building assets. Runtime data survives content replacement.
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { preparePageFiles } from './pages.js';
|
|
1
2
|
import { createServer } from 'node:http';
|
|
2
3
|
import { createHash, randomBytes } from 'node:crypto';
|
|
3
4
|
import { spawn } from 'node:child_process';
|
|
@@ -280,15 +281,15 @@ async function main() {
|
|
|
280
281
|
if (action === 'list') {
|
|
281
282
|
const pages = await client.get('/api/pages');
|
|
282
283
|
for (const page of pages) {
|
|
283
|
-
console.log(`${page.slug}\t${page.fileCount}\t${page.ownerDisplayName}`);
|
|
284
|
+
console.log(`${page.slug}\t${page.kind}\t${page.fileCount}\t${page.ownerDisplayName}`);
|
|
284
285
|
}
|
|
285
286
|
return;
|
|
286
287
|
}
|
|
287
|
-
if (action === 'show') {
|
|
288
|
+
if (action === 'show' || action === 'runtime') {
|
|
288
289
|
const slug = options.positionals[1];
|
|
289
290
|
if (!slug)
|
|
290
|
-
throw new Error(
|
|
291
|
-
console.log(JSON.stringify(await client.get(`/api/pages/${encodeURIComponent(slug)}`), null, 2));
|
|
291
|
+
throw new Error(`pages ${action} requires <slug>`);
|
|
292
|
+
console.log(JSON.stringify(await client.get(`/api/pages/${encodeURIComponent(slug)}${action === 'runtime' ? '/runtime' : ''}`), null, 2));
|
|
292
293
|
return;
|
|
293
294
|
}
|
|
294
295
|
if (action === 'upload') {
|
|
@@ -311,7 +312,7 @@ async function main() {
|
|
|
311
312
|
}
|
|
312
313
|
return;
|
|
313
314
|
}
|
|
314
|
-
throw new Error(`Unknown pages command '${action}'. Use list, show, or upload.`);
|
|
315
|
+
throw new Error(`Unknown pages command '${action}'. Use list, show, runtime, or upload.`);
|
|
315
316
|
}
|
|
316
317
|
}
|
|
317
318
|
return;
|
|
@@ -796,17 +797,14 @@ async function loadLocalSkillContext(directory, baseUrl) {
|
|
|
796
797
|
};
|
|
797
798
|
}
|
|
798
799
|
async function loadLocalPageFiles(directory) {
|
|
799
|
-
const filePlans = await collectFilePlans(directory, directory, { includeSkillMetadata: true });
|
|
800
|
+
const filePlans = await collectFilePlans(directory, directory, { includeSkillMetadata: true, pageProject: true });
|
|
800
801
|
if (filePlans.length === 0) {
|
|
801
802
|
throw new Error('The page directory is empty.');
|
|
802
803
|
}
|
|
803
|
-
|
|
804
|
-
throw new Error('The page directory must contain index.html at its root.');
|
|
805
|
-
}
|
|
806
|
-
const totalBytes = filePlans.reduce((sum, file) => sum + file.size, 0);
|
|
804
|
+
const files = await preparePageFiles(directory, await collectFiles(filePlans));
|
|
807
805
|
return {
|
|
808
|
-
files
|
|
809
|
-
totalBytes,
|
|
806
|
+
files,
|
|
807
|
+
totalBytes: files.reduce((sum, file) => sum + file.content.length, 0),
|
|
810
808
|
};
|
|
811
809
|
}
|
|
812
810
|
async function createSkillFromDirectory(client, localSkill, input) {
|
|
@@ -1881,6 +1879,8 @@ async function collectFilePlans(root, current = root, options = {}) {
|
|
|
1881
1879
|
const files = [];
|
|
1882
1880
|
for (const entry of entries) {
|
|
1883
1881
|
const entryPath = join(current, entry.name);
|
|
1882
|
+
if (options.pageProject && (entry.isSymbolicLink() || ['.git', '.celld', '.wrangler', 'node_modules'].includes(entry.name)))
|
|
1883
|
+
continue;
|
|
1884
1884
|
if (entry.isDirectory()) {
|
|
1885
1885
|
files.push(...(await collectFilePlans(root, entryPath, options)));
|
|
1886
1886
|
continue;
|
package/dist/pages.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { builtinModules } from 'node:module';
|
|
2
|
+
import { build } from 'esbuild';
|
|
3
|
+
import { parse, printParseErrorCode } from 'jsonc-parser';
|
|
4
|
+
import { resolve, relative } from 'node:path';
|
|
5
|
+
export async function preparePageFiles(directory, files) {
|
|
6
|
+
const configurations = files.filter(file => file.relativePath === 'wrangler.json' || file.relativePath === 'wrangler.jsonc');
|
|
7
|
+
if (configurations.length > 1)
|
|
8
|
+
throw new Error('Publish only one of wrangler.json and wrangler.jsonc.');
|
|
9
|
+
const configFile = configurations[0];
|
|
10
|
+
if (!configFile) {
|
|
11
|
+
if (!files.some(file => file.relativePath === 'index.html'))
|
|
12
|
+
throw new Error('The page directory must contain index.html or a Wrangler configuration at its root.');
|
|
13
|
+
return files;
|
|
14
|
+
}
|
|
15
|
+
const errors = [];
|
|
16
|
+
const config = parse(configFile.content.toString('utf8'), errors, { allowTrailingComma: true });
|
|
17
|
+
if (errors.length || !config || typeof config !== 'object' || Array.isArray(config))
|
|
18
|
+
throw new Error(`Invalid ${configFile.relativePath}: ${errors.map(error => printParseErrorCode(error.error)).join(', ')}`);
|
|
19
|
+
if (!config.main || config.no_bundle === true)
|
|
20
|
+
return files;
|
|
21
|
+
if (typeof config.main !== 'string' || !files.some(file => file.relativePath === config.main.replace(/^\.\//, '')))
|
|
22
|
+
throw new Error('Wrangler main must name a file inside the uploaded project.');
|
|
23
|
+
if (files.some(file => file.relativePath.startsWith('__skillshare/')))
|
|
24
|
+
throw new Error('The __skillshare directory is reserved for the compiled Worker.');
|
|
25
|
+
const outputDirectory = resolve(directory, '__skillshare');
|
|
26
|
+
const output = await build({
|
|
27
|
+
absWorkingDir: directory,
|
|
28
|
+
entryPoints: [config.main],
|
|
29
|
+
outdir: outputDirectory,
|
|
30
|
+
entryNames: 'worker',
|
|
31
|
+
bundle: true,
|
|
32
|
+
write: false,
|
|
33
|
+
format: 'esm',
|
|
34
|
+
platform: 'browser',
|
|
35
|
+
target: 'es2024',
|
|
36
|
+
conditions: ['workerd', 'worker', 'browser'],
|
|
37
|
+
external: ['cloudflare:*', 'node:*', ...builtinModules],
|
|
38
|
+
loader: { '.wasm': 'copy' },
|
|
39
|
+
});
|
|
40
|
+
config.main = '__skillshare/worker.js';
|
|
41
|
+
// Native celld still performs its own validation/bundling, including sibling WASM modules.
|
|
42
|
+
const prepared = files.map(file => file === configFile
|
|
43
|
+
? { ...file, content: Buffer.from(JSON.stringify(config, null, 2)) } : file);
|
|
44
|
+
for (const file of output.outputFiles)
|
|
45
|
+
prepared.push({
|
|
46
|
+
relativePath: relative(directory, file.path).replaceAll('\\', '/'),
|
|
47
|
+
content: Buffer.from(file.contents),
|
|
48
|
+
contentType: file.path.endsWith('.wasm') ? 'application/wasm' : 'text/javascript; charset=utf-8',
|
|
49
|
+
});
|
|
50
|
+
return prepared;
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quadient/skillshare",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Skillshare CLI for installing and publishing Quadient skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,6 +29,8 @@
|
|
|
29
29
|
"node": ">=20"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@clack/prompts": "^1.7.0"
|
|
32
|
+
"@clack/prompts": "^1.7.0",
|
|
33
|
+
"esbuild": "0.28.2",
|
|
34
|
+
"jsonc-parser": "3.3.1"
|
|
33
35
|
}
|
|
34
36
|
}
|