@jay-framework/wix-deploy 0.18.3 → 0.18.4

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.
@@ -1,250 +0,0 @@
1
- /**
2
- * jay-stack run wix-deploy/deploy-baas
3
- *
4
- * Deploys the dist/ folder to Wix BaaS using ambassador SDK packages.
5
- * Uploads ALL files including node_modules/.
6
- *
7
- * Reads appId from wix.config.json and auth from ~/.wix/auth/.
8
- */
9
- import { makeCliCommand, CONSOLE_CONTEXT } from '@jay-framework/fullstack-component';
10
- import { createHttpClient } from '@wix/http-client';
11
- import { createAppDeployment, completeAppDeployment, } from '@wix/ambassador-velo-backend-v1-app-deployment/http';
12
- import { createComponentsOverride } from '@wix/ambassador-devcenter-components-overrides-v1-components-override/http';
13
- import { getLatestProductionVersion } from '@wix/ambassador-devcenter-apps-v1-app-version/http';
14
- import { release } from '@wix/ambassador-ctp-gradual-rollout-v1-baas-release/http';
15
- import fs from 'node:fs';
16
- import path from 'node:path';
17
- import crypto from 'node:crypto';
18
- const BACKEND_WORKER_COMPONENT_ID = 'ed5f3d0e-7b79-4717-9c00-c4cd7bbbe906';
19
- function md5(buffer) {
20
- return crypto.createHash('md5').update(buffer).digest('hex');
21
- }
22
- function getMimeType(filePath) {
23
- const ext = path.extname(filePath).toLowerCase();
24
- const types = {
25
- '.mjs': 'application/javascript',
26
- '.js': 'application/javascript',
27
- '.json': 'application/json',
28
- '.html': 'text/html',
29
- '.css': 'text/css',
30
- '.yaml': 'text/yaml',
31
- '.yml': 'text/yaml',
32
- };
33
- return types[ext] || 'application/octet-stream';
34
- }
35
- function collectFiles(dir, base = '') {
36
- const files = [];
37
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
38
- const fullPath = path.join(dir, entry.name);
39
- const relativePath = base ? `${base}/${entry.name}` : entry.name;
40
- if (entry.isDirectory()) {
41
- files.push(...collectFiles(fullPath, relativePath));
42
- }
43
- else {
44
- const content = fs.readFileSync(fullPath);
45
- files.push({
46
- path: `/${relativePath}`,
47
- content,
48
- hash: md5(content),
49
- contentType: getMimeType(entry.name),
50
- size: content.length,
51
- });
52
- }
53
- }
54
- return files;
55
- }
56
- export const deployBaas = makeCliCommand('deploy-baas')
57
- .withServices(CONSOLE_CONTEXT)
58
- .withHandler(async (input, ctx) => {
59
- const distDir = path.resolve(ctx.projectRoot, 'dist');
60
- const dryRun = input.dryRun || false;
61
- if (!fs.existsSync(distDir)) {
62
- ctx.error('dist/ not found. Run wix-deploy/build-entry first.');
63
- return { success: false };
64
- }
65
- // Read appId + siteId from wix.config.json
66
- const wixConfigPath = path.join(ctx.projectRoot, 'wix.config.json');
67
- if (!fs.existsSync(wixConfigPath)) {
68
- ctx.error('wix.config.json not found.');
69
- return { success: false };
70
- }
71
- const wixConfig = JSON.parse(fs.readFileSync(wixConfigPath, 'utf8'));
72
- const appId = wixConfig.appId;
73
- const siteId = wixConfig.siteId;
74
- // Load auth — prefer OAuth token from ~/.wix/auth/, refresh if expired
75
- const os = await import('node:os');
76
- const accountPath = path.join(os.homedir(), '.wix', 'auth', 'account.json');
77
- if (!fs.existsSync(accountPath)) {
78
- ctx.error('No auth found. Run `wix login` first.');
79
- return { success: false };
80
- }
81
- const authData = JSON.parse(fs.readFileSync(accountPath, 'utf8'));
82
- let accessToken = authData.accessToken;
83
- // Check if token is expired and refresh
84
- const issuedAt = authData.issuedAt * 1000; // epoch seconds → ms
85
- const expiresAt = issuedAt + authData.expiresIn * 1000;
86
- if (Date.now() > expiresAt - 60_000) {
87
- // 1 min buffer
88
- ctx.log('Access token expired, refreshing...');
89
- const refreshResponse = await fetch('https://manage.wix.com/oauth2/token', {
90
- method: 'POST',
91
- headers: {
92
- 'Content-Type': 'application/json',
93
- 'X-XSRF-TOKEN': 'nocheck',
94
- Cookie: 'XSRF-TOKEN=nocheck',
95
- },
96
- body: JSON.stringify({
97
- clientId: '6f95cec8-3e98-48b9-b4e5-1fb92fcd9973',
98
- grantType: 'refresh_token',
99
- refreshToken: authData.refreshToken,
100
- }),
101
- });
102
- if (!refreshResponse.ok) {
103
- ctx.error(`Token refresh failed: ${refreshResponse.status}. Run \`wix login\` again.`);
104
- return { success: false };
105
- }
106
- const tokenData = (await refreshResponse.json());
107
- accessToken = tokenData.access_token;
108
- // Update stored token
109
- authData.accessToken = accessToken;
110
- authData.issuedAt = Math.floor(Date.now() / 1000);
111
- authData.expiresIn = tokenData.expires_in;
112
- fs.writeFileSync(accountPath, JSON.stringify(authData, null, 2));
113
- ctx.log('Token refreshed');
114
- }
115
- const httpClient = createHttpClient({
116
- baseURL: 'https://manage.wix.com',
117
- getAppToken: async () => accessToken,
118
- createHeaders: () => ({
119
- 'X-XSRF-TOKEN': 'nocheck',
120
- Cookie: 'XSRF-TOKEN=nocheck',
121
- }),
122
- });
123
- const frontendDir = ctx.build.frontend;
124
- const serverDir = distDir;
125
- const clientFiles = fs.existsSync(frontendDir) ? collectFiles(frontendDir) : [];
126
- const serverFiles = collectFiles(serverDir);
127
- const files = [...clientFiles, ...serverFiles];
128
- const totalSize = files.reduce((sum, f) => sum + f.size, 0);
129
- ctx.log(`${clientFiles.length} client + ${serverFiles.length} server files (${(totalSize / 1024 / 1024).toFixed(1)} MB)`);
130
- if (dryRun) {
131
- for (const f of files) {
132
- ctx.log(` ${f.path} (${(f.size / 1024).toFixed(1)} KB)`);
133
- }
134
- return { success: true, fileCount: files.length, totalSize };
135
- }
136
- ctx.log('Creating deployment...');
137
- const staticFilesMetadata = clientFiles.map((f) => ({
138
- path: f.path,
139
- hash: f.hash,
140
- contentType: f.contentType,
141
- size: f.size,
142
- }));
143
- const { data: deployData } = await httpClient.request(createAppDeployment({
144
- appDeployment: { appProjectId: appId, staticFilesMetadata },
145
- }));
146
- const appDeployment = deployData.appDeployment;
147
- const uploadUrls = deployData.staticFilesUploadUrls || [];
148
- const uploadToken = deployData.uploadAuthToken;
149
- const uploadBuckets = deployData.uploadBuckets || [];
150
- if (uploadUrls.length > 0) {
151
- const isKubernetes = appDeployment?.cloudProviderOverride === 'KUBERNETES';
152
- if (isKubernetes) {
153
- let uploaded = 0;
154
- for (const uploadInfo of uploadUrls) {
155
- const file = clientFiles.find((f) => f.path === uploadInfo.staticFileMetadata?.path);
156
- if (!file || !uploadInfo.uploadUrl)
157
- continue;
158
- const response = await fetch(uploadInfo.uploadUrl, {
159
- method: 'PUT',
160
- headers: { 'Content-Type': file.contentType },
161
- body: file.content,
162
- });
163
- if (!response.ok) {
164
- ctx.error(` FAILED ${file.path}: ${response.status}`);
165
- }
166
- else {
167
- uploaded++;
168
- }
169
- }
170
- ctx.log(`Uploaded ${uploaded} CDN files`);
171
- }
172
- else {
173
- const uploadUrl = uploadUrls[0].uploadUrl;
174
- const buckets = uploadBuckets.length > 0
175
- ? uploadBuckets
176
- : [{ hashes: clientFiles.map((f) => f.hash) }];
177
- for (let i = 0; i < buckets.length; i++) {
178
- const bucket = buckets[i];
179
- const formData = new FormData();
180
- for (const hash of bucket.hashes || []) {
181
- const file = clientFiles.find((f) => f.hash === hash);
182
- if (!file)
183
- continue;
184
- formData.append(hash, new Blob([file.content], { type: file.contentType }), hash);
185
- }
186
- const response = await fetch(uploadUrl, {
187
- method: 'POST',
188
- headers: { Authorization: `Bearer ${uploadToken}` },
189
- body: formData,
190
- });
191
- if (!response.ok) {
192
- ctx.error(` Bucket ${i + 1} FAILED: ${response.status}`);
193
- }
194
- else {
195
- ctx.log(` Bucket ${i + 1}/${buckets.length}: ${bucket.hashes?.length} files`);
196
- }
197
- }
198
- }
199
- }
200
- ctx.log('Uploading server files...');
201
- const backendFiles = serverFiles.map((f) => ({
202
- path: f.path.startsWith('/') ? f.path.slice(1) : f.path,
203
- content: f.content.toString('base64'),
204
- }));
205
- const { data: completeData } = await httpClient.request(completeAppDeployment({
206
- appDeployment: { ...appDeployment, files: backendFiles },
207
- staticsCompletionToken: uploadToken,
208
- }));
209
- const completedDeployment = completeData.appDeployment || {};
210
- const deploymentId = appDeployment?.id;
211
- const deploymentBaseUrl = completedDeployment.deploymentBaseUrl || appDeployment?.deploymentBaseUrl || '';
212
- let appVersion = 0;
213
- try {
214
- const { data: versionData } = await httpClient.request(getLatestProductionVersion({ appId }));
215
- appVersion = versionData.appVersion?.version || 0;
216
- }
217
- catch {
218
- /* first deployment */
219
- }
220
- ctx.log('Registering + releasing...');
221
- const overrideId = crypto.randomUUID();
222
- await httpClient.request(createComponentsOverride({
223
- componentsOverride: {
224
- appId,
225
- appVersion,
226
- externalId: appId,
227
- id: overrideId,
228
- modifiedComponents: [
229
- {
230
- componentId: BACKEND_WORKER_COMPONENT_ID,
231
- type: 'BACKEND_WORKER',
232
- data: {
233
- backendWorker: {
234
- deploymentId,
235
- deploymentUrl: deploymentBaseUrl,
236
- },
237
- },
238
- },
239
- ],
240
- },
241
- }));
242
- const { data: releaseData } = await httpClient.request(release({
243
- appId,
244
- componentOverrideId: overrideId,
245
- createMinorVersion: true,
246
- }));
247
- const releaseUrl = releaseData.releaseBaseUrl || deploymentBaseUrl;
248
- ctx.log(`Released → ${releaseUrl}`);
249
- return { success: true, deploymentId, baseUrl: releaseUrl };
250
- });
@@ -1,19 +0,0 @@
1
- /**
2
- * jay-stack run wix-deploy/deploy
3
- *
4
- * Single command that replaces the 3-step deploy sequence:
5
- * 1. Bump deploy version in build-metadata.json
6
- * 2. build-entry (bundle entry.mjs with new version)
7
- * 3. upload-backend (data collection) — parallel with step 4
8
- * 4. deploy-baas (BaaS + CDN) — parallel with step 3
9
- */
10
- import type { ConsoleContext } from '@jay-framework/fullstack-component';
11
- import type { WixClientService } from '@jay-framework/wix-server-client';
12
- interface DeployInput {
13
- collectionId?: string;
14
- staticBaseUrl?: string;
15
- excludePlugins?: string;
16
- dryRun?: boolean;
17
- }
18
- export declare const deploy: import("@jay-framework/fullstack-component").JayCliCommand<DeployInput> & import("@jay-framework/fullstack-component").JayCliCommandDefinition<DeployInput, [WixClientService, ConsoleContext]>;
19
- export {};
@@ -1,87 +0,0 @@
1
- /**
2
- * jay-stack run wix-deploy/deploy
3
- *
4
- * Single command that replaces the 3-step deploy sequence:
5
- * 1. Bump deploy version in build-metadata.json
6
- * 2. build-entry (bundle entry.mjs with new version)
7
- * 3. upload-backend (data collection) — parallel with step 4
8
- * 4. deploy-baas (BaaS + CDN) — parallel with step 3
9
- */
10
- import { makeCliCommand, CONSOLE_CONTEXT } from '@jay-framework/fullstack-component';
11
- import { WIX_CLIENT_SERVICE } from '@jay-framework/wix-server-client';
12
- import { buildEntry } from './build-entry.js';
13
- import { uploadBackend } from './upload-backend.js';
14
- import { deployBaas } from './deploy-baas.js';
15
- function silentCtx(ctx) {
16
- return { ...ctx, log: () => { }, warn: () => { } };
17
- }
18
- function prefixCtx(ctx, prefix) {
19
- return { ...ctx, log: (msg) => ctx.log(`[deploy] ${prefix} ${msg}`), warn: () => { } };
20
- }
21
- function bumpPatch(version) {
22
- const parts = version.split('.');
23
- if (parts.length === 3) {
24
- parts[2] = String(parseInt(parts[2], 10) + 1);
25
- return parts.join('.');
26
- }
27
- return version + '.1';
28
- }
29
- export const deploy = makeCliCommand('deploy')
30
- .withServices(WIX_CLIENT_SERVICE, CONSOLE_CONTEXT)
31
- .withHandler(async (input, wixClient, ctx) => {
32
- const fs = await import('node:fs');
33
- const path = await import('node:path');
34
- const dryRun = input.dryRun || false;
35
- const t0 = Date.now();
36
- // Bump deploy version so running instances aren't disturbed
37
- const metadataPath = path.join(ctx.build.backend, 'build-metadata.json');
38
- if (!fs.existsSync(metadataPath)) {
39
- ctx.error(`[deploy] build-metadata.json not found. Run build:production first.`);
40
- return { success: false };
41
- }
42
- const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
43
- const buildVersion = String(metadata.version || '1.0.0');
44
- const deployVersion = bumpPatch(buildVersion);
45
- metadata.version = deployVersion;
46
- fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
47
- ctx.log(`[deploy] Version: ${buildVersion} → ${deployVersion}`);
48
- ctx.log('[deploy] Bundling entry.mjs...');
49
- const buildResult = (await buildEntry.handler({
50
- collectionId: input.collectionId,
51
- staticBaseUrl: input.staticBaseUrl,
52
- excludePlugins: input.excludePlugins,
53
- }, silentCtx(ctx)));
54
- if (!buildResult?.success) {
55
- ctx.error('[deploy] Bundle failed');
56
- return { success: false };
57
- }
58
- const bundleTime = ((Date.now() - t0) / 1000).toFixed(1);
59
- ctx.log(`[deploy] Bundled entry.mjs (${buildResult.sizeMB} MB) in ${bundleTime}s`);
60
- ctx.log('[deploy] Uploading...');
61
- const t1 = Date.now();
62
- const [uploadResult, deployResult] = (await Promise.all([
63
- uploadBackend.handler({ collectionId: input.collectionId, dryRun }, wixClient, prefixCtx(ctx, 'data |')),
64
- dryRun
65
- ? { success: true, baseUrl: '(dry run)' }
66
- : deployBaas.handler({ dryRun }, prefixCtx(ctx, 'baas |')),
67
- ]));
68
- if (!uploadResult?.success)
69
- ctx.error('[deploy] Backend data upload failed');
70
- if (!deployResult?.success)
71
- ctx.error('[deploy] BaaS deployment failed');
72
- const success = !!(uploadResult?.success && deployResult?.success);
73
- const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
74
- const deployTime = ((Date.now() - t1) / 1000).toFixed(1);
75
- ctx.log('');
76
- if (success) {
77
- ctx.log(`[deploy] Done in ${elapsed}s (bundle ${bundleTime}s + deploy ${deployTime}s)`);
78
- ctx.log(`[deploy] Version: ${deployVersion} | Entry: ${buildResult.sizeMB} MB | Backend files: ${uploadResult.uploaded}`);
79
- if (deployResult.baseUrl) {
80
- ctx.log(`[deploy] URL: ${deployResult.baseUrl}`);
81
- }
82
- }
83
- else {
84
- ctx.log(`[deploy] Failed after ${elapsed}s`);
85
- }
86
- return { success, ...deployResult };
87
- });
@@ -1,14 +0,0 @@
1
- /**
2
- * jay-stack run wix-deploy/upload-backend
3
- *
4
- * Uploads backend build files to a Wix data collection for BaaS serving.
5
- * Uses WixDataArtifactStore for consistent schema, versioning, and writes.
6
- */
7
- import type { ConsoleContext } from '@jay-framework/fullstack-component';
8
- import type { WixClientService } from '@jay-framework/wix-server-client';
9
- interface UploadBackendInput {
10
- collectionId?: string;
11
- dryRun?: boolean;
12
- }
13
- export declare const uploadBackend: import("@jay-framework/fullstack-component").JayCliCommand<UploadBackendInput> & import("@jay-framework/fullstack-component").JayCliCommandDefinition<UploadBackendInput, [WixClientService, ConsoleContext]>;
14
- export {};
@@ -1,122 +0,0 @@
1
- /**
2
- * jay-stack run wix-deploy/upload-backend
3
- *
4
- * Uploads backend build files to a Wix data collection for BaaS serving.
5
- * Uses WixDataArtifactStore for consistent schema, versioning, and writes.
6
- */
7
- import { makeCliCommand, CONSOLE_CONTEXT } from '@jay-framework/fullstack-component';
8
- import { WIX_CLIENT_SERVICE } from '@jay-framework/wix-server-client';
9
- import { DEFAULT_COLLECTION_ID } from '../constants.js';
10
- import { WixDataArtifactStore } from '../artifact-store.js';
11
- const SKIP_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.jay-html']);
12
- const MAX_BATCH_BYTES = 400_000;
13
- function categorize(relativePath) {
14
- if (relativePath === 'route-manifest.json')
15
- return 'eager';
16
- if (relativePath === 'build-metadata.json')
17
- return 'eager';
18
- if (relativePath.startsWith('server/'))
19
- return 'eager';
20
- return 'lazy';
21
- }
22
- function scanFileEntries(dir, base, fs, path) {
23
- const entries = [];
24
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
25
- const fullPath = path.join(dir, entry.name);
26
- const relativePath = base ? `${base}/${entry.name}` : entry.name;
27
- if (entry.isDirectory()) {
28
- entries.push(...scanFileEntries(fullPath, relativePath, fs, path));
29
- }
30
- else {
31
- const ext = path.extname(entry.name).toLowerCase();
32
- if (SKIP_EXTENSIONS.has(ext))
33
- continue;
34
- const stat = fs.statSync(fullPath);
35
- entries.push({
36
- relativePath,
37
- fullPath,
38
- sizeBytes: stat.size,
39
- category: categorize(relativePath),
40
- });
41
- }
42
- }
43
- return entries;
44
- }
45
- function buildBatches(entries) {
46
- const batches = [];
47
- let currentBatch = [];
48
- let currentSize = 0;
49
- for (const entry of entries) {
50
- if (currentBatch.length > 0 && currentSize + entry.sizeBytes > MAX_BATCH_BYTES) {
51
- batches.push(currentBatch);
52
- currentBatch = [];
53
- currentSize = 0;
54
- }
55
- currentBatch.push(entry);
56
- currentSize += entry.sizeBytes;
57
- }
58
- if (currentBatch.length > 0)
59
- batches.push(currentBatch);
60
- return batches;
61
- }
62
- export const uploadBackend = makeCliCommand('upload-backend')
63
- .withServices(WIX_CLIENT_SERVICE, CONSOLE_CONTEXT)
64
- .withHandler(async (input, wixClient, ctx) => {
65
- const fs = await import('node:fs');
66
- const path = await import('node:path');
67
- const buildDir = ctx.build.backend;
68
- const collectionId = input.collectionId || DEFAULT_COLLECTION_ID;
69
- const dryRun = input.dryRun || false;
70
- // Read version from build-metadata.json
71
- const metadataPath = path.join(buildDir, 'build-metadata.json');
72
- let version = '1.0.0';
73
- if (fs.existsSync(metadataPath)) {
74
- const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
75
- version = String(metadata.version || '1.0.0');
76
- }
77
- if (dryRun)
78
- ctx.log('DRY RUN — no uploads');
79
- if (!fs.existsSync(buildDir)) {
80
- ctx.error(`Backend dir not found: ${buildDir}`);
81
- return { success: false };
82
- }
83
- const entries = scanFileEntries(buildDir, '', fs, path);
84
- const totalSize = entries.reduce((sum, f) => sum + f.sizeBytes, 0);
85
- ctx.log(`${entries.length} files (${(totalSize / 1024 / 1024).toFixed(1)} MB)`);
86
- if (dryRun) {
87
- for (const f of entries.slice(0, 20)) {
88
- ctx.log(` [${f.category}] ${f.relativePath} (${(f.sizeBytes / 1024).toFixed(0)} KB)`);
89
- }
90
- if (entries.length > 20)
91
- ctx.log(` ... and ${entries.length - 20} more`);
92
- return { success: true, fileCount: entries.length, totalSize };
93
- }
94
- const store = new WixDataArtifactStore({
95
- wixClient: wixClient,
96
- collectionId,
97
- version,
98
- cacheDir: '/tmp/upload-staging',
99
- });
100
- const batches = buildBatches(entries);
101
- let uploaded = 0;
102
- let errors = 0;
103
- let lastReported = 0;
104
- for (let i = 0; i < batches.length; i++) {
105
- const batch = batches[i];
106
- const files = batch.map((f) => ({
107
- path: f.relativePath,
108
- content: fs.readFileSync(f.fullPath, 'utf8'),
109
- category: f.category,
110
- }));
111
- const count = await store.writeFiles(files);
112
- uploaded += count;
113
- errors += batch.length - count;
114
- if (uploaded - lastReported >= 100 || i === batches.length - 1) {
115
- ctx.log(`${uploaded}/${entries.length} files uploaded`);
116
- lastReported = uploaded;
117
- }
118
- }
119
- if (errors > 0)
120
- ctx.log(`${errors} errors`);
121
- return { success: errors === 0, uploaded, errors, version };
122
- });
@@ -1,2 +0,0 @@
1
- export declare const DEFAULT_COLLECTION_ID = "jay-backend-files";
2
- export declare const DEFAULT_CACHE_DIR = "/tmp/jay-backend";
package/dist/constants.js DELETED
@@ -1,2 +0,0 @@
1
- export const DEFAULT_COLLECTION_ID = 'jay-backend-files';
2
- export const DEFAULT_CACHE_DIR = '/tmp/jay-backend';
package/dist/setup.d.ts DELETED
@@ -1,19 +0,0 @@
1
- /**
2
- * wix-deploy setup hook
3
- *
4
- * Runs after wix-server-client setup. Reads wix.config.json to populate
5
- * clientId and siteId in config/.wix.yaml (if they're still placeholders),
6
- * then validates the data collection exists.
7
- */
8
- interface SetupContext {
9
- configDir: string;
10
- projectRoot: string;
11
- initError?: Error;
12
- }
13
- interface SetupResult {
14
- status: 'configured' | 'needs-config' | 'error';
15
- message?: string;
16
- configCreated?: string[];
17
- }
18
- export declare function setupWixDeploy(ctx: SetupContext): Promise<SetupResult>;
19
- export {};
package/dist/setup.js DELETED
@@ -1,101 +0,0 @@
1
- /**
2
- * wix-deploy setup hook
3
- *
4
- * Runs after wix-server-client setup. Reads wix.config.json to populate
5
- * clientId and siteId in config/.wix.yaml (if they're still placeholders),
6
- * then validates the data collection exists.
7
- */
8
- import fs from 'node:fs';
9
- import path from 'node:path';
10
- import yaml from 'js-yaml';
11
- // @ts-ignore — no type declarations
12
- import { getService } from '@jay-framework/stack-server-runtime';
13
- import { WIX_CLIENT_SERVICE } from '@jay-framework/wix-server-client';
14
- import { items } from '@wix/data';
15
- import { DEFAULT_COLLECTION_ID } from './constants.js';
16
- export async function setupWixDeploy(ctx) {
17
- if (ctx.initError) {
18
- return {
19
- status: 'error',
20
- message: `Service init failed: ${ctx.initError.message}`,
21
- };
22
- }
23
- const wixConfigPath = path.join(ctx.projectRoot, 'wix.config.json');
24
- const wixYamlPath = path.join(ctx.configDir, '.wix.yaml');
25
- if (!fs.existsSync(wixConfigPath)) {
26
- return {
27
- status: 'needs-config',
28
- message: 'wix.config.json not found. Run: npm create @wix/new@latest init',
29
- };
30
- }
31
- const wixConfig = JSON.parse(fs.readFileSync(wixConfigPath, 'utf8'));
32
- const appId = wixConfig.appId;
33
- const siteId = wixConfig.siteId;
34
- if (!appId) {
35
- return {
36
- status: 'error',
37
- message: 'wix.config.json missing appId',
38
- };
39
- }
40
- const configCreated = [];
41
- if (fs.existsSync(wixYamlPath)) {
42
- const content = fs.readFileSync(wixYamlPath, 'utf8');
43
- const config = yaml.load(content);
44
- if (config) {
45
- let changed = false;
46
- const currentClientId = config.oauthStrategy?.clientId || '';
47
- if (!currentClientId || currentClientId.startsWith('<')) {
48
- if (!config.oauthStrategy)
49
- config.oauthStrategy = {};
50
- config.oauthStrategy.clientId = appId;
51
- changed = true;
52
- configCreated.push('clientId');
53
- }
54
- const currentSiteId = config.apiKeyStrategy?.siteId || '';
55
- if (siteId && (!currentSiteId || currentSiteId.startsWith('<'))) {
56
- if (!config.apiKeyStrategy)
57
- config.apiKeyStrategy = {};
58
- config.apiKeyStrategy.siteId = siteId;
59
- changed = true;
60
- configCreated.push('siteId');
61
- }
62
- if (changed) {
63
- fs.writeFileSync(wixYamlPath, yaml.dump(config, { lineWidth: -1 }), 'utf8');
64
- }
65
- }
66
- }
67
- // Check if API key is configured
68
- if (fs.existsSync(wixYamlPath)) {
69
- const config = yaml.load(fs.readFileSync(wixYamlPath, 'utf8'));
70
- const apiKey = config?.apiKeyStrategy?.apiKey || '';
71
- if (!apiKey || apiKey.startsWith('<')) {
72
- return {
73
- status: 'needs-config',
74
- configCreated,
75
- message: 'API key required — create one at https://manage.wix.com/ and add to config/.wix.yaml',
76
- };
77
- }
78
- }
79
- // Validate data collection exists
80
- let collectionOk = false;
81
- try {
82
- const wixClient = getService(WIX_CLIENT_SERVICE);
83
- if (wixClient) {
84
- const dataClient = wixClient.use({ items });
85
- await dataClient.items.query(DEFAULT_COLLECTION_ID).limit(1).find();
86
- collectionOk = true;
87
- }
88
- }
89
- catch {
90
- return {
91
- status: 'needs-config',
92
- configCreated,
93
- message: `Data collection "${DEFAULT_COLLECTION_ID}" not found. Create it in the Wix dashboard with fields: path (text), content (text), fileType (text), sizeBytes (number), category (text), version (text)`,
94
- };
95
- }
96
- return {
97
- status: 'configured',
98
- configCreated,
99
- message: `Deploy target: wix.config.json (appId: ${appId.slice(0, 8)}...). Collection: ${DEFAULT_COLLECTION_ID} ${collectionOk ? '✓' : ''}`,
100
- };
101
- }