nowebcli 0.2.9 → 0.2.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.
@@ -67,19 +67,6 @@ export function normalizeCustomizationValues(value: unknown): CustomizationValue
67
67
  : null;
68
68
  }
69
69
 
70
- export function restoreCustomizationValues(
71
- current: CustomizationValues,
72
- previous: CustomizationValues,
73
- fieldIds: string[],
74
- ) {
75
- const restored = { ...current };
76
- for (const fieldId of fieldIds) {
77
- if (Object.hasOwn(previous, fieldId)) restored[fieldId] = previous[fieldId];
78
- else delete restored[fieldId];
79
- }
80
- return restored;
81
- }
82
-
83
70
  export function normalizeCustomizationValue(
84
71
  field: TemplateCustomization,
85
72
  value: unknown,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nowebcli",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "description": "Create, preview and publish community templates for Noweb",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -43,8 +43,6 @@
43
43
  "ci": "npm audit --omit=dev --audit-level=high && npm run typecheck && npm run lint && npm run test && npm run build && npm run test:e2e"
44
44
  },
45
45
  "dependencies": {
46
- "@ai-sdk/openai": "^4.0.24",
47
- "@ai-sdk/react": "^4.0.45",
48
46
  "@aws-sdk/client-s3": "^3.1055.0",
49
47
  "@heroicons/react": "^2.2.0",
50
48
  "@monaco-editor/react": "^4.7.0",
@@ -57,7 +55,6 @@
57
55
  "@tailwindcss/postcss": "^4",
58
56
  "@types/pg": "^8.20.0",
59
57
  "@uiw/react-textarea-code-editor": "^3.1.1",
60
- "ai": "^7.0.42",
61
58
  "better-auth": "1.7.2",
62
59
  "clsx": "^2.1.1",
63
60
  "esbuild": "^0.28.1",
package/scripts/noweb.mjs CHANGED
@@ -36,9 +36,9 @@ function usage() {
36
36
  login connect the CLI to your Noweb account
37
37
  list list your uploaded templates
38
38
  download <slug> restore one into the current folder
39
- upload publish this template directly to Noweb
39
+ upload save this template without publishing
40
40
  delete [slug] delete one of your templates
41
- publish [slug] publish validated templates (deployment only)`);
41
+ publish make the uploaded template public`);
42
42
  }
43
43
 
