@construct-space/cli 1.1.6 → 1.1.9

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/index.js CHANGED
@@ -5,43 +5,25 @@ var __getProtoOf = Object.getPrototypeOf;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- function __accessProp(key) {
9
- return this[key];
10
- }
11
- var __toESMCache_node;
12
- var __toESMCache_esm;
13
8
  var __toESM = (mod, isNodeMode, target) => {
14
- var canCache = mod != null && typeof mod === "object";
15
- if (canCache) {
16
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
- var cached = cache.get(mod);
18
- if (cached)
19
- return cached;
20
- }
21
9
  target = mod != null ? __create(__getProtoOf(mod)) : {};
22
10
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
11
  for (let key of __getOwnPropNames(mod))
24
12
  if (!__hasOwnProp.call(to, key))
25
13
  __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
14
+ get: () => mod[key],
27
15
  enumerable: true
28
16
  });
29
- if (canCache)
30
- cache.set(mod, to);
31
17
  return to;
32
18
  };
33
19
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
- var __returnValue = (v) => v;
35
- function __exportSetter(name, newValue) {
36
- this[name] = __returnValue.bind(null, newValue);
37
- }
38
20
  var __export = (target, all) => {
39
21
  for (var name in all)
40
22
  __defProp(target, name, {
41
23
  get: all[name],
42
24
  enumerable: true,
43
25
  configurable: true,
44
- set: __exportSetter.bind(all, name)
26
+ set: (newValue) => all[name] = () => newValue
45
27
  });
46
28
  };
47
29
  var __require = import.meta.require;
@@ -2807,6 +2789,7 @@ Object.defineProperties(createChalk.prototype, styles2);
2807
2789
  var chalk = createChalk();
2808
2790
  var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
2809
2791
  var source_default = chalk;
2792
+
2810
2793
  // node_modules/@inquirer/core/dist/lib/key.js
2811
2794
  var isUpKey = (key, keybindings = []) => key.name === "up" || keybindings.includes("vim") && key.name === "k" || keybindings.includes("emacs") && key.ctrl && key.name === "p";
2812
2795
  var isDownKey = (key, keybindings = []) => key.name === "down" || keybindings.includes("vim") && key.name === "j" || keybindings.includes("emacs") && key.ctrl && key.name === "n";
