@wix/himalaya-cli 0.1117.0 → 0.1119.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/dist/cli.mjs CHANGED
@@ -13266,12 +13266,12 @@ function stripBrandFromIdiom(tokens) {
13266
13266
  for (const key2 of BRAND_COLOR_TOKENS) delete colors[key2];
13267
13267
  if (Object.keys(colors).length === 0) delete o.colors;
13268
13268
  }
13269
- const shadows = o.shadows;
13270
- const glow = shadows && typeof shadows === "object" && !Array.isArray(shadows) ? shadows[BRAND_GLOW_SHADOW] : void 0;
13269
+ const shadows2 = o.shadows;
13270
+ const glow = shadows2 && typeof shadows2 === "object" && !Array.isArray(shadows2) ? shadows2[BRAND_GLOW_SHADOW] : void 0;
13271
13271
  if (glow && typeof glow === "object" && !Array.isArray(glow) && "color" in glow) {
13272
13272
  delete glow.color;
13273
- if (Object.keys(glow).length === 0) delete shadows[BRAND_GLOW_SHADOW];
13274
- if (Object.keys(shadows).length === 0) delete o.shadows;
13273
+ if (Object.keys(glow).length === 0) delete shadows2[BRAND_GLOW_SHADOW];
13274
+ if (Object.keys(shadows2).length === 0) delete o.shadows;
13275
13275
  }
13276
13276
  }
13277
13277
  }
@@ -13363,9 +13363,9 @@ function tokenColorIssues(dir, tokensPathOverride) {
13363
13363
  error(`${tokensPath}: must be an object`);
13364
13364
  return issues;
13365
13365
  }