44
44
  async function createTemplate(value) {
@@ -184,6 +184,20 @@ async function upload() {
184
184
  if (!response.ok) throw new Error(result.error || `Upload failed (${response.status}).`);
185
185
  if (!Number.isInteger(result.id) || result.id < 1) throw new Error("Upload returned an invalid template ID.");
186
186
  await writeFile(join(root, "template.json"), `${JSON.stringify({ id: result.id, slug: template.slug, type: template.type }, null, 2)}\n`);
187
+ console.log(`Uploaded ${template.slug} (unpublished): ${new URL(result.url, apiBase)}`);
188
+ }
189
+
190
+ async function publishUploadedTemplate() {
191
+ const template = JSON.parse(await readFile(join(root, "template.json"), "utf8"));
192
+ if (!Number.isInteger(template.id) || template.id < 1) throw new Error("Upload this template first: npx nowebcli upload");
193
+ const credentials = await requireCredentials();
194
+ const response = await fetch(`${apiBase}/api/templates/visibility`, {
195
+ method: "PATCH",
196
+ headers: { Authorization: `Bearer ${credentials.token}`, "Content-Type": "application/json" },
197
+ body: JSON.stringify({ id: template.id, published: true }),
198
+ });
199
+ const result = await response.json().catch(() => ({}));
200
+ if (!response.ok) throw new Error(result.error || `Publish failed (${response.status}).`);
187
201
  console.log(`Published ${template.slug}: ${new URL(result.url, apiBase)}`);
188
202
  }
189
203
 
@@ -226,7 +240,7 @@ async function listTemplates() {
226
240
  }
227
241
  if (templates.length) {
228
242
  console.log("Templates");
229
- for (const template of templates) console.log(template.slug || template.id);
243
+ for (const template of templates) console.log(`${template.slug || template.id}\t${template.is_published ? "public" : "unpublished"}`);
230
244
  }
231
245
  if (projects.length) {
232
246
  if (templates.length) console.log();
@@ -396,22 +410,24 @@ async function publish(value) {
396
410
  await uploadTemplateAssets(template, storageApi.createObjectStorageClient(storage), storage.bucketName);
397
411
  const [published] = await sql`
398
412
  INSERT INTO templates (
399
- slug, type, component, compiled_js, compiled_css, customizations, author_id
413
+ slug, type, component, compiled_js, compiled_css, customizations, author_id, is_published
400
414
  ) VALUES (
401
415
  ${template.slug}, ${template.type}, ${template.source}, ${template.js},
402
- ${template.css}, ${sql.json(template.customizations)}, 'github'
416
+ ${template.css}, ${sql.json(template.customizations)}, 'github', true
403
417
  )
404
418
  ON CONFLICT (slug) DO UPDATE SET
405
419
  type = EXCLUDED.type,
406
420
  component = EXCLUDED.component,
407
421
  compiled_js = EXCLUDED.compiled_js,
408
422
  compiled_css = EXCLUDED.compiled_css,
409
- customizations = EXCLUDED.customizations
423
+ customizations = EXCLUDED.customizations,
424
+ is_published = true
410
425
  WHERE templates.type IS DISTINCT FROM EXCLUDED.type
411
426
  OR templates.component IS DISTINCT FROM EXCLUDED.component
412
427
  OR templates.compiled_js IS DISTINCT FROM EXCLUDED.compiled_js
413
428
  OR templates.compiled_css IS DISTINCT FROM EXCLUDED.compiled_css
414
429
  OR templates.customizations::jsonb IS DISTINCT FROM EXCLUDED.customizations::jsonb
430
+ OR templates.is_published IS DISTINCT FROM true
415
431
  RETURNING id
416
432
  `;
417
433
  if (!published) {
@@ -646,7 +662,7 @@ try {
646
662
  else if (command === "download") await downloadTemplate(slug);
647
663
  else if (command === "upload") await upload();
648
664
  else if (command === "delete") await deleteTemplate(slug);
649
- else if (command === "publish") await publish(slug);
665
+ else if (command === "publish") await (engineRoot === root ? publish(slug) : publishUploadedTemplate());
650
666
  else if (command === "help" || command === "--help" || command === "-h") usage();
651
667
  else throw new Error(`Unknown command: ${command}`);
652
668
  } catch (error) {
@@ -51,14 +51,21 @@ function loadFont(family: string, fallback: string) {
51
51
  const stylesheet = document.createElement("link");
52
52
  stylesheet.id = "noweb-template-fonts";
53
53
  stylesheet.rel = "stylesheet";
54
- stylesheet.href = window.location.hostname === "storage.noweb.app"
55
- ? "https://noweb.app/fonts/templates/fonts.css"
56
- : "/fonts/templates/fonts.css";
54
+ stylesheet.href = templateFontStylesheetHref(
55
+ currentTemplateRuntime.dataApiBase,
56
+ window.location.hostname,
57
+ );
57
58
  document.head.append(stylesheet);
58
59
  }
59
60
  return `"${family}", ${fallback}`;
60
61
  }
61
62
 
63
+ export function templateFontStylesheetHref(appBase: string | undefined, hostname: string) {
64
+ const base = appBase?.replace(/\/$/, "")
65
+ || (hostname === "storage.noweb.app" ? "https://noweb.app" : "");
66
+ return `${base}/fonts/templates/fonts.css`;
67
+ }
68
+
62
69
  export const font = fontStyles;
63
70
 
64
71
  export function TemplateRuntimeProvider({