@@ -2951,7 +2934,7 @@ var effectScheduler = {
2951
2934
  // node_modules/@inquirer/core/dist/lib/use-state.js
2952
2935
  function useState(defaultValue) {
2953
2936
  return withPointer((pointer) => {
2954
- const setState = AsyncResource2.bind(function setState2(newValue) {
2937
+ const setState = AsyncResource2.bind(function setState(newValue) {
2955
2938
  if (pointer.get() !== newValue) {
2956
2939
  pointer.set(newValue);
2957
2940
  handleChange();
@@ -4726,7 +4709,7 @@ async function scaffold(nameArg, options) {
4726
4709
  }
4727
4710
 
4728
4711
  // src/commands/build.ts
4729
- import { existsSync as existsSync6, readFileSync as readFileSync4, readdirSync as readdirSync2, renameSync, statSync as statSync2 } from "fs";
4712
+ import { existsSync as existsSync6, readFileSync as readFileSync4, readdirSync as readdirSync3, renameSync, statSync as statSync3 } from "fs";
4730
4713
  import { join as join6 } from "path";
4731
4714
  import { createHash } from "crypto";
4732
4715
 
@@ -7379,8 +7362,8 @@ function validate2(m) {
7379
7362
  errors2.push("author: must be an object with a name");
7380
7363
  if (!m.icon)
7381
7364
  errors2.push("icon: must be a string");
7382
- if (!["company", "project", "both"].includes(m.scope))
7383
- errors2.push('scope: must be "company", "project", or "both"');
7365
+ if (!["app", "project", "org", "any"].includes(m.scope))
7366
+ errors2.push('scope: must be "app", "project", "org", or "any"');
7384
7367
  if (!m.pages?.length)
7385
7368
  errors2.push("pages: must be a non-empty array");
7386
7369
  if (!m.navigation?.label)
@@ -7407,30 +7390,84 @@ function exists(dir) {
7407
7390
  }
7408
7391
 
7409
7392
  // src/lib/entry.ts
7410
- import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
7411
- import { join as join4, basename, extname } from "path";
7393
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync4, readdirSync, statSync } from "fs";
7394
+ import { join as join4, basename, extname, relative } from "path";
7412
7395
  function capitalize(s) {
7413
7396
  if (!s)
7414
7397
  return s;
7415
7398
  return s.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
7416
7399
  }
7417
- function resolvePages(m, prefix) {
7400
+ function scanPagesDir(pagesDir) {
7401
+ const results = [];
7402
+ function walk(dir, routeSegments) {
7403
+ if (!existsSync4(dir))
7404
+ return;
7405
+ const entries = readdirSync(dir).sort();
7406
+ for (const entry of entries) {
7407
+ const fullPath = join4(dir, entry);
7408
+ const stat = statSync(fullPath);
7409
+ if (stat.isDirectory()) {
7410
+ const segment = entry.replace(/^\[(.+)\]$/, ":$1");
7411
+ walk(fullPath, [...routeSegments, segment]);
7412
+ } else if (stat.isFile() && entry.endsWith(".vue")) {
7413
+ const nameWithoutExt = entry.replace(/\.vue$/, "");
7414
+ const relFile = relative(join4(pagesDir, ".."), fullPath).replace(/\\/g, "/");
7415
+ let routePath;
7416
+ if (nameWithoutExt === "index") {
7417
+ routePath = routeSegments.join("/");
7418
+ } else {
7419
+ const segment = nameWithoutExt.replace(/^\[(.+)\]$/, ":$1");
7420
+ routePath = [...routeSegments, segment].join("/");
7421
+ }
7422
+ results.push({ filePath: relFile, routePath });
7423
+ }
7424
+ }
7425
+ }
7426
+ walk(pagesDir, []);
7427
+ return results;
7428
+ }
7429
+ function varNameFromFile(filePath) {
7430
+ let cleaned = filePath.replace(/^pages\//, "").replace(/\.vue$/, "");
7431
+ cleaned = cleaned.replace(/\[([^\]]+)\]/g, "$1");
7432
+ const segments = cleaned.split(/[\/-]/).filter(Boolean);
7433
+ const name = segments.map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
7434
+ return (name || "Index") + "Page";
7435
+ }
7436
+ function resolvePages(m, root, prefix) {
7437
+ const pagesDir = join4(root, "src", "pages");
7438
+ const fsPages = scanPagesDir(pagesDir);
7439
+ const fsMap = new Map;
7440
+ for (const fp of fsPages) {
7441
+ fsMap.set(fp.routePath, fp);
7442
+ }
7418
7443
  return m.pages.map((p) => {
7419
- let component = p.component;
7420
- if (!component) {
7421
- component = p.path === "" ? "pages/index.vue" : `pages/${p.path}.vue`;
7444
+ if (p.component) {
7445
+ const base = basename(p.component);
7446
+ const nameWithoutExt = base.replace(extname(base), "").replace(/[\[\]]/g, "");
7447
+ const dir = p.component.replace(/^pages\//, "").replace(/\/[^/]+$/, "");
7448
+ let varName;
7449
+ if (dir && dir !== p.component.replace(/^pages\//, "")) {
7450
+ varName = capitalize(dir.split("/").map((s) => s.replace(/[\[\]]/g, "")).join("-")) + capitalize(nameWithoutExt) + "Page";
7451
+ } else {
7452
+ varName = capitalize(nameWithoutExt) + "Page";
7453
+ }
7454
+ return { varName, importPath: prefix + p.component, path: p.path };
7455
+ }
7456
+ const fsPage = fsMap.get(p.path);
7457
+ if (fsPage) {
7458
+ const varName = varNameFromFile(fsPage.filePath);
7459
+ return { varName, importPath: prefix + fsPage.filePath, path: p.path };
7422
7460
  }
7423
- let varName = "IndexPage";
7424
- if (p.path) {
7425
- let cleanPath = p.path.replace(/:/g, "");
7426
- if (p.component) {
7427
- let base = basename(p.component);
7428
- base = base.replace(extname(base), "").replace(/[\[\]]/g, "");
7429
- cleanPath = base;
7461
+ const legacyComponent = p.path === "" ? "pages/index.vue" : `pages/${p.path}.vue`;
7462
+ const legacyFullPath = join4(root, "src", legacyComponent);
7463
+ if (existsSync4(legacyFullPath)) {
7464
+ let varName = "IndexPage";
7465
+ if (p.path) {
7466
+ varName = capitalize(p.path.replace(/[\/:]/g, "-").replace(/-+/g, "-")) + "Page";
7430
7467
  }
7431
- varName = capitalize(cleanPath) + "Page";
7468
+ return { varName, importPath: prefix + legacyComponent, path: p.path };
7432
7469
  }
7433
- return { varName, importPath: prefix + component, path: p.path };
7470
+ throw new Error(`[entry] Could not resolve component for page "${p.path}". ` + `Checked: manifest component field, filesystem scan of src/pages/, ` + `and legacy fallback at ${legacyComponent}. ` + `Ensure a .vue file exists for this route.`);
7434
7471
  });
7435
7472
  }
7436
7473
  function resolveWidgets(m, prefix) {
@@ -7449,7 +7486,7 @@ function resolveWidgets(m, prefix) {
7449
7486
  }
7450
7487
  function generate(root, m) {
7451
7488
  const pagePrefix = existsSync4(join4(root, "src", "pages")) ? "./" : "../";
7452
- const pages = resolvePages(m, pagePrefix);
7489
+ const pages = resolvePages(m, root, pagePrefix);
7453
7490
  const widgets = resolveWidgets(m, "../");
7454
7491
  const actionsPath = join4(root, "src", "actions.ts");
7455
7492
  const hasActions = existsSync4(actionsPath);
@@ -7505,7 +7542,7 @@ function writeEntry(root, m) {
7505
7542
  }
7506
7543
 
7507
7544
  // src/lib/agent.ts
7508
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, readdirSync, existsSync as existsSync5 } from "fs";
7545
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync2, existsSync as existsSync5 } from "fs";
7509
7546
  import { join as join5, extname as extname2, basename as basename2 } from "path";
7510
7547
  var AGENT_KEY = "construct-agent-obfuscate-v1";
7511
7548
  function encode(content) {
@@ -7521,7 +7558,7 @@ function readMdFiles(dir) {
7521
7558
  const result = {};
7522
7559
  if (!existsSync5(dir))
7523
7560
  return result;
7524
- for (const f of readdirSync(dir)) {
7561
+ for (const f of readdirSync2(dir)) {
7525
7562
  if (extname2(f) !== ".md")
7526
7563
  continue;
7527
7564
  result[basename2(f, ".md")] = readFileSync3(join5(dir, f), "utf-8");
@@ -7532,7 +7569,7 @@ function readJsonFiles(dir) {
7532
7569
  const result = {};
7533
7570
  if (!existsSync5(dir))
7534
7571
  return result;
7535
- for (const f of readdirSync(dir)) {
7572
+ for (const f of readdirSync2(dir)) {
7536
7573
  if (extname2(f) !== ".json")
7537
7574
  continue;
7538
7575
  result[basename2(f, ".json")] = readFileSync3(join5(dir, f), "utf-8");
@@ -7622,7 +7659,7 @@ async function build(options) {
7622
7659
  }
7623
7660
  runHook(m.hooks, "postBuild", root);
7624
7661
  const agentDir = join6(root, "agent");
7625
- if (existsSync6(agentDir) && statSync2(agentDir).isDirectory()) {
7662
+ if (existsSync6(agentDir) && statSync3(agentDir).isDirectory()) {
7626
7663
  const distDir2 = join6(root, "dist");
7627
7664
  bundleAgentDir(agentDir, distDir2);
7628
7665
  bundleAgentDir(agentDir, root);
@@ -7631,7 +7668,7 @@ async function build(options) {
7631
7668
  const expectedBundle = `space-${m.id}.iife.js`;
7632
7669
  let bundlePath = join6(distDir, expectedBundle);
7633
7670
  if (!existsSync6(bundlePath)) {
7634
- const matches = readdirSync2(distDir).filter((f) => f.startsWith("space-") && f.endsWith(".iife.js"));
7671
+ const matches = readdirSync3(distDir).filter((f) => f.startsWith("space-") && f.endsWith(".iife.js"));
7635
7672
  if (matches.length === 1) {
7636
7673
  renameSync(join6(distDir, matches[0]), bundlePath);
7637
7674
  const oldCSS = join6(distDir, matches[0].replace(".iife.js", ".css"));
@@ -7665,16 +7702,16 @@ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
7665
7702
  import { join as join9 } from "path";
7666
7703
  import { createHash as createHash2 } from "crypto";
7667
7704
 
7668
- // node_modules/chokidar/esm/index.js
7669
- import { stat as statcb } from "fs";
7670
- import { stat as stat3, readdir as readdir2 } from "fs/promises";
7705
+ // node_modules/chokidar/index.js
7671
7706
  import { EventEmitter } from "events";
7672
- import * as sysPath2 from "path";
7707
+ import { stat as statcb, Stats } from "fs";
7708
+ import { readdir as readdir2, stat as stat3 } from "fs/promises";
7709
+ import * as sp2 from "path";
7673
7710
 
7674
- // node_modules/readdirp/esm/index.js
7675
- import { stat, lstat, readdir, realpath } from "fs/promises";
7711
+ // node_modules/readdirp/index.js
7712
+ import { lstat, readdir, realpath, stat } from "fs/promises";
7713
+ import { join as pjoin, relative as prelative, resolve as presolve, sep as psep } from "path";
7676
7714
  import { Readable } from "stream";
7677
- import { resolve as presolve, relative as prelative, join as pjoin, sep as psep } from "path";
7678
7715
  var EntryTypes = {
7679
7716
  FILE_TYPE: "files",
7680
7717
  DIR_TYPE: "directories",
@@ -7730,6 +7767,20 @@ var normalizeFilter = (filter) => {
7730
7767
  };
7731
7768
 
7732
7769
  class ReaddirpStream extends Readable {
7770
+ parents;
7771
+ reading;
7772
+ parent;
7773
+ _stat;
7774
+ _maxDepth;
7775
+ _wantsDir;
7776
+ _wantsFile;
7777
+ _wantsEverything;
7778
+ _root;
7779
+ _isDirent;
7780
+ _statsProp;
7781
+ _rdOptions;
7782
+ _fileFilter;
7783
+ _directoryFilter;
7733
7784
  constructor(options = {}) {
7734
7785
  super({
7735
7786
  objectMode: true,
@@ -7746,7 +7797,7 @@ class ReaddirpStream extends Readable {
7746
7797
  } else {
7747
7798
  this._stat = statMethod;
7748
7799
  }
7749
- this._maxDepth = opts.depth ?? defaultOptions.depth;
7800
+ this._maxDepth = opts.depth != null && Number.isSafeInteger(opts.depth) ? opts.depth : defaultOptions.depth;
7750
7801
  this._wantsDir = type ? DIR_TYPES.has(type) : false;
7751
7802
  this._wantsFile = type ? FILE_TYPES.has(type) : false;
7752
7803
  this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
@@ -7891,11 +7942,11 @@ function readdirp(root, options = {}) {
7891
7942
  return new ReaddirpStream(options);
7892
7943
  }
7893
7944
 
7894
- // node_modules/chokidar/esm/handler.js
7895
- import { watchFile, unwatchFile, watch as fs_watch } from "fs";
7896
- import { open, stat as stat2, lstat as lstat2, realpath as fsrealpath } from "fs/promises";
7897
- import * as sysPath from "path";
7945
+ // node_modules/chokidar/handler.js
7946
+ import { watch as fs_watch, unwatchFile, watchFile } from "fs";
7947
+ import { realpath as fsrealpath, lstat as lstat2, open, stat as stat2 } from "fs/promises";
7898
7948
  import { type as osType } from "os";
7949
+ import * as sp from "path";
7899
7950
  var STR_DATA = "data";
7900
7951
  var STR_END = "end";
7901
7952
  var STR_CLOSE = "close";
@@ -8187,7 +8238,7 @@ var binaryExtensions = new Set([
8187
8238
  "zip",
8188
8239
  "zipx"
8189
8240
  ]);
8190
- var isBinaryPath = (filePath) => binaryExtensions.has(sysPath.extname(filePath).slice(1).toLowerCase());
8241
+ var isBinaryPath = (filePath) => binaryExtensions.has(sp.extname(filePath).slice(1).toLowerCase());
8191
8242
  var foreach = (val, fn) => {
8192
8243
  if (val instanceof Set) {
8193
8244
  val.forEach(fn);
@@ -8225,7 +8276,7 @@ function createFsWatchInstance(path, options, listener, errHandler, emitRaw) {
8225
8276
  listener(path);
8226
8277
  emitRaw(rawEvent, evPath, { watchedPath: path });
8227
8278
  if (evPath && path !== evPath) {
8228
- fsWatchBroadcast(sysPath.resolve(path, evPath), KEY_LISTENERS, sysPath.join(path, evPath));
8279
+ fsWatchBroadcast(sp.resolve(path, evPath), KEY_LISTENERS, sp.join(path, evPath));
8229
8280
  }
8230
8281
  };
8231
8282
  try {
@@ -8340,17 +8391,19 @@ var setFsWatchFileListener = (path, fullPath, options, handlers) => {
8340
8391
  };
8341
8392
 
8342
8393
  class NodeFsHandler {
8394
+ fsw;
8395
+ _boundHandleError;
8343
8396
  constructor(fsW) {
8344
8397
  this.fsw = fsW;
8345
8398
  this._boundHandleError = (error2) => fsW._handleError(error2);
8346
8399
  }
8347
8400
  _watchWithNodeFs(path, listener) {
8348
8401
  const opts = this.fsw.options;
8349
- const directory = sysPath.dirname(path);
8350
- const basename4 = sysPath.basename(path);
8402
+ const directory = sp.dirname(path);
8403
+ const basename4 = sp.basename(path);
8351
8404
  const parent = this.fsw._getWatchedDir(directory);
8352
8405
  parent.add(basename4);
8353
- const absolutePath = sysPath.resolve(path);
8406
+ const absolutePath = sp.resolve(path);
8354
8407
  const options = {
8355
8408
  persistent: opts.persistent
8356
8409
  };
@@ -8377,8 +8430,8 @@ class NodeFsHandler {
8377
8430
  if (this.fsw.closed) {
8378
8431
  return;
8379
8432
  }
8380
- const dirname3 = sysPath.dirname(file);
8381
- const basename4 = sysPath.basename(file);
8433
+ const dirname3 = sp.dirname(file);
8434
+ const basename4 = sp.basename(file);
8382
8435
  const parent = this.fsw._getWatchedDir(dirname3);
8383
8436
  let prevStats = stats;
8384
8437
  if (parent.has(basename4))
@@ -8461,8 +8514,9 @@ class NodeFsHandler {
8461
8514
  this.fsw._symlinkPaths.set(full, true);
8462
8515
  }
8463
8516
  _handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
8464
- directory = sysPath.join(directory, "");
8465
- throttler = this.fsw._throttle("readdir", directory, 1000);
8517
+ directory = sp.join(directory, "");
8518
+ const throttleKey = target ? `${directory}:${target}` : directory;
8519
+ throttler = this.fsw._throttle("readdir", throttleKey, 1000);
8466
8520
  if (!throttler)
8467
8521
  return;
8468
8522
  const previous = this.fsw._getWatchedDir(wh.path);
@@ -8479,7 +8533,7 @@ class NodeFsHandler {
8479
8533
  return;
8480
8534
  }
8481
8535
  const item = entry.path;
8482
- let path = sysPath.join(directory, item);
8536
+ let path = sp.join(directory, item);
8483
8537
  current.add(item);
8484
8538
  if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path, item)) {
8485
8539
  return;
@@ -8490,7 +8544,7 @@ class NodeFsHandler {
8490
8544
  }
8491
8545
  if (item === target || !target && !previous.has(item)) {
8492
8546
  this.fsw._incrReadyCount();
8493
- path = sysPath.join(dir, sysPath.relative(dir, path));
8547
+ path = sp.join(dir, sp.relative(dir, path));
8494
8548
  this._addToNodeFs(path, initialAdd, wh, depth + 1);
8495
8549
  }
8496
8550
  }).on(EV.ERROR, this._boundHandleError);
@@ -8516,12 +8570,12 @@ class NodeFsHandler {
8516
8570
  });
8517
8571
  }
8518
8572
  async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath2) {
8519
- const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));
8520
- const tracked = parentDir.has(sysPath.basename(dir));
8573
+ const parentDir = this.fsw._getWatchedDir(sp.dirname(dir));
8574
+ const tracked = parentDir.has(sp.basename(dir));
8521
8575
  if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
8522
8576
  this.fsw._emit(EV.ADD_DIR, dir, stats);
8523
8577
  }
8524
- parentDir.add(sysPath.basename(dir));
8578
+ parentDir.add(sp.basename(dir));
8525
8579
  this.fsw._getWatchedDir(dir);
8526
8580
  let throttler;
8527
8581
  let closer;
@@ -8562,7 +8616,7 @@ class NodeFsHandler {
8562
8616
  const follow = this.fsw.options.followSymlinks;
8563
8617
  let closer;
8564
8618
  if (stats.isDirectory()) {
8565
- const absPath = sysPath.resolve(path);
8619
+ const absPath = sp.resolve(path);
8566
8620
  const targetPath = follow ? await fsrealpath(path) : path;
8567
8621
  if (this.fsw.closed)
8568
8622
  return;
@@ -8576,14 +8630,14 @@ class NodeFsHandler {
8576
8630
  const targetPath = follow ? await fsrealpath(path) : path;
8577
8631
  if (this.fsw.closed)
8578
8632
  return;
8579
- const parent = sysPath.dirname(wh.watchPath);
8633
+ const parent = sp.dirname(wh.watchPath);
8580
8634
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
8581
8635
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
8582
8636
  closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);
8583
8637
  if (this.fsw.closed)
8584
8638
  return;
8585
8639
  if (targetPath !== undefined) {
8586
- this.fsw._symlinkPaths.set(sysPath.resolve(path), targetPath);
8640
+ this.fsw._symlinkPaths.set(sp.resolve(path), targetPath);
8587
8641
  }
8588
8642
  } else {
8589
8643
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
@@ -8601,7 +8655,7 @@ class NodeFsHandler {
8601
8655
  }
8602
8656
  }
8603
8657
 
8604
- // node_modules/chokidar/esm/index.js
8658
+ // node_modules/chokidar/index.js
8605
8659
  /*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) */
8606
8660
  var SLASH = "/";
8607
8661
  var SLASH_SLASH = "//";
@@ -8609,7 +8663,7 @@ var ONE_DOT = ".";
8609
8663
  var TWO_DOTS = "..";
8610
8664
  var STRING_TYPE = "string";
8611
8665
  var BACK_SLASH_RE = /\\/g;
8612
- var DOUBLE_SLASH_RE = /\/\//;
8666
+ var DOUBLE_SLASH_RE = /\/\//g;
8613
8667
  var DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
8614
8668
  var REPLACER_RE = /^\.[/\\]/;
8615
8669
  function arrify(item) {
@@ -8628,11 +8682,11 @@ function createPattern(matcher) {
8628
8682
  if (matcher.path === string)
8629
8683
  return true;
8630
8684
  if (matcher.recursive) {
8631
- const relative3 = sysPath2.relative(matcher.path, string);
8632
- if (!relative3) {
8685
+ const relative4 = sp2.relative(matcher.path, string);
8686
+ if (!relative4) {
8633
8687
  return false;
8634
8688
  }
8635
- return !relative3.startsWith("..") && !sysPath2.isAbsolute(relative3);
8689
+ return !relative4.startsWith("..") && !sp2.isAbsolute(relative4);
8636
8690
  }
8637
8691
  return false;
8638
8692
  };
@@ -8642,14 +8696,12 @@ function createPattern(matcher) {
8642
8696
  function normalizePath(path) {
8643
8697
  if (typeof path !== "string")
8644
8698
  throw new Error("string expected");
8645
- path = sysPath2.normalize(path);
8699
+ path = sp2.normalize(path);
8646
8700
  path = path.replace(/\\/g, "/");
8647
8701
  let prepend = false;
8648
8702
  if (path.startsWith("//"))
8649
8703
  prepend = true;
8650
- const DOUBLE_SLASH_RE2 = /\/\//;
8651
- while (path.match(DOUBLE_SLASH_RE2))
8652
- path = path.replace(DOUBLE_SLASH_RE2, "/");
8704
+ path = path.replace(DOUBLE_SLASH_RE, "/");
8653
8705
  if (prepend)
8654
8706
  path = "/" + path;
8655
8707
  return path;
@@ -8690,31 +8742,32 @@ var toUnix = (string) => {
8690
8742
  if (str.startsWith(SLASH_SLASH)) {
8691
8743
  prepend = true;
8692
8744
  }
8693
- while (str.match(DOUBLE_SLASH_RE)) {
8694
- str = str.replace(DOUBLE_SLASH_RE, SLASH);
8695
- }
8745
+ str = str.replace(DOUBLE_SLASH_RE, SLASH);
8696
8746
  if (prepend) {
8697
8747
  str = SLASH + str;
8698
8748
  }
8699
8749
  return str;
8700
8750
  };
8701
- var normalizePathToUnix = (path) => toUnix(sysPath2.normalize(toUnix(path)));
8751
+ var normalizePathToUnix = (path) => toUnix(sp2.normalize(toUnix(path)));
8702
8752
  var normalizeIgnored = (cwd = "") => (path) => {
8703
8753
  if (typeof path === "string") {
8704
- return normalizePathToUnix(sysPath2.isAbsolute(path) ? path : sysPath2.join(cwd, path));
8754
+ return normalizePathToUnix(sp2.isAbsolute(path) ? path : sp2.join(cwd, path));
8705
8755
  } else {
8706
8756
  return path;
8707
8757
  }
8708
8758
  };
8709
8759
  var getAbsolutePath = (path, cwd) => {
8710
- if (sysPath2.isAbsolute(path)) {
8760
+ if (sp2.isAbsolute(path)) {
8711
8761
  return path;
8712
8762
  }
8713
- return sysPath2.join(cwd, path);
8763
+ return sp2.join(cwd, path);
8714
8764
  };
8715
8765
  var EMPTY_SET = Object.freeze(new Set);
8716
8766
 
8717
8767
  class DirEntry {
8768
+ path;
8769
+ _removeWatcher;
8770
+ items;
8718
8771
  constructor(dir, removeWatcher) {
8719
8772
  this.path = dir;
8720
8773
  this._removeWatcher = removeWatcher;
@@ -8739,7 +8792,7 @@ class DirEntry {
8739
8792
  await readdir2(dir);
8740
8793
  } catch (err) {
8741
8794
  if (this._removeWatcher) {
8742
- this._removeWatcher(sysPath2.dirname(dir), sysPath2.basename(dir));
8795
+ this._removeWatcher(sp2.dirname(dir), sp2.basename(dir));
8743
8796
  }
8744
8797
  }
8745
8798
  }
@@ -8767,12 +8820,19 @@ var STAT_METHOD_F = "stat";
8767
8820
  var STAT_METHOD_L = "lstat";
8768
8821
 
8769
8822
  class WatchHelper {
8823
+ fsw;
8824
+ path;
8825
+ watchPath;
8826
+ fullWatchPath;
8827
+ dirParts;
8828
+ followSymlinks;
8829
+ statMethod;
8770
8830
  constructor(path, follow, fsw) {
8771
8831
  this.fsw = fsw;
8772
8832
  const watchPath = path;
8773
8833
  this.path = path = path.replace(REPLACER_RE, "");
8774
8834
  this.watchPath = watchPath;
8775
- this.fullWatchPath = sysPath2.resolve(watchPath);
8835
+ this.fullWatchPath = sp2.resolve(watchPath);
8776
8836
  this.dirParts = [];
8777
8837
  this.dirParts.forEach((parts) => {
8778
8838
  if (parts.length > 1)
@@ -8782,7 +8842,7 @@ class WatchHelper {
8782
8842
  this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
8783
8843
  }
8784
8844
  entryPath(entry) {
8785
- return sysPath2.join(this.watchPath, sysPath2.relative(this.watchPath, entry.fullPath));
8845
+ return sp2.join(this.watchPath, sp2.relative(this.watchPath, entry.fullPath));
8786
8846
  }
8787
8847
  filterPath(entry) {
8788
8848
  const { stats } = entry;
@@ -8797,6 +8857,24 @@ class WatchHelper {
8797
8857
  }
8798
8858
 
8799
8859
  class FSWatcher extends EventEmitter {
8860
+ closed;
8861
+ options;
8862
+ _closers;
8863
+ _ignoredPaths;
8864
+ _throttled;
8865
+ _streams;
8866
+ _symlinkPaths;
8867
+ _watched;
8868
+ _pendingWrites;
8869
+ _pendingUnlinks;
8870
+ _readyCount;
8871
+ _emitReady;
8872
+ _closePromise;
8873
+ _userIgnored;
8874
+ _readyEmitted;
8875
+ _emitRaw;
8876
+ _boundRemove;
8877
+ _nodeFsHandler;
8800
8878
  constructor(_opts = {}) {
8801
8879
  super();
8802
8880
  this.closed = false;
@@ -8905,7 +8983,7 @@ class FSWatcher extends EventEmitter {
8905
8983
  return;
8906
8984
  results.forEach((item) => {
8907
8985
  if (item)
8908
- this.add(sysPath2.dirname(item), sysPath2.basename(_origAdd || item));
8986
+ this.add(sp2.dirname(item), sp2.basename(_origAdd || item));
8909
8987
  });
8910
8988
  });
8911
8989
  return this;
@@ -8916,10 +8994,10 @@ class FSWatcher extends EventEmitter {
8916
8994
  const paths = unifyPaths(paths_);
8917
8995
  const { cwd } = this.options;
8918
8996
  paths.forEach((path) => {
8919
- if (!sysPath2.isAbsolute(path) && !this._closers.has(path)) {
8997
+ if (!sp2.isAbsolute(path) && !this._closers.has(path)) {
8920
8998
  if (cwd)
8921
- path = sysPath2.join(cwd, path);
8922
- path = sysPath2.resolve(path);
8999
+ path = sp2.join(cwd, path);
9000
+ path = sp2.resolve(path);
8923
9001
  }
8924
9002
  this._closePath(path);
8925
9003
  this._addIgnoredPath(path);
@@ -8963,7 +9041,7 @@ class FSWatcher extends EventEmitter {
8963
9041
  getWatched() {
8964
9042
  const watchList = {};
8965
9043
  this._watched.forEach((entry, dir) => {
8966
- const key = this.options.cwd ? sysPath2.relative(this.options.cwd, dir) : dir;
9044
+ const key = this.options.cwd ? sp2.relative(this.options.cwd, dir) : dir;
8967
9045
  const index = key || ONE_DOT;
8968
9046
  watchList[index] = entry.getChildren().sort();
8969
9047
  });
@@ -8979,9 +9057,9 @@ class FSWatcher extends EventEmitter {
8979
9057
  return;
8980
9058
  const opts = this.options;
8981
9059
  if (isWindows)
8982
- path = sysPath2.normalize(path);
9060
+ path = sp2.normalize(path);
8983
9061
  if (opts.cwd)
8984
- path = sysPath2.relative(opts.cwd, path);
9062
+ path = sp2.relative(opts.cwd, path);
8985
9063
  const args = [path];
8986
9064
  if (stats != null)
8987
9065
  args.push(stats);
@@ -9032,7 +9110,7 @@ class FSWatcher extends EventEmitter {
9032
9110
  return this;
9033
9111
  }
9034
9112
  if (opts.alwaysStat && stats === undefined && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
9035
- const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path) : path;
9113
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path) : path;
9036
9114
  let stats2;
9037
9115
  try {
9038
9116
  stats2 = await stat3(fullPath);
@@ -9088,8 +9166,8 @@ class FSWatcher extends EventEmitter {
9088
9166
  const pollInterval = awf.pollInterval;
9089
9167
  let timeoutHandler;
9090
9168
  let fullPath = path;
9091
- if (this.options.cwd && !sysPath2.isAbsolute(path)) {
9092
- fullPath = sysPath2.join(this.options.cwd, path);
9169
+ if (this.options.cwd && !sp2.isAbsolute(path)) {
9170
+ fullPath = sp2.join(this.options.cwd, path);
9093
9171
  }
9094
9172
  const now = new Date;
9095
9173
  const writes = this._pendingWrites;
@@ -9146,7 +9224,7 @@ class FSWatcher extends EventEmitter {
9146
9224
  return new WatchHelper(path, this.options.followSymlinks, this);
9147
9225
  }
9148
9226
  _getWatchedDir(directory) {
9149
- const dir = sysPath2.resolve(directory);
9227
+ const dir = sp2.resolve(directory);
9150
9228
  if (!this._watched.has(dir))
9151
9229
  this._watched.set(dir, new DirEntry(dir, this._boundRemove));
9152
9230
  return this._watched.get(dir);
@@ -9157,8 +9235,8 @@ class FSWatcher extends EventEmitter {
9157
9235
  return Boolean(Number(stats.mode) & 256);
9158
9236
  }
9159
9237
  _remove(directory, item, isDirectory) {
9160
- const path = sysPath2.join(directory, item);
9161
- const fullPath = sysPath2.resolve(path);
9238
+ const path = sp2.join(directory, item);
9239
+ const fullPath = sp2.resolve(path);
9162
9240
  isDirectory = isDirectory != null ? isDirectory : this._watched.has(path) || this._watched.has(fullPath);
9163
9241
  if (!this._throttle("remove", path, 100))
9164
9242
  return;
@@ -9176,7 +9254,7 @@ class FSWatcher extends EventEmitter {
9176
9254
  }
9177
9255
  let relPath = path;
9178
9256
  if (this.options.cwd)
9179
- relPath = sysPath2.relative(this.options.cwd, path);
9257
+ relPath = sp2.relative(this.options.cwd, path);
9180
9258
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
9181
9259
  const event = this._pendingWrites.get(relPath).cancelWait();
9182
9260
  if (event === EVENTS.ADD)
@@ -9191,8 +9269,8 @@ class FSWatcher extends EventEmitter {
9191
9269
  }
9192
9270
  _closePath(path) {
9193
9271
  this._closeFile(path);
9194
- const dir = sysPath2.dirname(path);
9195
- this._getWatchedDir(dir).remove(sysPath2.basename(path));
9272
+ const dir = sp2.dirname(path);
9273
+ this._getWatchedDir(dir).remove(sp2.basename(path));
9196
9274
  }
9197
9275
  _closeFile(path) {
9198
9276
  const closers = this._closers.get(path);
@@ -9361,7 +9439,7 @@ function install() {
9361
9439
  }
9362
9440
 
9363
9441
  // src/commands/publish.ts
9364
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, statSync as statSync4, unlinkSync as unlinkSync2 } from "fs";
9442
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, statSync as statSync5, unlinkSync as unlinkSync2 } from "fs";
9365
9443
  import { join as join14, basename as basename6 } from "path";
9366
9444
 
9367
9445
  // src/lib/auth.ts
@@ -9404,7 +9482,7 @@ function clear() {
9404
9482
  }
9405
9483
 
9406
9484
  // src/lib/pack.ts
9407
- import { readdirSync as readdirSync3, statSync as statSync3, existsSync as existsSync10 } from "fs";
9485
+ import { readdirSync as readdirSync4, statSync as statSync4, existsSync as existsSync10 } from "fs";
9408
9486
  import { join as join13 } from "path";
9409
9487
  import { tmpdir } from "os";
9410
9488
  import { execSync as execSync3 } from "child_process";
@@ -9446,8 +9524,8 @@ async function packSource(root) {
9446
9524
  if (existsSync10(join13(root, name)))
9447
9525
  entries.push(name);
9448
9526
  }
9449
- for (const entry of readdirSync3(root)) {
9450
- if (statSync3(join13(root, entry)).isDirectory())
9527
+ for (const entry of readdirSync4(root)) {
9528
+ if (statSync4(join13(root, entry)).isDirectory())
9451
9529
  continue;
9452
9530
  if (allowedRootFiles.includes(entry))
9453
9531
  continue;
@@ -9467,7 +9545,7 @@ async function packSource(root) {
9467
9545
  const excludes = "--exclude=node_modules --exclude=dist --exclude=.git --exclude=*.env --exclude=*.log --exclude=*.lock --exclude=*.lockb";
9468
9546
  const cmd = `tar czf "${tarballPath}" ${excludes} ${validEntries.join(" ")}`;
9469
9547
  execSync3(cmd, { cwd: root });
9470
- const size = statSync3(tarballPath).size;
9548
+ const size = statSync4(tarballPath).size;
9471
9549
  if (size > MAX_SIZE) {
9472
9550
  throw new Error(`Source exceeds maximum size of ${MAX_SIZE / 1024 / 1024}MB`);
9473
9551
  }
@@ -9490,6 +9568,16 @@ async function uploadSource(portalURL, token, tarballPath, m) {
9490
9568
  if (resp.status === 401) {
9491
9569
  throw new Error("authentication failed \u2014 run 'construct login' to re-authenticate");
9492
9570
  }
9571
+ if (resp.status === 403) {
9572
+ let msg = result.error || "You are not the owner of this space";
9573
+ if (result.owner_user_id) {
9574
+ msg += `
9575
+ Current owner: ${result.owner_user_id}`;
9576
+ }
9577
+ msg += `
9578
+ Fork to a new space_id to publish your own version.`;
9579
+ throw new Error(msg);
9580
+ }
9493
9581
  if (resp.status >= 400) {
9494
9582
  const msg = result.error || result.errors?.join("; ") || `server returned ${resp.status}`;
9495
9583
  throw new Error(msg);
@@ -9607,7 +9695,7 @@ async function publish(options) {
9607
9695
  let tarballPath;
9608
9696
  try {
9609
9697
  tarballPath = await packSource(root);
9610
- const size = statSync4(tarballPath).size;
9698
+ const size = statSync5(tarballPath).size;
9611
9699
  spinner.succeed(`Source packed (${formatBytes(size)})`);
9612
9700
  } catch (err) {
9613
9701
  spinner.fail("Pack failed");
@@ -10140,7 +10228,7 @@ function updateBarrel(modelsDir, modelName) {
10140
10228
  }
10141
10229
 
10142
10230
  // src/commands/graph/push.ts
10143
- import { existsSync as existsSync16, readdirSync as readdirSync4, readFileSync as readFileSync12 } from "fs";
10231
+ import { existsSync as existsSync16, readdirSync as readdirSync5, readFileSync as readFileSync12 } from "fs";
10144
10232
  import { join as join20, basename as basename7 } from "path";
10145
10233
  async function graphPush() {
10146
10234
  const root = process.cwd();
@@ -10154,7 +10242,7 @@ async function graphPush() {
10154
10242
  console.error(source_default.red("No src/models/ directory found. Run 'construct graph init' first."));
10155
10243
  process.exit(1);
10156
10244
  }
10157
- const modelFiles = readdirSync4(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10245
+ const modelFiles = readdirSync5(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10158
10246
  if (modelFiles.length === 0) {
10159
10247
  console.error(source_default.red("No model files found in src/models/"));
10160
10248
  console.log(source_default.dim(" Generate one: construct graph g User name:string email:string"));
@@ -10182,12 +10270,14 @@ async function graphPush() {
10182
10270
  const graphURL = process.env.GRAPH_URL || "https://graph.construct.space";
10183
10271
  const spinner = ora("Registering models...").start();
10184
10272
  try {
10273
+ const userID = creds.user?.id || "";
10185
10274
  const resp = await fetch(`${graphURL}/api/schemas/register`, {
10186
10275
  method: "POST",
10187
10276
  headers: {
10188
10277
  "Content-Type": "application/json",
10189
10278
  Authorization: `Bearer ${creds.token}`,
10190
- "X-Space-ID": m.id
10279
+ "X-Space-ID": m.id,
10280
+ "X-Auth-User-ID": userID
10191
10281
  },
10192
10282
  body: JSON.stringify({
10193
10283
  space_id: m.id,
@@ -10197,6 +10287,20 @@ async function graphPush() {
10197
10287
  manifest: { version: 1, models }
10198
10288
  })
10199
10289
  });
10290
+ if (resp.status === 403) {
10291
+ spinner.fail("Ownership check failed");
10292
+ try {
10293
+ const errBody = await resp.json();
10294
+ console.error(source_default.red(` ${errBody.error || "You are not the owner of this space"}`));
10295
+ if (errBody.owner_user_id) {
10296
+ console.error(source_default.dim(` Current owner: ${errBody.owner_user_id}`));
10297
+ }
10298
+ console.error(source_default.dim(" Fork to a new space_id to publish your own version."));
10299
+ } catch {
10300
+ console.error(source_default.red(` 403: Forbidden \u2014 ownership check failed`));
10301
+ }
10302
+ process.exit(1);
10303
+ }
10200
10304
  if (!resp.ok) {
10201
10305
  const body = await resp.text();
10202
10306
  spinner.fail("Registration failed");
@@ -10279,7 +10383,7 @@ function parseModelFile(content, fileName) {
10279
10383
  }
10280
10384
 
10281
10385
  // src/commands/graph/migrate.ts
10282
- import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync13 } from "fs";
10386
+ import { existsSync as existsSync17, readdirSync as readdirSync6, readFileSync as readFileSync13 } from "fs";
10283
10387
  import { join as join21, basename as basename8 } from "path";
10284
10388
  async function graphMigrate(options) {
10285
10389
  const root = process.cwd();
@@ -10316,7 +10420,7 @@ async function graphMigrate(options) {
10316
10420
  spinner.fail("Could not fetch schema");
10317
10421
  process.exit(1);
10318
10422
  }
10319
- const modelFiles = readdirSync5(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10423
+ const modelFiles = readdirSync6(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10320
10424
  const localModels = [];
10321
10425
  for (const file of modelFiles) {
10322
10426
  const content = readFileSync13(join21(modelsDir, file), "utf-8");
@@ -10423,7 +10527,7 @@ function parseModelFields(content, fileName) {
10423
10527
  }
10424
10528
 
10425
10529
  // src/index.ts
10426
- var VERSION = "1.1.6";
10530
+ var VERSION = "1.1.9";
10427
10531
  var program2 = new Command;
10428
10532
  program2.name("construct").description("Construct CLI \u2014 scaffold, build, develop, and publish spaces").version(VERSION);
10429
10533
  program2.command("scaffold [name]").alias("new").alias("create").description("Create a new Construct space project").option("--with-tests", "Include E2E testing boilerplate").option("--full", "Full preset: multiple pages, extra skills, widget templates").action(async (name, opts) => scaffold(name, opts));
@@ -7,7 +7,7 @@
7
7
  "name": "Your Name"
8
8
  },
9
9
  "icon": "i-lucide-box",
10
- "scope": "both",
10
+ "scope": "app",
11
11
  "minConstructVersion": "0.7.0",
12
12
  "navigation": {
13
13
  "label": "{{.DisplayName}}",
@@ -7,7 +7,7 @@
7
7
  "name": "Your Name"
8
8
  },
9
9
  "icon": "i-lucide-box",
10
- "scope": "both",
10
+ "scope": "app",
11
11
  "minConstructVersion": "0.7.0",
12
12
  "navigation": {
13
13
  "label": "{{.DisplayName}}",
@@ -1,7 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
3
  * {{.DisplayName}} Summary Widget — 2×1 compact view
4
+ *
5
+ * Widgets run inside a closed Shadow DOM sandbox.
6
+ * Use the injected widgetApi for theme and actions — do not access
7
+ * window, document, or global stores directly.
4
8
  */
9
+ import { inject } from 'vue'
10
+
11
+ const api = inject<{ theme: { mode: string; vars: Record<string, string> }; space: { id: string; name: string } }>('widgetApi')
5
12
  </script>
6
13
 
7
14
  <template>
@@ -1,7 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
3
  * {{.DisplayName}} Summary Widget — 4×1 wide view
4
+ *
5
+ * Widgets run inside a closed Shadow DOM sandbox.
6
+ * Use the injected widgetApi for theme and actions — do not access
7
+ * window, document, or global stores directly.
4
8
  */
9
+ import { inject } from 'vue'
10
+
11
+ const api = inject<{ theme: { mode: string; vars: Record<string, string> }; space: { id: string; name: string } }>('widgetApi')
5
12
  </script>
6
13
 
7
14
  <template>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@construct-space/cli",
3
- "version": "1.1.6",
3
+ "version": "1.1.9",
4
4
  "description": "Construct CLI — scaffold, build, develop, and publish spaces",
5
5
  "type": "module",
6
6
  "bin": {
@@ -7,7 +7,7 @@
7
7
  "name": "Your Name"
8
8
  },
9
9
  "icon": "i-lucide-box",
10
- "scope": "both",
10
+ "scope": "app",
11
11
  "minConstructVersion": "0.7.0",
12
12
  "navigation": {
13
13
  "label": "{{.DisplayName}}",
@@ -7,7 +7,7 @@
7
7
  "name": "Your Name"
8
8
  },
9
9
  "icon": "i-lucide-box",
10
- "scope": "both",
10
+ "scope": "app",
11
11
  "minConstructVersion": "0.7.0",
12
12
  "navigation": {
13
13
  "label": "{{.DisplayName}}",
@@ -1,7 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
3
  * {{.DisplayName}} Summary Widget — 2×1 compact view
4
+ *
5
+ * Widgets run inside a closed Shadow DOM sandbox.
6
+ * Use the injected widgetApi for theme and actions — do not access
7
+ * window, document, or global stores directly.
4
8
  */
9
+ import { inject } from 'vue'
10
+
11
+ const api = inject<{ theme: { mode: string; vars: Record<string, string> }; space: { id: string; name: string } }>('widgetApi')
5
12
  </script>
6
13
 
7
14
  <template>
@@ -1,7 +1,14 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
3
  * {{.DisplayName}} Summary Widget — 4×1 wide view
4
+ *
5
+ * Widgets run inside a closed Shadow DOM sandbox.
6
+ * Use the injected widgetApi for theme and actions — do not access
7
+ * window, document, or global stores directly.
4
8
  */
9
+ import { inject } from 'vue'
10
+
11
+ const api = inject<{ theme: { mode: string; vars: Record<string, string> }; space: { id: string; name: string } }>('widgetApi')
5
12
  </script>
6
13
 
7
14
  <template>