13366
- const shadows = raw.shadows;
13367
- if (shadows !== null && typeof shadows === "object" && !Array.isArray(shadows)) {
13368
- for (const [name, token] of Object.entries(shadows)) {
13366
+ const shadows2 = raw.shadows;
13367
+ if (shadows2 !== null && typeof shadows2 === "object" && !Array.isArray(shadows2)) {
13368
+ for (const [name, token] of Object.entries(shadows2)) {
13369
13369
  const value = token?.color;
13370
13370
  if (value === void 0) continue;
13371
13371
  if (typeof value === "string" && isCanonicalHex(value)) continue;
@@ -19946,6 +19946,7 @@ function deriveAppMeta(name, m, platforms = {}) {
19946
19946
  drawer: config.navigation?.drawer,
19947
19947
  navKind: config.navigation?.navKind,
19948
19948
  orientation: config.navigation?.orientation,
19949
+ cache: config.cache,
19949
19950
  sidebar: config.navigation?.sidebar,
19950
19951
  navLogicBundle: config.navigation?.navLogicBundle,
19951
19952
  appIconBadgeSources: config.navigation?.appIconBadgeSources,
@@ -36087,6 +36088,108 @@ var init_render_fonts = __esm({
36087
36088
  }
36088
36089
  });
36089
36090
 
36091
+ // ../../core/dev-server/src/wix-cache-defaults.generated.ts
36092
+ var PLATFORM_DEFAULT_WIX_CACHE_RULES;
36093
+ var init_wix_cache_defaults_generated = __esm({
36094
+ "../../core/dev-server/src/wix-cache-defaults.generated.ts"() {
36095
+ PLATFORM_DEFAULT_WIX_CACHE_RULES = [
36096
+ { method: "POST", pathPrefix: "/_api/bookings/v2/services/search", cacheable: true, ttl: 60, swr: 86400, tag: "bookings/services" },
36097
+ { method: "POST", pathPrefix: "/blog/v3/categories/query", cacheable: true, ttl: 60, swr: 86400, tag: "blog" },
36098
+ { method: "GET", pathPrefix: "/blog/v3/posts/", cacheable: true, ttl: 60, swr: 86400, tag: "blog" },
36099
+ { method: "POST", pathPrefix: "/blog/v3/posts/query", cacheable: true, ttl: 60, swr: 86400, tag: "blog" },
36100
+ { method: "GET", pathPrefix: "/bookings/v2/services/", cacheable: true, ttl: 60, swr: 86400, tag: "bookings/services" },
36101
+ { method: "POST", pathPrefix: "/bookings/v2/services/query", cacheable: true, ttl: 60, swr: 86400, tag: "bookings/services" },
36102
+ { method: "GET", pathPrefix: "/donation-campaigns/v2/donation-campaigns/", cacheable: true, ttl: 60, swr: 86400, tag: "donations" },
36103
+ { method: "POST", pathPrefix: "/donation-campaigns/v2/donation-campaigns/query", cacheable: true, ttl: 60, swr: 86400, tag: "donations" },
36104
+ { method: "GET", pathPrefix: "/events/v3/events/", cacheable: true, ttl: 60, swr: 86400, tag: "events" },
36105
+ { method: "GET", pathPrefix: "/events/v3/events/category/", cacheable: true, ttl: 60, swr: 86400, tag: "events" },
36106
+ { method: "POST", pathPrefix: "/events/v3/events/query", cacheable: true, ttl: 60, swr: 86400, tag: "events" },
36107
+ { method: "GET", pathPrefix: "/events/v3/events/slug/", cacheable: true, ttl: 60, swr: 86400, tag: "events" },
36108
+ { method: "GET", pathPrefix: "/faq/v2/categories", cacheable: true, ttl: 60, swr: 86400, tag: "faq" },
36109
+ { method: "GET", pathPrefix: "/faq/v2/categories/", cacheable: true, ttl: 60, swr: 86400, tag: "faq" },
36110
+ { method: "POST", pathPrefix: "/faq/v2/categories/query", cacheable: true, ttl: 60, swr: 86400, tag: "faq" },
36111
+ { method: "GET", pathPrefix: "/faq/v2/question-entries", cacheable: true, ttl: 60, swr: 86400, tag: "faq" },
36112
+ { method: "GET", pathPrefix: "/faq/v2/question-entries/", cacheable: true, ttl: 60, swr: 86400, tag: "faq" },
36113
+ { method: "POST", pathPrefix: "/faq/v2/question-entries/query", cacheable: true, ttl: 60, swr: 86400, tag: "faq" },
36114
+ { method: "POST", pathPrefix: "/online-programs/v3/programs/query", cacheable: true, ttl: 60, swr: 86400, tag: "online-programs" },
36115
+ { method: "GET", pathPrefix: "/portfolio/v1/collections", cacheable: true, ttl: 60, swr: 86400, tag: "portfolio" },
36116
+ { method: "GET", pathPrefix: "/portfolio/v1/collections/", cacheable: true, ttl: 60, swr: 86400, tag: "portfolio" },
36117
+ { method: "POST", pathPrefix: "/portfolio/v1/collections/query", cacheable: true, ttl: 60, swr: 86400, tag: "portfolio" },
36118
+ { method: "GET", pathPrefix: "/portfolio/v1/projects", cacheable: true, ttl: 60, swr: 86400, tag: "portfolio" },
36119
+ { method: "GET", pathPrefix: "/portfolio/v1/projects/", cacheable: true, ttl: 60, swr: 86400, tag: "portfolio" },
36120
+ { method: "POST", pathPrefix: "/portfolio/v1/projects/collections/query", cacheable: true, ttl: 60, swr: 86400, tag: "portfolio" },
36121
+ { method: "POST", pathPrefix: "/portfolio/v1/projects/query", cacheable: true, ttl: 60, swr: 86400, tag: "portfolio" },
36122
+ { method: "GET", pathPrefix: "/pricing-plans/v2/plans/public", cacheable: true, ttl: 60, swr: 86400, tag: "pricing-plans" },
36123
+ { method: "POST", pathPrefix: "/pricing-plans/v2/plans/public/query", cacheable: true, ttl: 60, swr: 86400, tag: "pricing-plans" },
36124
+ { method: "GET", pathPrefix: "/restaurants/menus/v1/items/", cacheable: true, ttl: 60, swr: 86400, tag: "restaurants/menus" },
36125
+ { method: "POST", pathPrefix: "/restaurants/menus/v1/items/query", cacheable: true, ttl: 60, swr: 86400, tag: "restaurants/menus" },
36126
+ { method: "GET", pathPrefix: "/restaurants/menus/v1/menus/", cacheable: true, ttl: 60, swr: 86400, tag: "restaurants/menus" },
36127
+ { method: "POST", pathPrefix: "/restaurants/menus/v1/menus/query", cacheable: true, ttl: 60, swr: 86400, tag: "restaurants/menus" },
36128
+ { method: "POST", pathPrefix: "/restaurants/menus/v1/sections/query", cacheable: true, ttl: 60, swr: 86400, tag: "restaurants/menus" },
36129
+ { method: "GET", pathPrefix: "/reviews/v1/reviews/", cacheable: true, ttl: 60, swr: 86400, tag: "reviews" },
36130
+ { method: "POST", pathPrefix: "/reviews/v1/reviews/query", cacheable: true, ttl: 60, swr: 86400, tag: "reviews" },
36131
+ { method: "GET", pathPrefix: "/stores/v1/products/", cacheable: true, ttl: 60, swr: 86400, tag: "stores/products" },
36132
+ { method: "POST", pathPrefix: "/stores/v1/products/query", cacheable: true, ttl: 60, swr: 86400, tag: "stores/products" },
36133
+ { method: "POST", pathPrefix: "/stores/v3/categories/query", cacheable: true, ttl: 60, swr: 86400, tag: "stores/categories" },
36134
+ { method: "GET", pathPrefix: "/stores/v3/products/", cacheable: true, ttl: 60, swr: 86400, tag: "stores/products" },
36135
+ { method: "POST", pathPrefix: "/stores/v3/products/query", cacheable: true, ttl: 60, swr: 86400, tag: "stores/products" },
36136
+ { method: "POST", pathPrefix: "/stores/v3/products/search", cacheable: true, ttl: 60, swr: 86400, tag: "stores/products" },
36137
+ { method: "DELETE", pathPrefix: "/blog/v3/categories", cacheable: false, ttl: 0, swr: 0, tag: "blog" },
36138
+ { method: "PATCH", pathPrefix: "/blog/v3/categories", cacheable: false, ttl: 0, swr: 0, tag: "blog" },
36139
+ { method: "POST", pathPrefix: "/blog/v3/categories", cacheable: false, ttl: 0, swr: 0, tag: "blog" },
36140
+ { method: "DELETE", pathPrefix: "/blog/v3/posts", cacheable: false, ttl: 0, swr: 0, tag: "blog" },
36141
+ { method: "PATCH", pathPrefix: "/blog/v3/posts", cacheable: false, ttl: 0, swr: 0, tag: "blog" },
36142
+ { method: "POST", pathPrefix: "/blog/v3/posts", cacheable: false, ttl: 0, swr: 0, tag: "blog" },
36143
+ { method: "DELETE", pathPrefix: "/bookings/v1/services", cacheable: false, ttl: 0, swr: 0, tag: "bookings/services" },
36144
+ { method: "POST", pathPrefix: "/bookings/v1/services", cacheable: false, ttl: 0, swr: 0, tag: "bookings/services" },
36145
+ { method: "PUT", pathPrefix: "/bookings/v1/services", cacheable: false, ttl: 0, swr: 0, tag: "bookings/services" },
36146
+ { method: "DELETE", pathPrefix: "/bookings/v2/services", cacheable: false, ttl: 0, swr: 0, tag: "bookings/services" },
36147
+ { method: "PATCH", pathPrefix: "/bookings/v2/services", cacheable: false, ttl: 0, swr: 0, tag: "bookings/services" },
36148
+ { method: "POST", pathPrefix: "/bookings/v2/services", cacheable: false, ttl: 0, swr: 0, tag: "bookings/services" },
36149
+ { method: "PATCH", pathPrefix: "/donation-campaigns/v2/donation-campaigns", cacheable: false, ttl: 0, swr: 0, tag: "donations" },
36150
+ { method: "POST", pathPrefix: "/donation-campaigns/v2/donation-campaigns", cacheable: false, ttl: 0, swr: 0, tag: "donations" },
36151
+ { method: "PATCH", pathPrefix: "/events/v1/events", cacheable: false, ttl: 0, swr: 0, tag: "events" },
36152
+ { method: "POST", pathPrefix: "/events/v1/events", cacheable: false, ttl: 0, swr: 0, tag: "events" },
36153
+ { method: "POST", pathPrefix: "/events/v1/events/orders", cacheable: false, ttl: 0, swr: 0, tag: "events" },
36154
+ { method: "DELETE", pathPrefix: "/events/v3/events", cacheable: false, ttl: 0, swr: 0, tag: "events" },
36155
+ { method: "POST", pathPrefix: "/events/v3/events", cacheable: false, ttl: 0, swr: 0, tag: "events" },
36156
+ { method: "POST", pathPrefix: "/events/v3/events/count-by-status", cacheable: false, ttl: 0, swr: 0, tag: "events" },
36157
+ { method: "DELETE", pathPrefix: "/faq/v2/categories", cacheable: false, ttl: 0, swr: 0, tag: "faq" },
36158
+ { method: "PATCH", pathPrefix: "/faq/v2/categories", cacheable: false, ttl: 0, swr: 0, tag: "faq" },
36159
+ { method: "POST", pathPrefix: "/faq/v2/categories", cacheable: false, ttl: 0, swr: 0, tag: "faq" },
36160
+ { method: "DELETE", pathPrefix: "/faq/v2/question-entries", cacheable: false, ttl: 0, swr: 0, tag: "faq" },
36161
+ { method: "PATCH", pathPrefix: "/faq/v2/question-entries", cacheable: false, ttl: 0, swr: 0, tag: "faq" },
36162
+ { method: "POST", pathPrefix: "/faq/v2/question-entries", cacheable: false, ttl: 0, swr: 0, tag: "faq" },
36163
+ { method: "DELETE", pathPrefix: "/online-programs/v3/programs", cacheable: false, ttl: 0, swr: 0, tag: "online-programs" },
36164
+ { method: "PATCH", pathPrefix: "/online-programs/v3/programs", cacheable: false, ttl: 0, swr: 0, tag: "online-programs" },
36165
+ { method: "POST", pathPrefix: "/online-programs/v3/programs", cacheable: false, ttl: 0, swr: 0, tag: "online-programs" },
36166
+ { method: "DELETE", pathPrefix: "/portfolio/v1/collections", cacheable: false, ttl: 0, swr: 0, tag: "portfolio" },
36167
+ { method: "POST", pathPrefix: "/portfolio/v1/collections", cacheable: false, ttl: 0, swr: 0, tag: "portfolio" },
36168
+ { method: "DELETE", pathPrefix: "/portfolio/v1/projects", cacheable: false, ttl: 0, swr: 0, tag: "portfolio" },
36169
+ { method: "PATCH", pathPrefix: "/portfolio/v1/projects", cacheable: false, ttl: 0, swr: 0, tag: "portfolio" },
36170
+ { method: "DELETE", pathPrefix: "/pricing-plans/v2/plans", cacheable: false, ttl: 0, swr: 0, tag: "pricing-plans" },
36171
+ { method: "PATCH", pathPrefix: "/pricing-plans/v2/plans", cacheable: false, ttl: 0, swr: 0, tag: "pricing-plans" },
36172
+ { method: "POST", pathPrefix: "/pricing-plans/v2/plans", cacheable: false, ttl: 0, swr: 0, tag: "pricing-plans" },
36173
+ { method: "DELETE", pathPrefix: "/restaurants/menus/v1/sections", cacheable: false, ttl: 0, swr: 0, tag: "restaurants/menus" },
36174
+ { method: "PATCH", pathPrefix: "/restaurants/menus/v1/sections", cacheable: false, ttl: 0, swr: 0, tag: "restaurants/menus" },
36175
+ { method: "POST", pathPrefix: "/restaurants/menus/v1/sections", cacheable: false, ttl: 0, swr: 0, tag: "restaurants/menus" },
36176
+ { method: "DELETE", pathPrefix: "/reviews/v1/reviews", cacheable: false, ttl: 0, swr: 0, tag: "reviews" },
36177
+ { method: "PATCH", pathPrefix: "/reviews/v1/reviews", cacheable: false, ttl: 0, swr: 0, tag: "reviews" },
36178
+ { method: "POST", pathPrefix: "/reviews/v1/reviews", cacheable: false, ttl: 0, swr: 0, tag: "reviews" },
36179
+ { method: "POST", pathPrefix: "/reviews/v1/reviews/count", cacheable: false, ttl: 0, swr: 0, tag: "reviews" },
36180
+ { method: "DELETE", pathPrefix: "/stores/v1/products", cacheable: false, ttl: 0, swr: 0, tag: "stores/products" },
36181
+ { method: "PATCH", pathPrefix: "/stores/v1/products", cacheable: false, ttl: 0, swr: 0, tag: "stores/products" },
36182
+ { method: "POST", pathPrefix: "/stores/v1/products", cacheable: false, ttl: 0, swr: 0, tag: "stores/products" },
36183
+ { method: "DELETE", pathPrefix: "/stores/v3/categories", cacheable: false, ttl: 0, swr: 0, tag: "stores/categories" },
36184
+ { method: "PATCH", pathPrefix: "/stores/v3/categories", cacheable: false, ttl: 0, swr: 0, tag: "stores/categories" },
36185
+ { method: "POST", pathPrefix: "/stores/v3/categories", cacheable: false, ttl: 0, swr: 0, tag: "stores/categories" },
36186
+ { method: "DELETE", pathPrefix: "/stores/v3/products", cacheable: false, ttl: 0, swr: 0, tag: "stores/products" },
36187
+ { method: "PATCH", pathPrefix: "/stores/v3/products", cacheable: false, ttl: 0, swr: 0, tag: "stores/products" },
36188
+ { method: "POST", pathPrefix: "/stores/v3/products", cacheable: false, ttl: 0, swr: 0, tag: "stores/products" }
36189
+ ];
36190
+ }
36191
+ });
36192
+
36090
36193
  // ../../core/dev-server/src/wix-cache-policy.ts
36091
36194
  function clampRule(rule, denyPrefixes, maxTTL) {
36092
36195
  const denied2 = denyPrefixes.some((p) => rule.pathPrefix.startsWith(p));
@@ -36096,15 +36199,61 @@ function clampRule(rule, denyPrefixes, maxTTL) {
36096
36199
  ttl: Math.min(rule.ttl, maxTTL)
36097
36200
  };
36098
36201
  }
36099
- function resolveWixCachePolicy(appName2) {
36100
- const appRules = CACHE_OVERRIDES[appName2] ?? [];
36101
- const rules = appRules.map((r) => clampRule(r, PLATFORM_DENY_PREFIXES, PLATFORM_MAX_TTL));
36102
- return { rules, defaultCacheable: false };
36202
+ function shadows(own3, dflt) {
36203
+ const methodCovers = own3.method === null || own3.method.toUpperCase() === (dflt.method ?? "").toUpperCase();
36204
+ return methodCovers && dflt.pathPrefix.startsWith(own3.pathPrefix);
36103
36205
  }
36104
- var PLATFORM_DENY_PREFIXES, PLATFORM_MAX_TTL, CACHE_OVERRIDES;
36206
+ function resolveWixCachePolicy(appName2, declared) {
36207
+ const own3 = (declared?.rules ?? []).map((r) => ({
36208
+ method: r.method ?? null,
36209
+ pathPrefix: r.pathPrefix,
36210
+ cacheable: r.cacheable,
36211
+ ttl: r.ttl,
36212
+ swr: r.swr ?? 0,
36213
+ tag: r.tag ?? null
36214
+ }));
36215
+ const legacy = CACHE_OVERRIDES[appName2] ?? [];
36216
+ const appLevel = [...own3, ...legacy];
36217
+ const defaults = declared?.platformDefaults === false ? [] : PLATFORM_DEFAULT_WIX_CACHE_RULES.filter((d) => !appLevel.some((a) => shadows(a, d)));
36218
+ const denies = [
36219
+ ...[...PLATFORM_DENY_PREFIXES, ...PERSONAL_DATA_PREFIXES].map((pathPrefix) => ({ method: null, pathPrefix, cacheable: false, ttl: 0, swr: 0, tag: null })),
36220
+ ...PERSONAL_DATA_READS.map(([method, pathPrefix]) => ({ method, pathPrefix, cacheable: false, ttl: 0, swr: 0, tag: null }))
36221
+ ];
36222
+ const rules = [...appLevel, ...defaults, ...denies].map((r) => clampRule(r, PLATFORM_DENY_PREFIXES, PLATFORM_MAX_TTL));
36223
+ return { rules, defaultCacheable: declared?.defaultCacheable ?? false };
36224
+ }
36225
+ var PLATFORM_DENY_PREFIXES, PERSONAL_DATA_PREFIXES, PERSONAL_DATA_READS, PLATFORM_MAX_TTL, CACHE_OVERRIDES;
36105
36226
  var init_wix_cache_policy = __esm({
36106
36227
  "../../core/dev-server/src/wix-cache-policy.ts"() {
36228
+ init_wix_cache_defaults_generated();
36107
36229
  PLATFORM_DENY_PREFIXES = ["/stores/v2/orders", "/stores/v3/orders"];
36230
+ PERSONAL_DATA_PREFIXES = [
36231
+ "/ecom/v1/carts",
36232
+ "/ecom/v1/checkouts",
36233
+ "/ecom/v1/orders",
36234
+ "/members/",
36235
+ "/contacts/",
36236
+ "/invoices/",
36237
+ "/bookings/v1/bookings",
36238
+ "/bookings/v2/bookings",
36239
+ "/events/v1/orders",
36240
+ "/events/v1/tickets",
36241
+ "/events/v2/rsvps",
36242
+ "/gift-cards/",
36243
+ "/loyalty/",
36244
+ "/inbox/",
36245
+ "/pricing-plans/v2/orders",
36246
+ // Every answer below depends on who is asking, like the ones above — the generator's
36247
+ // NEVER_CACHED set (codegen/wix/scripts/gen-cache-defaults.ts), which the platform-defaults
36248
+ // fixture pins. Denied HERE so an app opting into `defaultCacheable` cannot cache them either
36249
+ // (tests/wix-cache-policy.test.ts walks that fixture against the fallback).
36250
+ "/social-groups-proxy/",
36251
+ "/stores/v2/coupons",
36252
+ "/analytics/",
36253
+ "/site-list/",
36254
+ "/blog/v3/draft-posts"
36255
+ ];
36256
+ PERSONAL_DATA_READS = [["GET", "/events/v1/events/"]];
36108
36257
  PLATFORM_MAX_TTL = 3600;
36109
36258
  CACHE_OVERRIDES = {
36110
36259
  // The reference fixture exercises the cache-policy contract (products cacheable).
@@ -36255,7 +36404,7 @@ function deriveNavigationConfig(app, bundleCtx) {
36255
36404
  scheme: app.urlScheme,
36256
36405
  deepLinkPatterns: app.deepLinkPatterns ?? ov?.deepLinkPatterns ?? [],
36257
36406
  linkableScreens,
36258
- wixCachePolicy: resolveWixCachePolicy(appName2),
36407
+ wixCachePolicy: resolveWixCachePolicy(appName2, app.cache),
36259
36408
  // Fail-open baseline; tightened OTA via the admin app-config override. The
36260
36409
  // DESTINATION(s) are derived generically from the app's config (no app-name
36261
36410
  // special-casing) so the app chooses where telemetry goes via OTA:
@@ -49076,6 +49225,7 @@ function readInitWatch(dir) {
49076
49225
  const r = JSON.parse(readFileSync52(file, "utf8"));
49077
49226
  if (r.v !== 1 || typeof r.pid !== "number" || !Number.isInteger(r.pid) || r.pid < 1) return null;
49078
49227
  if (typeof r.url !== "string" || !/^http:\/\/127\.0\.0\.1:\d+\//.test(r.url) || typeof r.board !== "string" || typeof r.log !== "string" || typeof r.startedAt !== "string") return null;
49228
+ if (r.root !== runRootId(dir)) return null;
49079
49229
  return r;
49080
49230
  } catch {
49081
49231
  return null;
@@ -49173,7 +49323,7 @@ async function startWatchAtInit(dir, opts) {
49173
49323
  });
49174
49324
  child.unref();
49175
49325
  if (!child.pid) return { skipped: "the live preview did not start (no process) \u2014 start it yourself: `himi run --watch --content <dir> &`" };
49176
- const record2 = { v: 1, pid: child.pid, url, board, log: WATCH_LOG, startedAt: now.toISOString() };
49326
+ const record2 = { v: 1, pid: child.pid, root: runRootId(dir), url, board, log: WATCH_LOG, startedAt: now.toISOString() };
49177
49327
  if (realDirIn(dir, ".himi")) writeFileAtomic(join46(dir, INIT_WATCH_FILE), `${JSON.stringify(record2, null, 2)}
49178
49328
  `);
49179
49329
  return { started: true, pid: child.pid, url, board, log: WATCH_LOG };
@@ -49233,6 +49383,7 @@ var init_milestones = __esm({
49233
49383
  "src/milestones.ts"() {
49234
49384
  init_app_source();
49235
49385
  init_preview();
49386
+ init_run_identity();
49236
49387
  init_progress();
49237
49388
  init_shell_quote();
49238
49389
  init_studio_reel();
@@ -53884,6 +54035,15 @@ function symlinkComponent(base, abs) {
53884
54035
  }
53885
54036
  return void 0;
53886
54037
  }
54038
+ function workspaceRefusal(root, target) {
54039
+ const r = resolve45(root);
54040
+ const t = resolve45(r, target);
54041
+ if (!existsSync54(r)) return `the workspace folder ${r} does not exist`;
54042
+ if (!within(r, t) || !within(realpathSync7(r), realish(t))) return `"${target}" is outside the workspace folder ${r}`;
54043
+ const link = symlinkComponent(r, t);
54044
+ if (link) return `"${link}" is a symlink \u2014 paths must stay in real folders inside the workspace`;
54045
+ return null;
54046
+ }
53887
54047
  async function confinedAppDir(args, deps) {
53888
54048
  const root = resolve45(await deps.defaultDir());
53889
54049
  const dir = typeof args.dir === "string" && args.dir ? resolve45(root, args.dir) : root;
@@ -54154,6 +54314,15 @@ function guarded(h) {
54154
54314
  return async (args, deps) => {
54155
54315
  const bad2 = flagShapedInputs(args);
54156
54316
  if (bad2.length) return text6(`refused: ${bad2.join(", ")} may not start with "-" (it would be read as a himi flag)`, true);
54317
+ if (deps.workspaceBoundary) {
54318
+ const base = await deps.defaultDir();
54319
+ for (const k of PATH_ARGS) {
54320
+ const v = args[k];
54321
+ if (typeof v !== "string" || !v) continue;
54322
+ const why = workspaceRefusal(deps.workspaceBoundary, resolve46(base, v));
54323
+ if (why) return text6(`refused: ${k} ${why}`, true);
54324
+ }
54325
+ }
54157
54326
  return h(args, deps);
54158
54327
  };
54159
54328
  }
@@ -54406,7 +54575,7 @@ ${log}` : ""}`, true);
54406
54575
  }
54407
54576
  ];
54408
54577
  }
54409
- var MAX_TEXT2, clip, text6, json3, opt, sw, DIR2, LEARN, CHECK, LOCAL_WRITE, PUBLISH, PREVIEW_HINT, LOGIN_HINT;
54578
+ var MAX_TEXT2, clip, text6, json3, opt, sw, DIR2, LEARN, CHECK, LOCAL_WRITE, PUBLISH, PREVIEW_HINT, LOGIN_HINT, PATH_ARGS;
54410
54579
  var init_tools = __esm({
54411
54580
  "src/mcp/tools.ts"() {
54412
54581
  init_file_tools();
@@ -54424,6 +54593,7 @@ var init_tools = __esm({
54424
54593
  PUBLISH = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true };
54425
54594
  PREVIEW_HINT = "The preview is already running: open `url` in your built-in browser \u2014 Claude Code desktop's browser pane, Cursor's Browser tool, the Browser in Codex inside the ChatGPT desktop app, VS Code's integrated browser. Do not also start the `himi-preview` launch configuration: it serves the same port, and is the way to start a preview only when none is running. A terminal-only agent should show the owner `himi_render` PNGs instead. `board` (when present) is the build board on the same origin.";
54426
54595
  LOGIN_HINT = "Not signed in to the himi serve plane. Call himi_login: it opens a browser window where the user signs in to Wix (this server never sees the credential). A user with a terminal can run `himi login --browser` instead. Then retry.";
54596
+ PATH_ARGS = ["dir", "parentDir", "file"];
54427
54597
  }
54428
54598
  });
54429
54599
 
@@ -54564,7 +54734,8 @@ async function startMcpServer(opts) {
54564
54734
  stop: (dir) => stopPreview(dir, previewDeps)
54565
54735
  },
54566
54736
  readFile: (p) => readFileSync60(p),
54567
- login: createLoginSession({ ...defaultLoginDeps, selfCommand: previewDeps.selfCommand, ...opts.login })
54737
+ login: createLoginSession({ ...defaultLoginDeps, selfCommand: previewDeps.selfCommand, ...opts.login }),
54738
+ ...opts.workspace ? { workspaceBoundary: opts.workspace } : {}
54568
54739
  };
54569
54740
  const closed = new Promise((resolve54) => {
54570
54741
  stdin.once("end", resolve54);
@@ -57253,21 +57424,21 @@ var init_preview_assets_generated = __esm({
57253
57424
  "../../packages/serve/src/preview-assets.generated.ts"() {
57254
57425
  init_preview_assets_format();
57255
57426
  PREVIEW_ASSET_MANIFEST = [
57256
- { key: "assets/index-DRJmxLKY.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1102618, sha256: "9976a79a9d6842770adfdc5605a486bef46bfe8a3570538b6f585fc1ef100f82" },
57257
- { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1102626, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
57258
- { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1107236, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
57259
- { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1595820, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
57260
- { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1686744, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
57261
- { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1777728, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
57262
- { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1868708, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
57263
- { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1959580, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
57264
- { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 2050560, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
57265
- { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2159196, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
57266
- { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2268280, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
57267
- { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2377328, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
57268
- { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2485028, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
57269
- { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2573136, length: 4947, sha256: "1217e0b9a45c53ee3c35fd8dfe751a50b2276661a3e699efde98c17152c1b960" },
57270
- { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2578083, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
57427
+ { key: "assets/index-nG7b3MPu.js", contentType: "application/javascript; charset=utf-8", offset: 8, length: 1104209, sha256: "88839c5eb15058f706e50664752a75646d690de5148f8b504eb82c0f87a70a15" },
57428
+ { key: "fonts/Roboto-LICENSE.txt", contentType: "application/octet-stream", offset: 1104217, length: 4610, sha256: "994062dd7af4186a739de55dd9e151c63b6301f8e2cbafc608d35c2e87da0cd2" },
57429
+ { key: "fonts/Roboto-Variable.ttf", contentType: "font/ttf", offset: 1108827, length: 488584, sha256: "d7598e12c5dbef095ff8272cfc55da0250bd07fbdecbac8a530b9b277872a134" },
57430
+ { key: "fonts/WixMadeforDisplay-Bold.ttf", contentType: "font/ttf", offset: 1597411, length: 90924, sha256: "ec4c7d63772d980d6e147b2df6215fd2737e4b04cebe0cf7e11dc695fb63cdcf" },
57431
+ { key: "fonts/WixMadeforDisplay-ExtraBold.ttf", contentType: "font/ttf", offset: 1688335, length: 90984, sha256: "bf4c16a2774edd38a921edf3099469866cd74c094fd596f3fa4e81f54a04dfaa" },
57432
+ { key: "fonts/WixMadeforDisplay-Medium.ttf", contentType: "font/ttf", offset: 1779319, length: 90980, sha256: "368faca44fb9f7207e4af784e94750b1ee59c1098b8689e0f4a918926f244fb5" },
57433
+ { key: "fonts/WixMadeforDisplay-Regular.ttf", contentType: "font/ttf", offset: 1870299, length: 90872, sha256: "97ed8f1a6772abe26e3d8af5cecfc4b02faa94497d585b0398b5053b739a70c1" },
57434
+ { key: "fonts/WixMadeforDisplay-SemiBold.ttf", contentType: "font/ttf", offset: 1961171, length: 90980, sha256: "642feab8a48ddf6c7a89d7c2a7c0edfa0e6e773f671287e460e222703563191e" },
57435
+ { key: "fonts/WixMadeforText-Bold.ttf", contentType: "font/ttf", offset: 2052151, length: 108636, sha256: "37c179b4fefb63ea9637322b84583a2df53aa226c150294eea1675c14b1b8e29" },
57436
+ { key: "fonts/WixMadeforText-ExtraBold.ttf", contentType: "font/ttf", offset: 2160787, length: 109084, sha256: "62e2fe4718129f36ecdb0ae4de385bd6b81ecc876146447167c6c140fad699a9" },
57437
+ { key: "fonts/WixMadeforText-Medium.ttf", contentType: "font/ttf", offset: 2269871, length: 109048, sha256: "4ecf270136af928612c76fe4ebade91d8156975e5ea4c0cf9225501dd1788e02" },
57438
+ { key: "fonts/WixMadeforText-Regular.ttf", contentType: "font/ttf", offset: 2378919, length: 107700, sha256: "dee56f09fd3efc127c1ea95557edc74110c1460a46287da36e0af25efd341ef5" },
57439
+ { key: "fonts/WixMadeforText-SemiBold.ttf", contentType: "font/ttf", offset: 2486619, length: 88108, sha256: "dced97a77624bc5f8b1ae65850360908297f4588e09feb6f22792d29dc7e765b" },
57440
+ { key: "index.html", contentType: "text/html; charset=utf-8", offset: 2574727, length: 4947, sha256: "5c0a3e1b1d5bda7b6bc8cc2a025321bf6d0aa74d1f14b073861f188fd7b13a8b" },
57441
+ { key: "t5-frame.html", contentType: "text/html; charset=utf-8", offset: 2579674, length: 17209, sha256: "dffc9718e2faa63ae2cc919e0a842f585ed565aaaa7c53778e90379442c731b6" }
57271
57442
  ];
57272
57443
  }
57273
57444
  });
@@ -57516,6 +57687,7 @@ __export(run_watch_exports, {
57516
57687
  isEmptyDiff: () => isEmptyDiff,
57517
57688
  rebuildBlocker: () => rebuildBlocker,
57518
57689
  reloadEvents: () => reloadEvents,
57690
+ removeInitWatchRecord: () => removeInitWatchRecord,
57519
57691
  removeRunRecord: () => removeRunRecord,
57520
57692
  serialize: () => serialize,
57521
57693
  watchContent: () => watchContent,
@@ -57685,6 +57857,24 @@ function removeRunRecord(dir, owner) {
57685
57857
  }
57686
57858
  rmSync15(join59(root, "run.json"), { force: true });
57687
57859
  }
57860
+ function removeInitWatchRecord(dir, owner) {
57861
+ const root = join59(dir, ".himi");
57862
+ try {
57863
+ if (!lstatSync13(root).isDirectory()) return;
57864
+ } catch {
57865
+ return;
57866
+ }
57867
+ const bytes2 = readConfined(root, "watch.json");
57868
+ if (!bytes2) return;
57869
+ let pid;
57870
+ try {
57871
+ pid = JSON.parse(bytes2.toString("utf8")).pid;
57872
+ } catch {
57873
+ return;
57874
+ }
57875
+ if (pid !== owner) return;
57876
+ rmSync15(join59(root, "watch.json"), { force: true });
57877
+ }
57688
57878
  function boardFile(dir, pathname) {
57689
57879
  if (pathname === RUN_IDENTITY_PATH) {
57690
57880
  return new Response(JSON.stringify(runIdentity(dir)), { status: 200, headers: { "content-type": "application/json", "cache-control": "no-store" } });
@@ -59412,7 +59602,7 @@ var init_push_send = __esm({
59412
59602
  });
59413
59603
 
59414
59604
  // ../../core/serve-control/src/push-send-service.ts
59415
- var PUSH_SEND_ERROR_CODES;
59605
+ var PUSH_SEND_ERROR_CODES, PushSendError;
59416
59606
  var init_push_send_service = __esm({
59417
59607
  "../../core/serve-control/src/push-send-service.ts"() {
59418
59608
  PUSH_SEND_ERROR_CODES = [
@@ -59454,6 +59644,160 @@ var init_push_send_service = __esm({
59454
59644
  "ping_rate_limited",
59455
59645
  "send_outcome_unknown"
59456
59646
  ];
59647
+ PushSendError = class extends Error {
59648
+ constructor(code, status, retryAfterSec) {
59649
+ super(code);
59650
+ this.code = code;
59651
+ this.status = status;
59652
+ this.retryAfterSec = retryAfterSec;
59653
+ }
59654
+ };
59655
+ }
59656
+ });
59657
+
59658
+ // ../../core/serve-control/src/site-member.ts
59659
+ async function assertOwnerSiteToken(input, fetchImpl = input.fetch ?? fetch) {
59660
+ const info2 = await upstreamJson(fetchImpl, {
59661
+ path: "/oauth2/token-info",
59662
+ body: { token: input.siteToken },
59663
+ unavailable: "site_token_unverifiable",
59664
+ // An expired, revoked or malformed token is answered with a 4xx: the caller's to fix, not an outage.
59665
+ rejected: { code: "site_token_invalid", when: (status) => status >= 400 && status < 500 }
59666
+ });
59667
+ const owned = info2.active === true && info2.subjectType === "USER" && info2.siteId === input.site && info2.accountId === input.ownerAccountId;
59668
+ if (!owned) throw new PushSendError("site_token_invalid", 403);
59669
+ }
59670
+ async function upstreamJson(fetchImpl, call) {
59671
+ let response;
59672
+ try {
59673
+ response = await fetchImpl(`${WIXAPIS}${call.path}`, {
59674
+ method: "POST",
59675
+ redirect: "error",
59676
+ signal: AbortSignal.timeout(TIMEOUT_MS2),
59677
+ headers: {
59678
+ "content-type": "application/json",
59679
+ accept: "application/json",
59680
+ ...call.authorization ? { Authorization: call.authorization } : {},
59681
+ ...call.siteId ? { "wix-site-id": call.siteId } : {}
59682
+ },
59683
+ body: JSON.stringify(call.body)
59684
+ });
59685
+ } catch {
59686
+ throw new PushSendError(call.unavailable, 503);
59687
+ }
59688
+ if (call.rejected?.when(response.status)) throw new PushSendError(call.rejected.code, 403);
59689
+ if (!response.ok) throw new PushSendError(call.unavailable, 503);
59690
+ try {
59691
+ return await response.json();
59692
+ } catch {
59693
+ throw new PushSendError(call.unavailable, 503);
59694
+ }
59695
+ }
59696
+ var WIXAPIS, TIMEOUT_MS2;
59697
+ var init_site_member = __esm({
59698
+ "../../core/serve-control/src/site-member.ts"() {
59699
+ init_push_send_service();
59700
+ WIXAPIS = "https://www.wixapis.com";
59701
+ TIMEOUT_MS2 = 1e4;
59702
+ }
59703
+ });
59704
+
59705
+ // src/notify-ping-all.ts
59706
+ import { createHash as createHash31 } from "node:crypto";
59707
+ async function listApprovedMembers(input) {
59708
+ const first = await scanMembers(input);
59709
+ const second = await scanMembers(input);
59710
+ if (JSON.stringify(first.fingerprint) !== JSON.stringify(second.fingerprint)) throw new Error("member_roster_unstable");
59711
+ return second.recipients;
59712
+ }
59713
+ async function scanMembers(input) {
59714
+ const recipients = [];
59715
+ const fingerprint = [];
59716
+ const seenIds = /* @__PURE__ */ new Set();
59717
+ const seenEmails = /* @__PURE__ */ new Set();
59718
+ const fetchImpl = input.fetchImpl ?? fetch;
59719
+ for (let offset = 0; ; ) {
59720
+ let response;
59721
+ try {
59722
+ response = await fetchImpl(`${WIXAPIS2}/members/v1/members/query`, {
59723
+ method: "POST",
59724
+ headers: { Authorization: input.siteToken, "wix-site-id": input.site, "content-type": "application/json", accept: "application/json" },
59725
+ redirect: "error",
59726
+ signal: AbortSignal.timeout(1e4),
59727
+ body: JSON.stringify({ query: { paging: { limit: PAGE_SIZE, offset } }, fieldsets: ["FULL"] })
59728
+ });
59729
+ } catch {
59730
+ throw new Error("member_lookup_unavailable");
59731
+ }
59732
+ if (!response.ok) throw new Error("member_lookup_unavailable");
59733
+ let raw;
59734
+ try {
59735
+ raw = await response.json();
59736
+ } catch {
59737
+ throw new Error("member_lookup_unavailable");
59738
+ }
59739
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) throw new Error("member_lookup_unavailable");
59740
+ const result = raw;
59741
+ if (!Array.isArray(result.members)) throw new Error("member_lookup_unavailable");
59742
+ if (result.members.length === 0) return { recipients, fingerprint };
59743
+ if (result.members.length > PAGE_SIZE) throw new Error("member_roster_unstable");
59744
+ for (const member of result.members) {
59745
+ if (member === null || typeof member !== "object" || Array.isArray(member)) throw new Error("member_lookup_unavailable");
59746
+ if (typeof member.id !== "string" || !member.id || seenIds.has(member.id)) throw new Error("member_roster_unstable");
59747
+ seenIds.add(member.id);
59748
+ fingerprint.push(JSON.stringify([member.id, member.loginEmail, member.contactId, member.status]));
59749
+ if (member.status !== "APPROVED") continue;
59750
+ if (typeof member.loginEmail !== "string" || !member.loginEmail || typeof member.contactId !== "string" || !member.contactId) throw new Error("member_roster_incomplete");
59751
+ const email = member.loginEmail.toLowerCase();
59752
+ if (seenEmails.has(email)) throw new Error("member_ambiguous");
59753
+ seenEmails.add(email);
59754
+ recipients.push({ id: member.id, email: member.loginEmail });
59755
+ }
59756
+ offset += result.members.length;
59757
+ }
59758
+ }
59759
+ function campaignRequestId(campaignId, memberId) {
59760
+ const bytes2 = createHash31("sha256").update(JSON.stringify(["himi-push-campaign", campaignId, memberId])).digest().subarray(0, 16);
59761
+ bytes2[6] = bytes2[6] & 15 | 80;
59762
+ bytes2[8] = bytes2[8] & 63 | 128;
59763
+ const hex2 = bytes2.toString("hex");
59764
+ return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-${hex2.slice(12, 16)}-${hex2.slice(16, 20)}-${hex2.slice(20)}`;
59765
+ }
59766
+ async function sendCampaign(input) {
59767
+ let accepted = 0;
59768
+ let deduplicated = 0;
59769
+ for (const [index, recipient] of input.recipients.entries()) {
59770
+ if (index > 0) await input.wait(6100);
59771
+ const requestId2 = campaignRequestId(input.campaignId, recipient.id);
59772
+ let outcome = await input.send(recipient, requestId2);
59773
+ let retries = 0;
59774
+ while (outcome.state === "rate_limited" && outcome.retryAfterSec > 0 && retries < 3) {
59775
+ retries += 1;
59776
+ await input.wait(outcome.retryAfterSec * 1e3);
59777
+ outcome = await input.send(recipient, requestId2);
59778
+ }
59779
+ if (outcome.state !== "accepted") {
59780
+ throw new CampaignFailure(outcome.state === "failed" ? outcome.code : "rate_limited", index, recipient.email);
59781
+ }
59782
+ if (outcome.deduplicated) deduplicated += 1;
59783
+ else accepted += 1;
59784
+ input.onProgress?.(index + 1);
59785
+ }
59786
+ return { accepted, deduplicated };
59787
+ }
59788
+ var CampaignFailure, PAGE_SIZE, WIXAPIS2;
59789
+ var init_notify_ping_all = __esm({
59790
+ "src/notify-ping-all.ts"() {
59791
+ CampaignFailure = class extends Error {
59792
+ constructor(code, completed, memberEmail) {
59793
+ super(code);
59794
+ this.code = code;
59795
+ this.completed = completed;
59796
+ this.memberEmail = memberEmail;
59797
+ }
59798
+ };
59799
+ PAGE_SIZE = 100;
59800
+ WIXAPIS2 = "https://www.wixapis.com";
59457
59801
  }
59458
59802
  });
59459
59803
 
@@ -59471,8 +59815,11 @@ function refuse5(io, error, hint, exit = 2, extra = {}) {
59471
59815
  async function runNotifyPing(flags, io, deps = {}) {
59472
59816
  const flagError = flagRefusal(flags);
59473
59817
  if (flagError) return refuse5(io, flagError);
59818
+ const allMembers = flags["all-members"] === true;
59819
+ if (allMembers && text7(flags["member-email"])) return refuse5(io, "recipient_conflict", "Choose --all-members or --member-email.");
59820
+ if (allMembers && flags["dry-run"] !== true && flags.yes !== true) return refuse5(io, "confirmation_required", "Preview with --dry-run, then add --yes to send to every approved member.");
59474
59821
  const input = mergePushInputs(pushInputsFromContent(resolve50(text7(flags.content) ?? process.cwd())), flags);
59475
- const missing = [...input.app ? [] : ["app"], ...input.site ? [] : ["site"], ...REQUIRED_FLAGS.filter((f) => !text7(flags[f]))];
59822
+ const missing = [...input.app ? [] : ["app"], ...input.site ? [] : ["site"], ...REQUIRED_FLAGS.filter((f) => !(allMembers && f === "member-email") && !text7(flags[f]))];
59476
59823
  if (missing.length) return refuse5(io, "needs_input", "Name the app and site (or --content), the member's login email, a title and a link.", 2, { missing });
59477
59824
  const requestId2 = text7(flags["request-id"]) ?? (deps.newRequestId ?? randomUUID5)();
59478
59825
  if (!UUID_RE.test(requestId2)) return refuse5(io, "invalid_request_id", "Reuse the requestId a previous send printed.");
@@ -59487,6 +59834,7 @@ async function runNotifyPing(flags, io, deps = {}) {
59487
59834
  } catch {
59488
59835
  return refuse5(io, "wix_site_token_required", "Run wix login as the site owner; the site token reads this site's members.", 1);
59489
59836
  }
59837
+ if (allMembers) return runAllMembers({ input: { app: ownership.slug, site: input.site }, siteToken: siteToken2, session, requestId: requestId2, flags, io, deps });
59490
59838
  const body = {
59491
59839
  app: ownership.slug,
59492
59840
  site: input.site,
@@ -59500,6 +59848,88 @@ async function runNotifyPing(flags, io, deps = {}) {
59500
59848
  const http = await pushSendRequest({ target: session.target, wixToken: session.wixToken, siteToken: siteToken2, body, previewTag: session.previewTag, fetchImpl: deps.fetchImpl });
59501
59849
  return report2(http, body, flags, io);
59502
59850
  }
59851
+ async function runAllMembers(args) {
59852
+ const { input, siteToken: siteToken2, session, requestId: requestId2, flags, io, deps } = args;
59853
+ const ownerAccountId = decodeControlTokenClaims(session.controlToken)?.sub;
59854
+ if (!ownerAccountId) return refuse5(io, "owner_login_required");
59855
+ const readiness = await pushSetupRequest({
59856
+ target: session.target,
59857
+ wixToken: session.wixToken,
59858
+ operation: "status",
59859
+ query: input,
59860
+ previewTag: session.previewTag,
59861
+ fetchImpl: deps.fetchImpl
59862
+ });
59863
+ if (!readiness.ok) {
59864
+ const reported = String(readiness.body?.error ?? "");
59865
+ const code = KNOWN_CODES.has(reported) ? reported : "push_setup_unavailable";
59866
+ return refuse5(io, code, HINTS[code], 1);
59867
+ }
59868
+ if (readiness.body?.app !== input.app || readiness.body?.site !== input.site || readiness.body?.state !== "ready") {
59869
+ return refuse5(io, "push_not_ready", HINTS.push_not_ready, 1);
59870
+ }
59871
+ let recipients;
59872
+ try {
59873
+ await assertOwnerSiteToken({
59874
+ site: input.site,
59875
+ siteToken: siteToken2,
59876
+ ownerAccountId,
59877
+ fetch: deps.fetchImpl
59878
+ });
59879
+ recipients = await listApprovedMembers({ site: input.site, siteToken: siteToken2, fetchImpl: deps.fetchImpl });
59880
+ } catch (error) {
59881
+ const code = error instanceof PushSendError ? error.code : error instanceof Error && ROSTER_CODES.has(error.message) ? error.message : "member_lookup_unavailable";
59882
+ return refuse5(io, code, void 0, 1);
59883
+ }
59884
+ const base = { app: input.app, site: input.site, title: text7(flags.title), ...text7(flags.body) ? { body: text7(flags.body) } : {}, link: text7(flags.link) };
59885
+ const send2 = (recipient, memberRequestId, dryRun = false) => pushSendRequest({
59886
+ target: session.target,
59887
+ wixToken: session.wixToken,
59888
+ siteToken: siteToken2,
59889
+ previewTag: session.previewTag,
59890
+ fetchImpl: deps.fetchImpl,
59891
+ body: { ...base, memberEmail: recipient.email, requestId: memberRequestId, ...dryRun ? { dryRun: true } : {} }
59892
+ });
59893
+ if (flags["dry-run"] === true) {
59894
+ if (recipients.length) {
59895
+ const result = await send2(recipients[0], requestId2, true);
59896
+ const code = refusalCode(result);
59897
+ if (code) return refuse5(io, code, HINTS[code], 1);
59898
+ }
59899
+ io.log(JSON.stringify({ ok: true, state: "dry_run", campaignId: requestId2, recipients: recipients.length, sent: false }));
59900
+ return 0;
59901
+ }
59902
+ io.log(JSON.stringify({ ok: true, state: "started", campaignId: requestId2, recipients: recipients.length }));
59903
+ try {
59904
+ const result = await sendCampaign({
59905
+ recipients,
59906
+ campaignId: requestId2,
59907
+ wait: deps.wait ?? ((ms) => new Promise((resolve54) => setTimeout(resolve54, ms))),
59908
+ onProgress: (completed) => {
59909
+ if (completed % 10 === 0 || completed === recipients.length) {
59910
+ io.log(JSON.stringify({ ok: true, state: "progress", campaignId: requestId2, completed, recipients: recipients.length }));
59911
+ }
59912
+ },
59913
+ send: async (recipient, memberRequestId) => {
59914
+ const response = await send2(recipient, memberRequestId);
59915
+ const code = refusalCode(response);
59916
+ if (code === "rate_limited" && response.retryAfterSec) return { state: "rate_limited", retryAfterSec: response.retryAfterSec };
59917
+ if (code) return { state: "failed", code };
59918
+ return { state: "accepted", deduplicated: response.body?.deduplicated === true };
59919
+ }
59920
+ });
59921
+ io.log(JSON.stringify({ ok: true, state: "completed", campaignId: requestId2, recipients: recipients.length, ...result }));
59922
+ return 0;
59923
+ } catch (error) {
59924
+ if (!(error instanceof CampaignFailure)) return refuse5(io, "send_outcome_unknown", void 0, 1, { campaignId: requestId2 });
59925
+ const hint = error.code === "send_outcome_unknown" || error.code === "rate_limited" ? `Re-run the same command with --request-id ${requestId2} within Ping's 24-hour deduplication window.` : HINTS[error.code];
59926
+ return refuse5(io, error.code, hint, 1, {
59927
+ campaignId: requestId2,
59928
+ completed: error.completed,
59929
+ memberEmail: error.memberEmail
59930
+ });
59931
+ }
59932
+ }
59503
59933
  function flagRefusal(flags) {
59504
59934
  if (LOCAL_SENDER_FLAGS.some((f) => flags[f] !== void 0)) return "flag_not_supported_with_ping";
59505
59935
  if (SWITCHES.some((f) => flags[f] !== void 0 && typeof flags[f] !== "boolean")) return "invalid_boolean_flag";
@@ -59534,20 +59964,24 @@ function summary(result, sent) {
59534
59964
  }
59535
59965
  return `Ping accepted request ${sent.requestId} for ${sent.memberEmail}. Ping does not confirm delivery: it reaches the member's signed-in devices with notifications allowed.`;
59536
59966
  }
59537
- var LOCAL_SENDER_FLAGS, SWITCHES, REQUIRED_FLAGS, UUID_RE, KNOWN_CODES, HINTS, text7;
59967
+ var LOCAL_SENDER_FLAGS, SWITCHES, REQUIRED_FLAGS, UUID_RE, KNOWN_CODES, ROSTER_CODES, HINTS, text7;
59538
59968
  var init_notify_ping = __esm({
59539
59969
  "src/notify-ping.ts"() {
59540
59970
  init_push_send();
59971
+ init_push_setup();
59972
+ init_site_member();
59541
59973
  init_push_send_service();
59542
59974
  init_hosted_owner();
59975
+ init_notify_ping_all();
59543
59976
  init_owner_claim();
59544
59977
  init_push_onboarding();
59545
59978
  init_wix_owner();
59546
59979
  LOCAL_SENDER_FLAGS = ["token", "device", "all", "admin-url", "admin-session", "silent", "badge", "data", "category", "channel", "platform", "bundle-id"];
59547
- SWITCHES = ["json", "dry-run"];
59980
+ SWITCHES = ["json", "dry-run", "all-members"];
59548
59981
  REQUIRED_FLAGS = ["member-email", "title", "link"];
59549
59982
  UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
59550
59983
  KNOWN_CODES = new Set(PUSH_SEND_ERROR_CODES);
59984
+ ROSTER_CODES = /* @__PURE__ */ new Set(["member_lookup_unavailable", "member_roster_unstable", "member_roster_incomplete", "member_ambiguous"]);
59551
59985
  HINTS = {
59552
59986
  push_not_ready: "Run himi notify setup for this app and site first; status must be ready.",
59553
59987
  member_not_found: "Use the login email of an approved member of this site.",
@@ -63882,8 +64316,8 @@ async function applyOverlays(input) {
63882
64316
  if (bringsBrand) {
63883
64317
  const accent = tokens.colors?.accentPrimary;
63884
64318
  const primary = typeof accent === "string" ? accent : accent?.light;
63885
- const shadows = merged.shadows;
63886
- const glow = shadows && typeof shadows === "object" && !Array.isArray(shadows) ? shadows[BRAND_GLOW_SHADOW] : void 0;
64319
+ const shadows2 = merged.shadows;
64320
+ const glow = shadows2 && typeof shadows2 === "object" && !Array.isArray(shadows2) ? shadows2[BRAND_GLOW_SHADOW] : void 0;
63887
64321
  if (primary && glow && typeof glow.color === "string") glow.color = primary;
63888
64322
  stripBrandFromIdiom(merged);
63889
64323
  }
@@ -69498,7 +69932,7 @@ var init_context = __esm({
69498
69932
  import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync80, mkdirSync as mkdirSync33, writeFileSync as writeFileSync29, existsSync as existsSync67, rmSync as rmSync16, readdirSync as readdirSync31, statSync as statSync19, lstatSync as lstatSync14, accessSync, constants as constants3, renameSync as renameSync8, cpSync as cpSync3 } from "node:fs";
69499
69933
  import { execFile, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn7 } from "node:child_process";
69500
69934
  import { AsyncLocalStorage } from "node:async_hooks";
69501
- import { createHash as createHash31, randomUUID as randomUUID7 } from "node:crypto";
69935
+ import { createHash as createHash32, randomUUID as randomUUID7 } from "node:crypto";
69502
69936
  import { tmpdir as tmpdir5, homedir as homedir7, hostname } from "node:os";
69503
69937
  import { join as join65, resolve as resolve53, relative as relative14, basename as basename17, dirname as dirname37 } from "node:path";
69504
69938
  import { fileURLToPath as fileURLToPath26 } from "node:url";
@@ -71520,6 +71954,7 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
71520
71954
  "allow-destructive",
71521
71955
  "allow-draft",
71522
71956
  "all",
71957
+ "all-members",
71523
71958
  "all-screens",
71524
71959
  "ai",
71525
71960
  "binary",
@@ -71554,6 +71989,7 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
71554
71989
  "wait",
71555
71990
  "wix",
71556
71991
  "silent",
71992
+ "with-files",
71557
71993
  "include-disabled",
71558
71994
  "skip-build",
71559
71995
  "refresh",
@@ -72120,6 +72556,12 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
72120
72556
  wix login as the site owner, and a ready notify setup. Acceptance is not delivery.
72121
72557
  --dry-run checks ownership, member, link and readiness without sending. After an unknown
72122
72558
  outcome, re-run the same command with the printed --request-id; Ping never runs it twice.
72559
+ himi notify send --provider ping --target wix --app <a> --site <msid> --all-members
72560
+ --title <s> [--body <s>] --link <scheme>://screen/<id>[?\u2026] [--dry-run | --yes] [--request-id <uuid>]
72561
+ Sends to every approved site member with a login email. --dry-run counts recipients and
72562
+ checks one member's server gates; --yes is required to send. The CLI paces sends for the
72563
+ owner's 10/minute limit. Reuse the campaign ID with --request-id after an interrupted run
72564
+ within Ping's 24-hour deduplication window. Acceptance is not delivery.
72123
72565
  himi notify send --app <a> (--token <t> | --device <id> | --all) --title <s> [--body <s>] [--link <url>]
72124
72566
  deliver a REAL push notification to registered devices \u2014
72125
72567
  APNs (.p8) for iOS, FCM v1 for Android. --all fans out to
@@ -72312,7 +72754,7 @@ var COMMANDS = [
72312
72754
  { name: "icon", summary: "the app's LAUNCHER icon \u2014 the home-screen / store mark, which is BINARY-floor and never over-the-air (distinct from `himi icons`, the in-screen glyph vocabulary). `status`/`check` report what this app would actually ship and validate it against both stores: exact 1024x1024, no tRNS chunk, non-interlaced, and \u2014 when the optional image toolchain is installed \u2014 no transparent regions (Apple permits an alpha channel but not transparent pixels: ITMS-90717), no pre-masked corners, no letterboxed wordmark; they also print the store-policy checklist no checker can decide. `set <file.png>` installs supplied art, `from-site` installs the logo `himi site analyze` downloaded, and `generate` makes a gradient tile + glyph \u2014 all three trim, flatten onto the app's accent, write the 1024 opaque master plus the 512 Play listing icon (Play wants 32-bit WITH alpha, so it is a separate file), and DECLARE it in himalaya.content.json, without which the release bakes no icon at all", required: "[status|check] | set <file.png> | from-site [--book <dir>] | generate [--glyph <g>] [--accent <hex>] | glyphs [--content <dir>] [--json]" },
72313
72755
  { name: "login", summary: "obtain a scoped serve-control token from Wix identity and store it in ~/.himalaya/serve-targets.json (so push/promote need no hand-pasted token). Three modes: --browser (your own Wix account, no VPN/BO), --wix (HEADLESS \u2014 the machine's own Wix CLI credential; the mode a CI pod or agent container can satisfy), --dpx (Wix employee, reads ~/.dpx/session/token). RFC 0026 WS3.", required: "[--browser|--wix|--dpx] [--target <t>] [--issue-url <url>]" },
72314
72756
  { name: "logout", summary: "remove the stored control token for a target", required: "[--target <t>]" },
72315
- { name: "notify", summary: "`setup` optionally configures Branded/Ping from local customer key files; `status --refresh` reads live backend readiness. Setup may be deferred while building. These use serve-control and require owner plus Wix account authentication. `send`, `tokens`, `doctor`: send a REAL push notification to registered devices (RFC 0036 WS5) \u2014 APNs .p8 over HTTP/2 for iOS, FCM v1 for Android. `send` targets one device (--token / --device <registry id>) or fans out to the whole app (--all), reporting a per-device result; a token the provider says is uninstalled (APNs 410 Unregistered / FCM UNREGISTERED) is auto-disabled, while a merely wrong one (BadDeviceToken \u2014 usually a sandbox/production mismatch) is recorded and left enabled. `tokens` lists the registry. `doctor` reports which credentials resolve and which sender each platform picks. Talks to the admin-server via --admin-url/HIMALAYA_ADMIN_URL, authenticated with --admin-session/HIMALAYA_ADMIN_SESSION. `send --provider ping` is the hosted alternative: the serve plane sends one test notification to one site member through Ping as Himalaya, with owner login instead of provider keys, and reports acceptance, never delivery. NOT `himi push`, which publishes a release.", required: "setup|status [--content <dir>] [--target <t>] [--refresh] [--yes] [--no-push-setup] | send --provider ping [--content <dir>] [--app <a>] [--site <msid>] --member-email <email> --title <s> [--body <s>] --link <url> [--request-id <uuid>] [--dry-run] [--json] | send|tokens|doctor [--app <a>] [--token <t> | --device <id> | --all] [--title <s>] [--body <s>] [--link <url>] [--platform ios|android] [--badge <n>] [--channel <id>] [--silent] [--dry-run] [--json]" },
72757
+ { name: "notify", summary: "Hosted --all-members requires --yes for a live send; --dry-run checks ownership/readiness and counts approved recipients without sending. `setup` optionally configures Branded/Ping from local customer key files; `status --refresh` reads live backend readiness. Setup may be deferred while building. These use serve-control and require owner plus Wix account authentication. `send`, `tokens`, `doctor`: send a REAL push notification to registered devices (RFC 0036 WS5) \u2014 APNs .p8 over HTTP/2 for iOS, FCM v1 for Android. `send` targets one device (--token / --device <registry id>) or fans out to the whole app (--all), reporting a per-device result; a token the provider says is uninstalled (APNs 410 Unregistered / FCM UNREGISTERED) is auto-disabled, while a merely wrong one (BadDeviceToken \u2014 usually a sandbox/production mismatch) is recorded and left enabled. `tokens` lists the registry. `doctor` reports which credentials resolve and which sender each platform picks. Talks to the admin-server via --admin-url/HIMALAYA_ADMIN_URL, authenticated with --admin-session/HIMALAYA_ADMIN_SESSION. `send --provider ping` is the hosted alternative: the serve plane sends to one site member (--member-email) or all approved members (--all-members) through Ping as Himalaya, with owner login instead of provider keys, and reports acceptance, never delivery. NOT `himi push`, which publishes a release.", required: "setup|status [--content <dir>] [--target <t>] [--refresh] [--yes] [--no-push-setup] | send --provider ping [--content <dir>] [--app <a>] [--site <msid>] (--member-email <email> | --all-members (--dry-run | --yes)) --title <s> [--body <s>] --link <url> [--request-id <uuid>] [--dry-run] [--json] | send|tokens|doctor [--app <a>] [--token <t> | --device <id> | --all] [--title <s>] [--body <s>] [--link <url>] [--platform ios|android] [--badge <n>] [--channel <id>] [--silent] [--dry-run] [--json]" },
72316
72758
  { name: "site", summary: "PROVISION a Wix project, then analyze or extend it. `create` is the one door that makes a site instead of adopting one: POST /funnel/projects/v1/create (a WIX site, a HEADLESS backend, a BRANDED_APP, or a VIBE site), and for headless it also mints the OAuth app whose id IS the `WIX_CLIENT_ID` \u2014 with --redirect-uri already registered, because an unregistered redirect makes native signInDirect fail at redirect-session. `--verticals` REQUIRES `--template` and reports only what Wix confirmed: measured, installing onto a project created from the BLANK template returns 200 while the vertical's API answers 428 forever, because there is no site for it to attach to (the install asks for CREATE_SITE). A real template ships its verticals installed and populated, and installing further ones onto it then works. It MUTATES your Wix account and needs --yes; without it you get the payload and nothing is sent. `list` is the read-only site inventory (with each site's premium flag, which is what explains an installed-but-428 vertical). Auth is the account-scoped `wix token` (no --site), falling back to WIX_API_KEY + WIX_ACCOUNT_ID. `client` is the same OAuth step for a site that ALREADY exists \u2014 the one himi adopts rather than makes \u2014 so a clientId no longer means a manual trip to the dashboard: `client ensure` reconciles rather than mints, because a site can already carry a client with NO registered redirect and shipping that one fails signInDirect at runtime; it reuses a client that carries --redirect-uri, PATCHes one that matches --name but lacks it (unioning, so another app's redirect survives), and creates only when there is nothing to reuse (--new forces a fresh one). --name IS the idempotency key. `client` auth is SITE-scoped (`wix token --site <msid>`) because minting is owner-of-that-site, and --yes gates every mutation: a site that already has a usable client resolves WITHOUT it, but --write-provisioning needs --yes even then because it rewrites a manifest on disk (the dry run's planned.provisioning names the file). That query endpoint IGNORES paging (measured: query.paging, cursorPaging and a top-level paging all return the full set at offset 0), so when pagingMetadata reports more clients than came back, `ensure` REFUSES EVERY outcome \u2014 create, patch AND reuse. Reuse is refused too because pickClient breaks ties on the OLDEST createdDate, so an omitted older same-named MOBILE client with the same redirect would win a later complete read and the resolved clientId would MOVE between runs \u2014 and it gets written into provisioning.json; convergence is what makes a stray duplicate untidy rather than harmful. --name cannot narrow the query (no filter is sent; the name is applied to the rows after they arrive), so the recovery is the dashboard; --new skips the query entirely. A `patched` result is verified against what Wix STORED \u2014 the redirect list is read off the PATCH response, and re-read via GET when the response omits it, because falling back to the list himi just sent would make the guard pass vacuously on exactly the silent no-op it exists to catch. `client list` is the read-only inventory of a site's clients: act on its `shippable` column rather than `hasRedirect`, because shippable ALSO requires a MOBILE applicationType and `ensure` refuses a WEB client \u2014 a WEB one carrying your redirect looks right and is not a candidate \u2014 and it sets truncated:true rather than presenting a partial inventory as the whole one. `analyze --read-only` returns API-only generation context as JSON (identity, access probes, CMS permissions and selected redacted Velo source); it needs no URL/browser and never uploads or applies anything. `analyze` reads a site into a business-book folder (REST + Playwright, GET-only); `--url` alone is the visitor-only pass over the published pages, and `--client-id` adds the vertical probes and the app binding `--apply` requires; `site function` list/get/put/publish/invokes that site's Velo HTTP functions and is not `himi functions` (serve-plane jobs). `--apply` bakes into a brandable shell (a site shell needs no `--client-id`: it is visitor-only and binds no client); `--shell` takes the same names `himi dist --shell` does and defaults to branded-lite, and a shell that ships no prepare:customer is refused by name rather than half-built. `--shell brandy` (the site shell, RFC 0062) also derives the site's page map from its published pages and header menu \u2014 Home first, up to four tabs for the pages a visitor returns to, the rest in the drawer \u2014 into the overlay's `site` block, so it can be edited before `--apply`; only a site shell ever receives that block. It never overwrites any shell's committed apps/<shell>/provisioning.json.", required: "create <name> [--type wix|headless|branded-app|vibe] [--template <guid>] [--verticals stores,bookings,\u2026] [--folder <guid>] [--redirect-uri <uri>] [--no-oauth-app] [--write-provisioning [path]] [--site-url <url>] [--json] --yes | list | client ensure|list --site <msid> [--name <name>] [--redirect-uri <uri>] [--new] [--write-provisioning [path]] [--site-url <url>] [--json] --yes | analyze --read-only [--site <msid>] [--client-id <id>] [--json] | analyze --url <https> [--client-id <id>] [--site <msid>] [--out <dir>] [--redirect-uri <uri>] [--apply] [--shell " + SHELL_ENUM + "] | function list|get|put|publish|invoke [--site <msid>] [--url <https>] [--method GET|POST|PUT|DELETE] [--source <file>] [--body <json>] [--publish] [--invoke]" },
72317
72759
  { name: "doctor", summary: 'off-VPN preflight in one shot: probe the PUBLIC data plane (www.wixapis.com \u2014 reachable off-VPN), the INTERNAL serve/control plane for the selected target (VPN-gated), and report auth state (dpx session on disk + stored control token). --dpx additionally runs a live, EADDRINUSE-safe `dpx whoami`. Machine-readable with --json (an unreachable internal plane carries a stable code:"needs_vpn" so an agent branches on the code, not the prose); human-readable otherwise. Never loads the authoring runtime \u2014 works off-repo.', required: "[--target <t>] [--dpx] [--json] [--data-url <url>] [--timeout <ms>]" },
72318
72760
  { name: "upgrade", summary: "install the newest published CLI (`npm i -g @wix/himalaya-cli@latest`) and report what moved. This is the explicit door: himi also updates itself when the running build is more than a week old \u2014 unattended it just does it and re-runs your command, at a terminal it asks first \u2014 and `HIMI_NO_UPDATE_CHECK=1` turns that off, leaving this command as the way to catch up. Reports `unchanged` rather than claiming success when npm exits 0 without moving the version, which is what a registry mirror serving its own pinned copy looks like.", required: "[--json]" },
@@ -74611,7 +75053,7 @@ ${doNow.map((l) => ` ${l}`).join("\n")}`);
74611
75053
  try {
74612
75054
  const bytes2 = readFileSync80(iconPath);
74613
75055
  payload.icon = bytes2.toString("base64");
74614
- iconHash = createHash31("sha256").update(bytes2).digest("hex");
75056
+ iconHash = createHash32("sha256").update(bytes2).digest("hex");
74615
75057
  } catch {
74616
75058
  }
74617
75059
  }
@@ -76401,7 +76843,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
76401
76843
  let localSourceHash;
76402
76844
  try {
76403
76845
  const { collectAppSource: collectAppSource2, zipAppSource: zipAppSource2 } = await Promise.resolve().then(() => (init_app_source(), app_source_exports));
76404
- localSourceHash = createHash31("sha256").update(zipAppSource2(collectAppSource2(dir).entries).toString("base64")).digest("hex");
76846
+ localSourceHash = createHash32("sha256").update(zipAppSource2(collectAppSource2(dir).entries).toString("base64")).digest("hex");
76405
76847
  } catch {
76406
76848
  }
76407
76849
  return runReleasePatch({
@@ -77134,9 +77576,30 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77134
77576
  let browserCopy = null;
77135
77577
  let servedMissing = [];
77136
77578
  const fromContent = !source;
77579
+ let dropEarlySignals;
77580
+ let dropInitRecord;
77137
77581
  try {
77138
77582
  if (!source) {
77139
77583
  const dir2 = contentDir(flags);
77584
+ if (watch2) {
77585
+ const { removeInitWatchRecord: removeInitWatchRecord2 } = await Promise.resolve().then(() => (init_run_watch(), run_watch_exports));
77586
+ dropInitRecord = () => removeInitWatchRecord2(dir2, process.pid);
77587
+ const onExit = () => dropInitRecord?.();
77588
+ process.once("exit", onExit);
77589
+ const early = [["SIGINT", 130], ["SIGTERM", 143], ["SIGHUP", 129]].map(([sig, code]) => {
77590
+ const onEarly = () => {
77591
+ dropInitRecord?.();
77592
+ process.exit(code);
77593
+ };
77594
+ process.once(sig, onEarly);
77595
+ return [sig, onEarly];
77596
+ });
77597
+ dropEarlySignals = () => {
77598
+ for (const [sig, onEarly] of early) process.removeListener(sig, onEarly);
77599
+ process.removeListener("exit", onExit);
77600
+ dropEarlySignals = void 0;
77601
+ };
77602
+ }
77140
77603
  const app = await appId(dir2);
77141
77604
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
77142
77605
  const bres = await buildContentBundles2(dir2, { resolution: resolutionFlag(flags) });
@@ -77170,7 +77633,10 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77170
77633
  let startedAt = "";
77171
77634
  const record2 = (served2, build2) => {
77172
77635
  try {
77173
- if (recordKit?.writeRunRecord(dir, { url: served2.url, port: served2.port, ...served2.lanUrl ? { lanUrl: served2.lanUrl } : {}, pid: process.pid, startedAt, watching: watch2, build: build2 })) recorded = true;
77636
+ if (recordKit?.writeRunRecord(dir, { url: served2.url, port: served2.port, ...served2.lanUrl ? { lanUrl: served2.lanUrl } : {}, pid: process.pid, startedAt, watching: watch2, build: build2 })) {
77637
+ recorded = true;
77638
+ if (watch2) recordKit.removeInitWatchRecord(dir, process.pid);
77639
+ }
77174
77640
  return null;
77175
77641
  } catch (e) {
77176
77642
  return e.message;
@@ -77207,6 +77673,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77207
77673
  const stop = () => {
77208
77674
  void retract().catch((e) => io.error(JSON.stringify({ run: "stop_cleanup_failed", message: e.message }))).finally(() => run2.close().finally(() => resolve54()));
77209
77675
  };
77676
+ dropEarlySignals?.();
77210
77677
  process.once("SIGINT", stop);
77211
77678
  process.once("SIGTERM", stop);
77212
77679
  });
@@ -77352,6 +77819,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77352
77819
  }
77353
77820
  };
77354
77821
  const onSignal = () => void stop();
77822
+ dropEarlySignals?.();
77355
77823
  for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) process.once(sig, onSignal);
77356
77824
  stopNow = (why) => void stop(why);
77357
77825
  armIdle();
@@ -77361,6 +77829,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77361
77829
  io.error(JSON.stringify({ error: e.message }));
77362
77830
  return 1;
77363
77831
  } finally {
77832
+ dropEarlySignals?.();
77833
+ dropInitRecord?.();
77364
77834
  if (temp) rmSync16(temp, { recursive: true, force: true });
77365
77835
  if (served) rmSync16(served, { recursive: true, force: true });
77366
77836
  if (browserCopy) rmSync16(browserCopy, { recursive: true, force: true });
@@ -78017,7 +78487,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
78017
78487
  });
78018
78488
  }
78019
78489
  const bytes2 = readFileSync80(file);
78020
- const sha = createHash31("sha256").update(bytes2).digest("hex");
78490
+ const sha = createHash32("sha256").update(bytes2).digest("hex");
78021
78491
  const forScreens = (str8(flags["for-screen"]) ?? "").split(",").map((s) => s.trim()).filter(Boolean);
78022
78492
  if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
78023
78493
  io.error(JSON.stringify({ ok: false, error: "no_rebind_takes_no_value", message: "--no-rebind is a switch and takes no value; pass it bare.", usage: PATCH_USAGE }));