@jay-framework/wix-deploy 0.19.7 → 0.21.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.
@@ -8,15 +8,16 @@ function makeItemId(version, relativePath) {
8
8
  class WixDataArtifactStore {
9
9
  collectionId;
10
10
  version;
11
+ distDir;
11
12
  cacheDir;
12
13
  dataClient;
13
14
  moduleRegistry;
14
15
  manifestCache;
15
- moduleCache = /* @__PURE__ */ new Map();
16
16
  fetchPromises = /* @__PURE__ */ new Map();
17
17
  constructor(options) {
18
18
  this.collectionId = options.collectionId;
19
19
  this.version = options.version;
20
+ this.distDir = options.distDir;
20
21
  this.cacheDir = options.cacheDir;
21
22
  this.dataClient = options.wixClient.use({ items });
22
23
  this.moduleRegistry = options.moduleRegistry || {};
@@ -54,16 +55,9 @@ class WixDataArtifactStore {
54
55
  if (this.moduleRegistry[modulePath]) {
55
56
  return this.moduleRegistry[modulePath];
56
57
  }
57
- const cached = this.moduleCache.get(modulePath);
58
- if (cached) return cached;
59
- await this.ensureFile(modulePath);
60
- const fullPath = path.join(this.cacheDir, modulePath);
61
- const mod = await import(
62
- /* @vite-ignore */
63
- fullPath
58
+ throw new Error(
59
+ `Module not found in registry: ${modulePath}. All JS modules must be bundled in entry.mjs.`
64
60
  );
65
- this.moduleCache.set(modulePath, mod);
66
- return mod;
67
61
  }
68
62
  getAssetPath(relativePath) {
69
63
  return path.join(this.cacheDir, relativePath);
@@ -94,11 +88,21 @@ class WixDataArtifactStore {
94
88
  console.log(`[WixDataArtifactStore] Loaded ${totalLoaded} eager files`);
95
89
  }
96
90
  // ========================================================================
91
+ // Lazy page file loading
92
+ // ========================================================================
93
+ async ensurePageFiles(preRenderedPath) {
94
+ const dir = path.dirname(preRenderedPath);
95
+ const base = preRenderedPath.replace(/\.jay-html$/, "");
96
+ const filesToEnsure = [
97
+ path.join(dir, "page-parts.json"),
98
+ `${base}.cache.json`,
99
+ `${base}.server-element.js`
100
+ ];
101
+ await Promise.all(filesToEnsure.map((f) => this.ensureFile(f)));
102
+ }
103
+ // ========================================================================
97
104
  // Writes (for upload-backend and renderer)
98
105
  // ========================================================================
99
- /**
100
- * Write a single file to the data collection.
101
- */
102
106
  async writeFile(relativePath, content, category) {
103
107
  const ext = path.extname(relativePath).slice(1);
104
108
  const item = {
@@ -112,10 +116,6 @@ class WixDataArtifactStore {
112
116
  };
113
117
  await this.dataClient.items.save(this.collectionId, item);
114
118
  }
115
- /**
116
- * Write a batch of files to the data collection.
117
- * Returns the number of successfully written files.
118
- */
119
119
  async writeFiles(files) {
120
120
  const dataItems = files.map((f) => ({
121
121
  _id: makeItemId(this.version, f.path),
@@ -142,12 +142,18 @@ class WixDataArtifactStore {
142
142
  }
143
143
  }
144
144
  // ========================================================================
145
- // Internal: lazy file fetching
145
+ // Internal: file resolution
146
146
  // ========================================================================
147
147
  async ensureFile(relativePath) {
148
- const fullPath = path.join(this.cacheDir, relativePath);
149
- if (fs.existsSync(fullPath)) {
150
- return fs.readFileSync(fullPath, "utf8");
148
+ if (this.distDir) {
149
+ const distPath = path.join(this.distDir, relativePath);
150
+ if (fs.existsSync(distPath)) {
151
+ return fs.readFileSync(distPath, "utf8");
152
+ }
153
+ }
154
+ const cachePath = path.join(this.cacheDir, relativePath);
155
+ if (fs.existsSync(cachePath)) {
156
+ return fs.readFileSync(cachePath, "utf8");
151
157
  }
152
158
  const existing = this.fetchPromises.get(relativePath);
153
159
  if (existing) return existing;
@@ -161,22 +167,26 @@ class WixDataArtifactStore {
161
167
  }
162
168
  async fetchFromCollection(relativePath) {
163
169
  const id = makeItemId(this.version, relativePath);
164
- const t0 = Date.now();
165
- const item = await this.dataClient.items.get(
166
- this.collectionId,
167
- id
168
- );
169
- const t1 = Date.now();
170
- if (!item?.content) {
171
- throw new Error(
172
- `File not found in data collection: v${this.version}/${relativePath} (id: ${id})`
170
+ try {
171
+ const item = await this.dataClient.items.get(
172
+ this.collectionId,
173
+ id
173
174
  );
175
+ if (item?.content) {
176
+ this.writeToCache(relativePath, item.content);
177
+ return item.content;
178
+ }
179
+ } catch {
174
180
  }
175
- this.writeToCache(relativePath, item.content);
176
- console.log(
177
- `[WixDataArtifactStore] Fetched v${this.version}/${relativePath} (${t1 - t0}ms)`
181
+ const result = await this.dataClient.items.query(this.collectionId).eq("path", relativePath).eq("version", this.version).limit(1).find();
182
+ if (result.items.length > 0) {
183
+ const item = result.items[0];
184
+ this.writeToCache(relativePath, item.content);
185
+ return item.content;
186
+ }
187
+ throw new Error(
188
+ `File not found in data collection: v${this.version}/${relativePath} (id: ${id})`
178
189
  );
179
- return item.content;
180
190
  }
181
191
  writeToCache(relativePath, content) {
182
192
  const fullPath = path.join(this.cacheDir, relativePath);
package/dist/index.d.ts CHANGED
@@ -7,13 +7,17 @@ import { WixClientService } from '@jay-framework/wix-server-client';
7
7
  /**
8
8
  * WixDataArtifactStore — implements ArtifactStore for Wix BaaS deployments.
9
9
  *
10
- * Manages both reads (for serving) and writes (for upload/renderer) of
11
- * backend build artifacts in a Wix data collection. All items are versioned
12
- * so that a new version can be uploaded while the current version serves.
10
+ * Reads eager files (route-manifest.json, build-metadata.json) from the dist
11
+ * directory alongside entry.mjs. Fetches lazy JSON files (page-parts, cache
12
+ * data) from a Wix data collection on demand and caches them to disk.
13
+ * All JS modules are resolved from the bundled MODULE_REGISTRY — no disk
14
+ * loading of JS files.
15
+ *
16
+ * Also supports writes (for upload-backend) to the data collection.
13
17
  *
14
18
  * Data collection schema:
15
- * _id: string — "{version}__{path}" (unique key)
16
- * version: string — build version (semver)
19
+ * _id: string — sha256("{version}/{path}") truncated to 32 chars
20
+ * version: string — deploy version (e.g. "0.0.1-d849685dc3de")
17
21
  * path: string — relative file path within backend dir
18
22
  * content: string — file content (text)
19
23
  * fileType: string — extension (js, json)
@@ -24,18 +28,21 @@ import { WixClientService } from '@jay-framework/wix-server-client';
24
28
  interface WixDataArtifactStoreOptions {
25
29
  wixClient: WixClient;
26
30
  collectionId: string;
27
- cacheDir: string;
28
31
  version: string;
32
+ /** Directory containing entry.mjs and eager files (route-manifest.json, build-metadata.json). Required for reads. */
33
+ distDir?: string;
34
+ /** Directory for caching lazy files fetched from the data collection */
35
+ cacheDir: string;
29
36
  moduleRegistry?: Record<string, any>;
30
37
  }
31
38
  declare class WixDataArtifactStore implements ArtifactStore {
32
39
  readonly collectionId: string;
33
40
  readonly version: string;
41
+ private readonly distDir;
34
42
  private readonly cacheDir;
35
43
  private readonly dataClient;
36
44
  private readonly moduleRegistry;
37
45
  private manifestCache?;
38
- private moduleCache;
39
46
  private fetchPromises;
40
47
  constructor(options: WixDataArtifactStoreOptions);
41
48
  readManifest(): Promise<RouteManifest>;
@@ -46,14 +53,8 @@ declare class WixDataArtifactStore implements ArtifactStore {
46
53
  getAssetPath(relativePath: string): string;
47
54
  getBuildDir(): string;
48
55
  loadEagerFiles(): Promise<void>;
49
- /**
50
- * Write a single file to the data collection.
51
- */
56
+ ensurePageFiles(preRenderedPath: string): Promise<void>;
52
57
  writeFile(relativePath: string, content: string, category: 'eager' | 'lazy'): Promise<void>;
53
- /**
54
- * Write a batch of files to the data collection.
55
- * Returns the number of successfully written files.
56
- */
57
58
  writeFiles(files: Array<{
58
59
  path: string;
59
60
  content: string;
package/dist/index.js CHANGED
@@ -21,7 +21,25 @@ import { getService } from "@jay-framework/stack-server-runtime";
21
21
  import { items } from "@wix/data";
22
22
  const DEFAULT_COLLECTION_ID = "jay-backend-files";
23
23
  const DEFAULT_CACHE_DIR = "/tmp/jay-backend";
24
+ function getDeployVersion(metadata) {
25
+ const version2 = metadata.version || "0.0.0";
26
+ const hash = metadata.sourceHash || "";
27
+ if (!hash) return version2;
28
+ return `${version2}-${hash}`;
29
+ }
24
30
  const DEFAULT_EXCLUDE_PLUGINS = ["aiditor", "ui-kit", "wix-deploy"];
31
+ function scanPagePartsFiles(dir, fs2, path2) {
32
+ const results = [];
33
+ for (const entry of fs2.readdirSync(dir, { withFileTypes: true })) {
34
+ const fullPath = path2.join(dir, entry.name);
35
+ if (entry.isDirectory()) {
36
+ results.push(...scanPagePartsFiles(fullPath, fs2, path2));
37
+ } else if (entry.name === "page-parts.json") {
38
+ results.push(fullPath);
39
+ }
40
+ }
41
+ return results;
42
+ }
25
43
  function sortPluginsByDeps(plugins) {
26
44
  const priorityPackages = ["@jay-framework/wix-server-client"];
27
45
  const sorted = [];
@@ -170,11 +188,11 @@ ${actionsArray.join("\n")}
170
188
  wixClient: wixClientService,
171
189
  collectionId: COLLECTION_ID,
172
190
  version: VERSION,
191
+ distDir: entryDir,
173
192
  cacheDir: '${DEFAULT_CACHE_DIR}',
174
193
  moduleRegistry: MODULE_REGISTRY,
175
194
  });
176
- console.log('[BaaS] Loading eager files...');
177
- await artifacts.loadEagerFiles();
195
+ console.log('[BaaS] Artifact store ready (eager files from dist/, lazy from collection)');
178
196
  }
179
197
 
180
198
  initialized = true;
@@ -257,8 +275,8 @@ function generateServeSource(frontendDir, backendDir) {
257
275
  'import { Readable } from "node:stream";',
258
276
  "",
259
277
  'process.env.STATIC_BASE_URL = process.env.STATIC_BASE_URL || "/";',
260
- `process.env.JAY_BACKEND_DIR = process.env.JAY_BACKEND_DIR || "${backendDir}";`,
261
- 'const entry = await import("./entry.mjs");',
278
+ `process.env.JAY_BACKEND_DIR = process.env.JAY_BACKEND_DIR || "";`,
279
+ 'const entry = await import("./dist/entry.mjs");',
262
280
  "const handler = entry.default?.fetch || entry.fetch;",
263
281
  'const PORT = parseInt(process.env.PORT || "4000", 10);',
264
282
  `const FRONTEND_DIR = "${frontendDir}";`,
@@ -326,10 +344,10 @@ const buildEntry = makeCliCommand("build-entry").withServices(CONSOLE_CONTEXT).w
326
344
  }
327
345
  const manifest = JSON.parse(fs2.readFileSync(manifestPath, "utf8"));
328
346
  const metadataPath = path2.join(buildDir, "build-metadata.json");
329
- let version2 = "1.0.0";
347
+ let version2 = "0.0.0";
330
348
  if (fs2.existsSync(metadataPath)) {
331
349
  const metadata = JSON.parse(fs2.readFileSync(metadataPath, "utf8"));
332
- version2 = String(metadata.version || "1.0.0");
350
+ version2 = getDeployVersion(metadata);
333
351
  }
334
352
  const filteredPlugins = manifest.plugins.filter(
335
353
  (p) => !excludePlugins.includes(p.name) && !excludePlugins.includes(p.packageName)
@@ -337,18 +355,35 @@ const buildEntry = makeCliCommand("build-entry").withServices(CONSOLE_CONTEXT).w
337
355
  const plugins = sortPluginsByDeps(filteredPlugins);
338
356
  const pluginPackages = new Set(plugins.map((p) => p.packageName));
339
357
  const actionPackages = manifest.actions.filter((a) => a.isPlugin && a.packageName && pluginPackages.has(a.packageName)).map((a) => a.packageName).filter((v, i, arr) => arr.indexOf(v) === i);
340
- const seSet = /* @__PURE__ */ new Set();
358
+ const localModuleSet = /* @__PURE__ */ new Set();
341
359
  for (const route of manifest.routes) {
342
- if (route.serverElementPath) seSet.add(route.serverElementPath);
360
+ if (route.serverElementPath) localModuleSet.add(route.serverElementPath);
361
+ if (route.serverModule) localModuleSet.add(route.serverModule);
343
362
  }
344
- const serverElements = [...seSet].map((rel) => ({
363
+ const pagePartsFiles = scanPagePartsFiles(buildDir, fs2, path2);
364
+ for (const ppFile of pagePartsFiles) {
365
+ try {
366
+ const config = JSON.parse(fs2.readFileSync(ppFile, "utf8"));
367
+ const collectLocal = (entries) => {
368
+ for (const entry of entries) {
369
+ if (entry.source === "local" && entry.modulePath) {
370
+ localModuleSet.add(entry.modulePath);
371
+ }
372
+ }
373
+ };
374
+ if (config.parts) collectLocal(config.parts);
375
+ if (config.instanceComponents) collectLocal(config.instanceComponents);
376
+ } catch {
377
+ }
378
+ }
379
+ const serverElements = [...localModuleSet].map((rel) => ({
345
380
  relativePath: rel,
346
381
  absolutePath: path2.join(buildDir, rel)
347
382
  }));
348
383
  ctx.log(`Version: ${version2}`);
349
384
  ctx.log(`Plugins: ${plugins.map((p) => p.name).join(", ")}`);
350
385
  ctx.log(`Action packages: ${actionPackages.join(", ")}`);
351
- ctx.log(`Server elements: ${serverElements.length} routes`);
386
+ ctx.log(`Local modules: ${serverElements.length} (${[...localModuleSet].join(", ")})`);
352
387
  const entrySource = generateEntrySource(
353
388
  plugins,
354
389
  actionPackages,
@@ -373,7 +408,11 @@ const buildEntry = makeCliCommand("build-entry").withServices(CONSOLE_CONTEXT).w
373
408
  "prettier",
374
409
  "vite",
375
410
  "lightningcss",
376
- "fsevents"
411
+ "fsevents",
412
+ "@types/js-yaml",
413
+ "@wix/sdk-runtime",
414
+ "yaml",
415
+ "js-beautify"
377
416
  ];
378
417
  const stubFilter = new RegExp(
379
418
  "^(" + stubs.map((s) => s.replace(/[/.]/g, "\\$&")).join("|") + ")$"
@@ -445,6 +484,13 @@ const buildEntry = makeCliCommand("build-entry").withServices(CONSOLE_CONTEXT).w
445
484
  JSON.stringify(result.metafile)
446
485
  );
447
486
  }
487
+ for (const name of ["route-manifest.json", "build-metadata.json"]) {
488
+ const src2 = path2.join(buildDir, name);
489
+ if (fs2.existsSync(src2)) {
490
+ fs2.copyFileSync(src2, path2.join(entryDir, name));
491
+ }
492
+ }
493
+ ctx.log("Copied route-manifest.json + build-metadata.json to dist/");
448
494
  const wixConfigSrc = path2.join(ctx.projectRoot, "config", ".wix.yaml");
449
495
  if (fs2.existsSync(wixConfigSrc)) {
450
496
  const configDir = path2.join(entryDir, "config");
@@ -458,16 +504,24 @@ const buildEntry = makeCliCommand("build-entry").withServices(CONSOLE_CONTEXT).w
458
504
  );
459
505
  }
460
506
  const serveFile = path2.join(ctx.projectRoot, "serve.mjs");
461
- fs2.writeFileSync(serveFile, generateServeSource(ctx.build.frontend, buildDir));
507
+ fs2.writeFileSync(serveFile, generateServeSource(ctx.build.frontend));
462
508
  ctx.log(`Generated ${serveFile} — run with: node serve.mjs`);
463
509
  return { success: true, outFile, sizeMB };
464
510
  });
465
- const SKIP_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp", ".jay-html"]);
511
+ const SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
512
+ ".js",
513
+ ".png",
514
+ ".jpg",
515
+ ".jpeg",
516
+ ".gif",
517
+ ".svg",
518
+ ".webp",
519
+ ".jay-html"
520
+ ]);
466
521
  const MAX_BATCH_BYTES = 4e5;
467
522
  function categorize(relativePath) {
468
523
  if (relativePath === "route-manifest.json") return "eager";
469
524
  if (relativePath === "build-metadata.json") return "eager";
470
- if (relativePath.startsWith("server/")) return "eager";
471
525
  return "lazy";
472
526
  }
473
527
  function scanFileEntries(dir, base, fs2, path2) {
@@ -515,10 +569,10 @@ const uploadBackend = makeCliCommand("upload-backend").withServices(WIX_CLIENT_S
515
569
  const collectionId = input.collectionId || DEFAULT_COLLECTION_ID;
516
570
  const dryRun = input.dryRun || false;
517
571
  const metadataPath = path2.join(buildDir, "build-metadata.json");
518
- let version2 = "1.0.0";
572
+ let version2 = "0.0.0";
519
573
  if (fs2.existsSync(metadataPath)) {
520
574
  const metadata = JSON.parse(fs2.readFileSync(metadataPath, "utf8"));
521
- version2 = String(metadata.version || "1.0.0");
575
+ version2 = getDeployVersion(metadata);
522
576
  }
523
577
  if (dryRun) ctx.log("DRY RUN — no uploads");
524
578
  if (!fs2.existsSync(buildDir)) {
@@ -7151,6 +7205,7 @@ const deployBaas = makeCliCommand("deploy-baas").withServices(CONSOLE_CONTEXT).w
7151
7205
  const completedDeployment = completeData.appDeployment || {};
7152
7206
  const deploymentId = appDeployment?.id;
7153
7207
  const deploymentBaseUrl = completedDeployment.deploymentBaseUrl || appDeployment?.deploymentBaseUrl || "";
7208
+ ctx.log(`Deployment uploaded. Base URL: ${deploymentBaseUrl}`);
7154
7209
  let appVersion = 0;
7155
7210
  try {
7156
7211
  const { data: versionData } = await httpClient2.request(
@@ -7203,14 +7258,6 @@ function prefixCtx(ctx, prefix) {
7203
7258
  return { ...ctx, log: (msg) => ctx.log(`[deploy] ${prefix} ${msg}`), warn: () => {
7204
7259
  } };
7205
7260
  }
7206
- function bumpPatch(version2) {
7207
- const parts = version2.split(".");
7208
- if (parts.length === 3) {
7209
- parts[2] = String(parseInt(parts[2], 10) + 1);
7210
- return parts.join(".");
7211
- }
7212
- return version2 + ".1";
7213
- }
7214
7261
  const deploy = makeCliCommand("deploy").withServices(WIX_CLIENT_SERVICE, CONSOLE_CONTEXT).withHandler(async (input, wixClient, ctx) => {
7215
7262
  const fs2 = await import("node:fs");
7216
7263
  const path2 = await import("node:path");
@@ -7222,11 +7269,8 @@ const deploy = makeCliCommand("deploy").withServices(WIX_CLIENT_SERVICE, CONSOLE
7222
7269
  return { success: false };
7223
7270
  }
7224
7271
  const metadata = JSON.parse(fs2.readFileSync(metadataPath, "utf8"));
7225
- const buildVersion = String(metadata.version || "1.0.0");
7226
- const deployVersion = bumpPatch(buildVersion);
7227
- metadata.version = deployVersion;
7228
- fs2.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
7229
- ctx.log(`[deploy] Version: ${buildVersion} → ${deployVersion}`);
7272
+ const deployVersion = getDeployVersion(metadata);
7273
+ ctx.log(`[deploy] Version: ${deployVersion}`);
7230
7274
  ctx.log("[deploy] Bundling entry.mjs...");
7231
7275
  const buildResult = await buildEntry.handler(
7232
7276
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/wix-deploy",
3
- "version": "0.19.7",
3
+ "version": "0.21.0",
4
4
  "type": "module",
5
5
  "description": "Wix BaaS deployment adapter for Jay Framework — WixDataArtifactStore and entry builder",
6
6
  "license": "Apache-2.0",
@@ -16,7 +16,8 @@
16
16
  "./package.json": "./package.json"
17
17
  },
18
18
  "scripts": {
19
- "build": "npm run clean && npm run build:server && npm run build:types",
19
+ "build": "npm run clean && npm run build:server && npm run build:types && npm run validate",
20
+ "validate": "jay-stack-cli validate-plugin",
20
21
  "build:server": "vite build --ssr",
21
22
  "build:types": "tsup lib/index.ts --dts-only --format esm",
22
23
  "build:check-types": "tsc",
@@ -25,10 +26,10 @@
25
26
  "test": ":"
26
27
  },
27
28
  "dependencies": {
28
- "@jay-framework/fullstack-component": "^0.19.7",
29
- "@jay-framework/production-server": "^0.19.7",
30
- "@jay-framework/stack-server-runtime": "^0.19.7",
31
- "@jay-framework/wix-server-client": "^0.19.7",
29
+ "@jay-framework/fullstack-component": "^0.21.0",
30
+ "@jay-framework/production-server": "^0.21.0",
31
+ "@jay-framework/stack-server-runtime": "^0.21.0",
32
+ "@jay-framework/wix-server-client": "^0.21.0",
32
33
  "@wix/data": "^1.0.433",
33
34
  "@wix/sdk": "^1.21.5",
34
35
  "esbuild": "^0.21.0",