@wix/agent-skills 1.22.0 → 1.23.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/package.json +1 -1
- package/skills/wix-app/SKILL.md +12 -4
- package/skills/wix-app/references/APP_VALIDATION.md +16 -10
- package/skills/wix-app/references/SDK_MODULE_MAP.md +2 -0
- package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE.md +1 -1
- package/skills/wix-headless-fast/SKILL.md +35 -8
- package/skills/wix-headless-fast/cold-start/cold-start.md +6 -2
- package/skills/wix-headless-fast/guides/existing-site.md +3 -1
- package/skills/wix-headless-fast/guides/migration.md +43 -0
- package/skills/wix-headless-fast/guides/reference-mode.md +4 -1
- package/skills/wix-headless-fast/install/AGENTS.md +3 -1
- package/skills/wix-headless-fast/install/agents-md.mjs +5 -1
- package/skills/wix-headless-fast/install/attach.mjs +10 -0
- package/skills/wix-headless-fast/install/check.mjs +87 -0
- package/skills/wix-headless-fast/install/context.mjs +126 -0
- package/skills/wix-headless-fast/install/deploy.mjs +85 -20
- package/skills/wix-headless-fast/install/setup.mjs +52 -12
- package/skills/wix-manage/SKILL.md +3 -3
- package/skills/wix-manage/references/app-installation/install-wix-apps.md +68 -2
- package/skills/wix-manage/references/contacts/bulk-label-and-unlabel-contacts.md +37 -4
- package/skills/wix-manage/references/ecommerce/ecom-pricing.md +29 -3
- package/skills/wix-manage/references/sites/site-import.md +101 -37
package/package.json
CHANGED
package/skills/wix-app/SKILL.md
CHANGED
|
@@ -28,8 +28,8 @@ Helps build extensions for Wix CLI applications. Covers all extension types: das
|
|
|
28
28
|
- [ ] **Every filter reaches the query**: declared in the collection hook's `filters` and read inside `fetchData`. Filter UI that never narrows the rows is a defect that looks like a feature.
|
|
29
29
|
|
|
30
30
|
A filtered table with no drill-in and no working filters is what gets built when nobody states the requirement — the most common way a generated dashboard disappoints. The aggregate is the judgment call; the drill-in and the filters are not.
|
|
31
|
-
- [ ] **🛑 Template-First Gate (MANDATORY, dashboard UI only, comes before writing any shell/provider/router):** Identified which case in [DRAFT_TEMPLATE.md](references/dashboard-page/DRAFT_TEMPLATE.md#which-case-matches-the-request) — A (collection only), B (collection + entity), C (settings only), or D (all three) — matches what Step 2's workflow analysis above just established — then copied and adapted that case's files. Composing the page shell, provider nesting, or router wiring from scratch when a case already shows it is the failure mode this gate exists to prevent — the Patterns/Component Docs gates below are for what the matching case's files don't cover, not a replacement for starting there.
|
|
32
|
-
- [ ] **🛑 Patterns Docs Gate (MANDATORY for any dashboard page UI):** Read [WIX_PATTERNS_DOCS.md](references/WIX_PATTERNS_DOCS.md),
|
|
31
|
+
- [ ] **🛑 Template-First Gate (MANDATORY, dashboard UI only, comes before writing any shell/provider/router):** Identified which case in [DRAFT_TEMPLATE.md](references/dashboard-page/DRAFT_TEMPLATE.md#which-case-matches-the-request) — A (collection only), B (collection + entity), C (settings only), or D (all three) — matches what Step 2's workflow analysis above just established — then copied and adapted that case's files. Composing the page shell, provider nesting, or router wiring from scratch when a case already shows it is the failure mode this gate exists to prevent — the Patterns/Component Docs gates below are for what the matching case's files don't cover, not a replacement for starting there. The reverse holds too: a case replaces composing the shell, never the discovery chain, so every symbol the case doesn't show still goes through the next gate.
|
|
32
|
+
- [ ] **🛑 Patterns Docs Gate (MANDATORY for any dashboard page UI):** Read [WIX_PATTERNS_DOCS.md](references/WIX_PATTERNS_DOCS.md) **before the first command that touches `node_modules/@wix/patterns`** — its discovery chain (`pkg-root.cjs`, the index, `Composition and Providers.md` once, `Collection Toolkit.md` for which component serves a need) lives only in that file, and this line is a summary of its step 1, not a substitute for opening it. Then **probe** `dist/docs/index.json` with `grep`/`python3` — never a whole-file `Read`, which truncates it silently. It is the one file that says, per symbol, where to import it from (`importPath`), whether its props live in the doc or in a `.d.ts` (`bundle`), and which worked examples exist (`examples`). Upgrade `@wix/patterns` if that file is missing. Patterns API facts come only from the published `dist/docs/` (pages), `dist/examples/` (worked calls) and `dist/dts-bundle/` (types) trees — never from `src/`, `dist/esm/`, or any other path inside the package, with one named exception: `dist/types/` when a bundle has stubbed the prop you need (WIX_PATTERNS_DOCS.md step 5).
|
|
33
33
|
- [ ] **🛑 Component Docs Gate (MANDATORY, dashboard UI only):** For each patterns symbol you are about to write, decided **from the index** which single artifact answers the question you actually have — `importPath`, `examples`, or `bundle` — and read only that one, per [Component Selection Order](#component-selection-order)'s "the short version". State which artifact you read per symbol, and why, before the first line of JSX. Reading a doc *and* its bundle for the same symbol, or opening a page for an `importPath` the index already gave you, is the failure this gate exists to prevent.
|
|
34
34
|
|
|
35
35
|
For the object `useTableCollection()` returns, read [TABLE_STATE.md](references/dashboard-page/TABLE_STATE.md) — a state object you receive rather than construct, whose members are unobvious and several plausible ones absent.
|
|
@@ -226,6 +226,14 @@ Use a Dashboard Modal for dialogs that neither write nor display a listed record
|
|
|
226
226
|
|
|
227
227
|
Find the entity the user mentioned in the [entity → SDK module map](references/SDK_MODULE_MAP.md) and use that package. If the entity isn't listed or you're unsure, run `SearchWixSDKDocumentation` for it — **never conclude CMS with zero MCP calls**. CMS is only for data your app itself introduces (configuration, rules, app-specific records) that no Wix app manages.
|
|
228
228
|
|
|
229
|
+
**SDK types:** access them through the namespace you import, as `<namespace>.<TypeName>`, using any type name shown in the docs — never import a type by name from the `@wix/<pkg>` root.
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
import { orders } from '@wix/ecom';
|
|
233
|
+
const rows: orders.Order[] = []; // ✅
|
|
234
|
+
// import type { Order } from '@wix/ecom'; // ❌ has no exported member 'Order'
|
|
235
|
+
```
|
|
236
|
+
|
|
229
237
|
---
|
|
230
238
|
|
|
231
239
|
## Data Collection Inference
|
|
@@ -440,7 +448,7 @@ Only after validation passes, provide a **concise summary section** at the top o
|
|
|
440
448
|
- ✅ Dependencies: [Installed / status message]
|
|
441
449
|
- ✅ TypeScript: [No compilation errors / status]
|
|
442
450
|
- ✅ Build: [Completed successfully / status]
|
|
443
|
-
- ✅/⚠️ Preview: [
|
|
451
|
+
- ✅/⚠️ Preview: [Created — Dashboard URL / Failed - reason]
|
|
444
452
|
|
|
445
453
|
**⚠️ IMPORTANT: [X] manual step(s) required to complete setup** (see "Manual Steps Required" section below)
|
|
446
454
|
```
|
|
@@ -480,7 +488,7 @@ Execute these steps sequentially after all implementation is complete. See [APP_
|
|
|
480
488
|
1. **Package Installation** — Detect package manager, run install
|
|
481
489
|
2. **TypeScript Compilation** — `npx tsc --noEmit -p .`
|
|
482
490
|
3. **Build** — `npx wix build`
|
|
483
|
-
4. **Preview** — `npx wix preview`
|
|
491
|
+
4. **Preview** — `npx wix preview`, in the foreground: it uploads, prints the preview URLs and exits on its own, so no `timeout`, backgrounding or `sleep`
|
|
484
492
|
|
|
485
493
|
Stop and report errors if any step fails. Check `.wix/debug.log` on failures.
|
|
486
494
|
|
|
@@ -85,23 +85,29 @@ npx wix build
|
|
|
85
85
|
|
|
86
86
|
### Step 4: Preview Deployment
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
`npx wix preview` is a one-shot command, not a server: it uploads a preview build, prints the preview URLs and **exits on its own**, in seconds. Run it in the foreground and read its output:
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
91
|
npx wix preview
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
Don't wrap it in `timeout` (macOS has none, so the call fails before preview runs), background it, `sleep` or poll a log for it, or `kill` it afterwards. There is no process left to wait for or stop.
|
|
95
|
+
|
|
94
96
|
**Success criteria:**
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
+
- The command exits 0
|
|
98
|
+
- It printed `✔ Preview created successfully!` and the preview URLs
|
|
99
|
+
|
|
100
|
+
**URL extraction:** the URLs follow `Open the preview on:`, one per line, each in parentheses:
|
|
97
101
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
```
|
|
103
|
+
› Site (https://www.wix.com/app-installer?appId=...)
|
|
104
|
+
› Editor (https://www.wix.com/app-installer?appId=...)
|
|
105
|
+
› Dashboard (https://www.wix.com/app-installer?appId=...)
|
|
106
|
+
```
|
|
101
107
|
|
|
102
|
-
|
|
108
|
+
A long URL wraps across several lines. Join the lines up to the closing `)` before using it. Give the Site and Dashboard URLs to the user for manual verification.
|
|
103
109
|
|
|
104
|
-
**On failure:** Report the preview
|
|
110
|
+
**On failure:** Report the preview errors, [check the debug log](#debug-log-on-errors) for detailed diagnostics, and stop validation.
|
|
105
111
|
|
|
106
112
|
## Validation Report
|
|
107
113
|
|
|
@@ -111,7 +117,7 @@ After completing all steps, provide a summary:
|
|
|
111
117
|
- Dependencies: ✓ All packages installed successfully
|
|
112
118
|
- TypeScript: ✓ No compilation errors
|
|
113
119
|
- Build: ✓ Compiled successfully
|
|
114
|
-
- Preview: ✓
|
|
120
|
+
- Preview: ✓ Created — [Dashboard URL]
|
|
115
121
|
|
|
116
122
|
**Fail:**
|
|
117
123
|
- Identify which step failed
|
|
@@ -138,7 +144,7 @@ Read: .wix/debug.log (with offset to the end)
|
|
|
138
144
|
| Package installation fails | Missing lock file, network issues, or corrupted node_modules | Delete `node_modules` and lock file, then reinstall |
|
|
139
145
|
| TypeScript compilation fails | Type mismatches, missing declarations, or incorrect types | Fix TypeScript errors shown in `npx tsc --noEmit -p .` output |
|
|
140
146
|
| Build fails | TypeScript errors, missing dependencies, or internal CLI error | Fix TypeScript errors in source; for non-obvious failures, check `.wix/debug.log` |
|
|
141
|
-
| Preview fails
|
|
147
|
+
| Preview fails | Config issue, not logged in, or internal CLI error | Check `wix.config.json`; if unclear, check `.wix/debug.log` for details |
|
|
142
148
|
| Console errors in preview | Runtime exceptions | Check browser console output |
|
|
143
149
|
| UI not rendering | Component errors | Review component code and imports |
|
|
144
150
|
| CLI error with no clear message | Truncated terminal output | Read `.wix/debug.log` for the full error trace and stack details |
|
|
@@ -41,4 +41,6 @@ Data owned by an existing Wix business app is read and written through that app'
|
|
|
41
41
|
| locations / site properties | `@wix/business-tools` |
|
|
42
42
|
| app instances | `@wix/app-management` |
|
|
43
43
|
|
|
44
|
+
Types from these packages are accessed as `<namespace>.<TypeName>` — see [SDK types](../SKILL.md#sdk-first-rule-existing-wix-app-data-is-never-cms).
|
|
45
|
+
|
|
44
46
|
If the entity isn't listed or you're unsure, run `SearchWixSDKDocumentation` for it — **never conclude CMS with zero MCP calls**. CMS is only for data your app itself introduces (configuration, rules, app-specific records) that no Wix app manages.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Draft Template — the starting point for every dashboard page
|
|
2
2
|
|
|
3
|
-
**Start here for any Dashboard Page request, before writing a shell, provider, or router from scratch.** Pick the case below, copy its files, rename, and adapt fields/API calls/data source. Only leave this file
|
|
3
|
+
**Start here for any Dashboard Page request, before writing a shell, provider, or router from scratch.** Pick the case below, copy its files, rename, and adapt fields/API calls/data source. Only leave this file when the request needs something no case shows, and then go to [WIX_PATTERNS_DOCS.md](../WIX_PATTERNS_DOCS.md) first — before any lookup inside `node_modules/@wix/patterns`, `index.json` included. It routes you to the package's own `Collection Toolkit.md` guide, a component doc or an example; [TABLE_STATE.md](TABLE_STATE.md) and MCP lookups come after it.
|
|
4
4
|
|
|
5
5
|
Every snippet below was copied from the installed `dist/docs/*.md` and `dist/dts-bundle/*.d.ts`, not from memory — confirm props against your own installed version before deviating.
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wix-headless-fast
|
|
3
|
-
description: "Build a Wix Headless site fast by wiring SHIPPED, verified @wix/sdk code instead of authoring the integration from recipes. Each Wix business vertical ships a typed, framework-agnostic React core (data layer returning plain DTOs, hooks, headless components) plus an Astro overlay (SSR pages with owner-editable SEO pre-wired) and a build-time REST seed script — the agent scaffolds via the Wix CLI, deploys the shipped code, seeds the backend, designs the presentation layer itself on the shipped hooks (product card/grid, PDP, home, theme), and releases to Wix hosting. Works on Wix-managed Astro (ambient auth, the default) and on any React-based project (Vite, non-Astro) over the public OAuth client id. Verticals: stores/storefront (products, categories, variants, cart, hosted checkout), bookings (services, appointment/class time slots, staff, booking form, checkout-or-place), blog (posts, categories/tags, rich content), cms (structured content collections), forms (schema-driven visitor forms: render, validate, submit), events (listing, RSVP, ticket sales), members (login, gated pages, account), portfolio (project collections, media galleries), pricing-plans (plan grid, hosted purchase), restaurants (menus, online ordering, table reservations). Triggers: build me a store/blog/booking/event/restaurant/portfolio site fast, take appointments fast, sell tickets or membership plans headless, wix headless fast, connect a Wix business app with ready-made SDK code."
|
|
3
|
+
description: "Build a Wix Headless site fast by wiring SHIPPED, verified @wix/sdk code instead of authoring the integration from recipes. Each Wix business vertical ships a typed, framework-agnostic React core (data layer returning plain DTOs, hooks, headless components) plus an Astro overlay (SSR pages with owner-editable SEO pre-wired) and a build-time REST seed script — the agent scaffolds via the Wix CLI, deploys the shipped code, seeds the backend, designs the presentation layer itself on the shipped hooks (product card/grid, PDP, home, theme), and releases to Wix hosting. Works on Wix-managed Astro (ambient auth, the default) and on any React-based project (Vite, non-Astro) over the public OAuth client id. Verticals: stores/storefront (products, categories, variants, cart, hosted checkout), bookings (services, appointment/class time slots, staff, booking form, checkout-or-place), blog (posts, categories/tags, rich content), cms (structured content collections), forms (schema-driven visitor forms: render, validate, submit), events (listing, RSVP, ticket sales), members (login, gated pages, account), portfolio (project collections, media galleries), pricing-plans (plan grid, hosted purchase), restaurants (menus, online ordering, table reservations), faq (categorized questions and answers, search, a link per question), donations (campaign pages, goal progress, one-time and recurring donations via hosted checkout). Triggers: build me a store/blog/booking/event/restaurant/portfolio/FAQ/donation site fast, take appointments fast, sell tickets or membership plans headless, collect donations headless, wix headless fast, connect a Wix business app with ready-made SDK code."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Wix Headless Fast
|
|
@@ -74,7 +74,13 @@ doesn't express — or once the site exists and the work turns to managing or ex
|
|
|
74
74
|
`capabilities.mediaUpload.policies` entry to the plan; Fast ships its client helper, Astro
|
|
75
75
|
endpoint, dependencies, and generated policy module once. Read
|
|
76
76
|
`templates/shared/CUSTOM_OPERATIONS.md` before choosing it. The agent wires the helper to
|
|
77
|
-
the product UI; it never authors or widens the endpoint.
|
|
77
|
+
the product UI; it never authors or widens the endpoint. For a site-wide search (a header box
|
|
78
|
+
with suggestions, a `/search?q=` page over the deployed verticals' products, services, posts
|
|
79
|
+
and events), add a `capabilities.siteSearch` entry; Fast ships its data layer, stores, hooks,
|
|
80
|
+
components and search page once. Enabling it means the Wix Site Search app is installed on the
|
|
81
|
+
site by the seed step (`install: true`; the capability's `seed/install.mjs`, run after the
|
|
82
|
+
content seed) — the index fills within about half a minute of the install. Playbook:
|
|
83
|
+
`templates/shared/capabilities/site-search/INSTRUCTIONS.md`.
|
|
78
84
|
|
|
79
85
|
## The run
|
|
80
86
|
|
|
@@ -83,9 +89,18 @@ Needed throughout: Node ≥ 20.11, git, a logged-in Wix CLI (`npx @wix/cli@lates
|
|
|
83
89
|
read tokens into context), and the two companion skills beside this one, `wix-docs` and
|
|
84
90
|
`wix-manage` (if `.agents/skills/` lacks them:
|
|
85
91
|
`CI=1 npx skills@latest add wix/skills --skill wix-docs --skill wix-manage --yes`). The cold-start
|
|
86
|
-
page, `cold-start/cold-start.md`, gets a machine with none of this to that point.
|
|
92
|
+
page, `cold-start/cold-start.md`, gets a machine with none of this to that point. In a folder that
|
|
93
|
+
already holds a `wix.config.json`, `node <SKILL_ROOT>/install/context.mjs` first: it runs
|
|
94
|
+
`wix env pull` when `.env.local` is missing and prints the two identities a project has — the
|
|
95
|
+
deploy site (the config, where `wix release` goes) and the content site (the env, whose app the SDK
|
|
96
|
+
client runs as and whose dashboard manages the business). They are one site, except on a
|
|
97
|
+
**migration preview** (`guides/migration.md`), where the env names the site being migrated.
|
|
98
|
+
Every script here reads that context; the site a call targets is never guessed from the config
|
|
99
|
+
alone. Then fetch the shipped
|
|
87
100
|
code once: `node <SKILL_ROOT>/install/templates.mjs`. It prints the folder;
|
|
88
101
|
the `templates/…` paths below are relative to `<SKILL_ROOT>`, where it lands.
|
|
102
|
+
`node <SKILL_ROOT>/install/check.mjs` says whether the skill or its templates have a newer version
|
|
103
|
+
and prints the update commands; it changes nothing.
|
|
89
104
|
|
|
90
105
|
1. **Resolve the stack.** Default is **Wix-managed Astro** — take it unless the user names
|
|
91
106
|
another framework or the directory already holds one. Then, by what the shipped code can run
|
|
@@ -157,13 +172,20 @@ the `templates/…` paths below are relative to `<SKILL_ROOT>`, where it lands.
|
|
|
157
172
|
- **`wix.config.json` and a project** → refuses: the folder is already a Wix project with a
|
|
158
173
|
frontend, whether the CLI made it, a hand wrote it, or this skill built it. Three commands
|
|
159
174
|
from the project root do everything setup would: `deploy.mjs <vertical…> --stack <stack>`
|
|
160
|
-
adds this skill's code or a solution (the client id
|
|
175
|
+
adds this skill's code or a solution (the client id comes from `.env.local`, the config as
|
|
176
|
+
the fallback), then ONE
|
|
161
177
|
`npm install`, then the seed module if there is content to create. A project this skill
|
|
162
178
|
built needs only the first, for a new solution; a change to what is built is file edits
|
|
163
179
|
and a release.
|
|
164
|
-
- **`wix.config.json` and no project
|
|
165
|
-
|
|
166
|
-
|
|
180
|
+
- **`wix.config.json` and no project, and `.env.local` declares a migration** (a project
|
|
181
|
+
downloaded from Wix for a site being moved to headless; the config names a site that only
|
|
182
|
+
hosts the deployment, the env the site being migrated) → **migrate**: setup copies the
|
|
183
|
+
vertical's composed template in around the config, deploys with the migrated site's app as
|
|
184
|
+
the client, starts the install, seeds nothing. `ready_for_brand_layer` says `mode: "migrate"`,
|
|
185
|
+
the parent as `siteId` and the child as `deploySiteId`. `guides/migration.md` has the rules.
|
|
186
|
+
- **`wix.config.json` and no project**, no migration (what `init` leaves in an empty folder) →
|
|
187
|
+
the site exists and has no frontend yet: `attach.mjs` (below), which takes the site from the
|
|
188
|
+
config, reuses its hosting, scaffolds and deploys. No seed: the site owns its content.
|
|
167
189
|
- The brief names a site by id → not this call: read `<SKILL_ROOT>/guides/existing-site.md`
|
|
168
190
|
and follow it (read the site, then `attach.mjs`, which does what setup does against the
|
|
169
191
|
site given; self-hosting and a project already on disk are in there too).
|
|
@@ -235,7 +257,10 @@ the `templates/…` paths below are relative to `<SKILL_ROOT>`, where it lands.
|
|
|
235
257
|
build+release mid-flow; backend content is fetched at
|
|
236
258
|
runtime, so a re-release never "refreshes" seeded data. The run is complete only when the
|
|
237
259
|
site is released — close with the live URL and the dashboard link
|
|
238
|
-
`https://manage.wix.com/dashboard/<siteId
|
|
260
|
+
`https://manage.wix.com/dashboard/<siteId>` (the `siteId` of the `ready_for_brand_layer`
|
|
261
|
+
event: on a migration preview that is the migrated site's dashboard, the release URL is the
|
|
262
|
+
preview's, the original site is unchanged, and completing the migration is the user's next
|
|
263
|
+
step in the Wix CLI once they approve — say all three). **Copy the live URL verbatim from the
|
|
239
264
|
`wix release` output — never retype it from memory** (a mistyped subdomain hands the user
|
|
240
265
|
a 404).
|
|
241
266
|
|
|
@@ -264,6 +289,8 @@ close.
|
|
|
264
289
|
| Portfolio/showcase: collections of projects, project pages with media galleries | **portfolio** | `templates/portfolio/INSTRUCTIONS.md` |
|
|
265
290
|
| Membership/subscription plans: pricing page, plan detail, hosted purchase | **pricing-plans** | `templates/pricing-plans/INSTRUCTIONS.md` |
|
|
266
291
|
| Restaurant: menu with photos, online ordering, table reservations | **restaurants** | `templates/restaurants/INSTRUCTIONS.md` |
|
|
292
|
+
| FAQ: questions grouped by category, search, expandable answers, a link per question | **faq** | `templates/faq/INSTRUCTIONS.md` |
|
|
293
|
+
| Donations: campaign pages, goal progress, one-time and recurring giving via hosted checkout | **donations** | `templates/donations/INSTRUCTIONS.md` |
|
|
267
294
|
|
|
268
295
|
Verticals compose: a brief that spans several (a restaurant with a blog, a store with member
|
|
269
296
|
accounts) deploys them together — setup takes one vertical; deploy the rest with
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wix-headless-fast-cold-start
|
|
3
|
-
description: "Build and release a Wix Headless site from a single prompt, on shipped, verified @wix/sdk code — this entry takes a cold environment (prerequisites, Wix sign-in) to the point where the wix-headless-fast skill runs the build. Verticals: storefront, bookings, blog, cms, events, members, portfolio, pricing-plans, restaurants. Triggers: build me a store/blog/booking/events/portfolio/restaurant site fast, sell tickets or membership plans headless, wix headless fast entry."
|
|
3
|
+
description: "Build and release a Wix Headless site from a single prompt, on shipped, verified @wix/sdk code — this entry takes a cold environment (prerequisites, Wix sign-in) to the point where the wix-headless-fast skill runs the build. Verticals: storefront, bookings, blog, cms, events, members, portfolio, pricing-plans, restaurants, faq, donations. Triggers: build me a store/blog/booking/events/portfolio/restaurant/FAQ/donation site fast, sell tickets or membership plans headless, collect donations headless, wix headless fast entry."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Wix Headless Fast — cold start
|
|
@@ -45,7 +45,11 @@ existing session just reports `logged_in`).
|
|
|
45
45
|
**Work from the folder that holds the project, or that will.** For a **new** build: an empty folder
|
|
46
46
|
is the project; if the current folder holds unrelated things, make one named for the business and
|
|
47
47
|
`cd` into it. For **connect**, **iterate** and **existing site**: you are already in it — the
|
|
48
|
-
project's root, where its `package.json` or `index.html` is.
|
|
48
|
+
project's root, where its `package.json` or `index.html` is. For a **project downloaded from Wix**
|
|
49
|
+
(the brief gives a zip URL or path, typically a site being migrated to headless): download it and
|
|
50
|
+
extract it into the working folder first, so that `wix.config.json` sits at the root, and work from
|
|
51
|
+
there — the skill reads the folder and knows what it is; do not create a site, install apps or seed.
|
|
52
|
+
Everything below — the
|
|
49
53
|
bootstrap, the skills, the scaffold — lands in this folder, so a later session opened in the project
|
|
50
54
|
finds all of it.
|
|
51
55
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# An existing site: a new frontend for a site that already has its content
|
|
2
2
|
|
|
3
3
|
Read when the brief names a Wix site by its id. Nothing here is seeded: the site owns its content
|
|
4
|
-
and the frontend reads it live.
|
|
4
|
+
and the frontend reads it live. (A project downloaded from Wix whose `.env.local` declares a
|
|
5
|
+
migration is the other way to arrive at an existing site: `guides/migration.md`; the reading
|
|
6
|
+
part below applies there too, against the migrated site.) `<SKILL_ROOT>` is the installed skill folder; the shipped code is
|
|
5
7
|
under `<SKILL_ROOT>/templates/` (SKILL.md, "The run").
|
|
6
8
|
|
|
7
9
|
**Read the site, then run attach.** First, one call tells you what
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# A migration preview: a site being moved to headless
|
|
2
|
+
|
|
3
|
+
Read when setup reports `mode: "migrate"`, or when a folder holds a `wix.config.json` and
|
|
4
|
+
`node <SKILL_ROOT>/install/context.mjs` prints `migration.active: true`. The brief usually arrives
|
|
5
|
+
as a project to download from Wix (a zip with the config), the names of the apps the site already
|
|
6
|
+
has, and a request not to probe, install or seed anything.
|
|
7
|
+
|
|
8
|
+
**Two sites, one project.** `wix.config.json` names a site created only to host the deployment:
|
|
9
|
+
`wix release` goes there and nowhere else. `.env.local` (what `wix env pull` writes; setup and
|
|
10
|
+
`context.mjs` pull it when it is missing) carries the credentials of the site being migrated, the
|
|
11
|
+
**parent**, and says so. The parent owns everything the pages show and everything the business
|
|
12
|
+
manages. So:
|
|
13
|
+
|
|
14
|
+
- **The SDK client is the parent's.** The Astro integration reads `WIX_CLIENT_ID` from the env and
|
|
15
|
+
never the config; `deploy.mjs` copies the same value into `src/wix/config.ts` (react, lib) and
|
|
16
|
+
`js/wix/config.js` (static). Nothing in the data layer is told about the migration; it reads the
|
|
17
|
+
parent because its client is the parent's app.
|
|
18
|
+
- **Admin, discovery and seed calls target the parent.** `read-site.mjs` and the seeds resolve the
|
|
19
|
+
site through `templates/shared/seed/site-context.mjs`; `wix-manage` recipes and any raw call take
|
|
20
|
+
`--site <parentSiteId>` (the `siteId` of the `ready_for_brand_layer` event). The dashboard link
|
|
21
|
+
is the parent's.
|
|
22
|
+
- **Nothing is seeded, nothing installed.** The parent is a live site with its content; a seed
|
|
23
|
+
would write into the original. The seeds refuse on a preview unless run with `--allow-parent`,
|
|
24
|
+
which is the user's explicit decision, never a default. Apps are not installed: the brief names
|
|
25
|
+
what the site has, and the verticals follow from that list (Wix Stores → storefront, Wix
|
|
26
|
+
Bookings → bookings, Wix Forms → forms, and so on per the Verticals table; Wix Pro Gallery and
|
|
27
|
+
Wix Members Area map to portfolio and members). When the brief allows reading, the vertical's
|
|
28
|
+
`read-site.mjs` sizes the content as in `guides/existing-site.md`; when it says not to probe,
|
|
29
|
+
design from the brief.
|
|
30
|
+
|
|
31
|
+
**The run.** `setup.mjs --vertical <vertical> [--stack <stack>]` in the folder: it copies the
|
|
32
|
+
vertical's composed template around the config (managed Astro), deploys the code, starts the
|
|
33
|
+
install, writes the project's AGENTS.md with the migration recorded, and skips the seed. Then
|
|
34
|
+
SKILL.md steps 4 and 5 as for any run. `attach.mjs` refuses a migration folder on purpose: the
|
|
35
|
+
app, hosting and credentials came with the download; nothing is provisioned or re-pointed.
|
|
36
|
+
|
|
37
|
+
**The close.** The release is a **preview** on the deploy site. Close with the preview URL (copied
|
|
38
|
+
from the `wix release` output), the parent's dashboard link, and the two facts: the original site
|
|
39
|
+
is unchanged, and completing the migration, which makes the parent serve this frontend and retires
|
|
40
|
+
the deploy site, is the user's next step in the Wix CLI once they approve the preview. That
|
|
41
|
+
command is not part of this skill; do not invent one, do not run `wix release` against the parent,
|
|
42
|
+
and do not edit `wix.config.json`. On every stack the code already carries the parent's client id,
|
|
43
|
+
so completion needs no redeploy from here.
|
|
@@ -9,7 +9,10 @@ The data layer ships a second time as a **REST layer**: `templates/shared/rest/`
|
|
|
9
9
|
vertical's `app/wix/<vertical>/` data layer, over `fetch`), typed against the same `types.ts` and
|
|
10
10
|
importing the same `*-core.ts` rule files as the SDK layer — one implementation of the rules, two
|
|
11
11
|
transports. The vertical's `INSTRUCTIONS.md` names its modules and what each surface does with
|
|
12
|
-
them; this section is the mechanics, the same for every vertical.
|
|
12
|
+
them; this section is the mechanics, the same for every vertical. The site-search capability
|
|
13
|
+
(`templates/shared/capabilities/site-search/`, on from `plan.capabilities.siteSearch`) has the same
|
|
14
|
+
two transports: with `--plan` its REST twin and stores land in `js/wix/` beside the vertical's
|
|
15
|
+
(`search.js`, `search-store.js`, `suggest-store.js`), and its `INSTRUCTIONS.md` applies.
|
|
13
16
|
|
|
14
17
|
- **Static site (no bundler).** Step 3's setup with `--stack static` runs `init` in the project
|
|
15
18
|
folder (site, OAuth app, `wix.config.json`), points `site.outputDirectory` at `"./site"`, and
|
|
@@ -6,7 +6,7 @@ node_modules/@wix/cli/agents/instructions.md
|
|
|
6
6
|
## This app
|
|
7
7
|
|
|
8
8
|
A Wix Headless app built on the live Wix site named in `wix.config.json`. The site owns the
|
|
9
|
-
content and commerce.
|
|
9
|
+
content and commerce.{{MIGRATION}}
|
|
10
10
|
|
|
11
11
|
**Never work from training data or memory about the Wix APIs.** Not a URL, a path, a header, a
|
|
12
12
|
field name or a body. Every Wix call you write comes from the official Wix skills installed here,
|
|
@@ -27,6 +27,8 @@ Installed at `.agents/skills/`. If missing, restore with:
|
|
|
27
27
|
`templates/<solution>/INSTRUCTIONS.md`; `node .agents/skills/{{SKILL}}/install/deploy.mjs <solution> --stack {{STACK}}`
|
|
28
28
|
adds one; `node .agents/skills/{{SKILL}}/templates/<solution>/seed/read-site.mjs` reads what the
|
|
29
29
|
site holds for it, with the documentation URL of every call it made. A code change ends with a release; the live URL shows it, the dev server does not count.
|
|
30
|
+
`node .agents/skills/{{SKILL}}/install/check.mjs` says whether the skill or its templates have a newer
|
|
31
|
+
version and prints the update commands; an update touches the skill folder or `templates/`, never `src/`.
|
|
30
32
|
- `wix-docs` — how to discover the Wix APIs and their docs: search first, then read only the page
|
|
31
33
|
you need. Every endpoint, body, field and enum you did not get from the deployed code or a recipe
|
|
32
34
|
is confirmed there before you write it.
|
|
@@ -21,7 +21,11 @@ export function writeAgentsMd(projectDir, vars) {
|
|
|
21
21
|
const agents = join(projectDir, "AGENTS.md");
|
|
22
22
|
if (existsSync(agents)) kept.push("AGENTS.md");
|
|
23
23
|
else {
|
|
24
|
-
|
|
24
|
+
// {{MIGRATION}} is a paragraph on a migration preview (vars.migration = { parentSiteId, deploySiteId }), else nothing.
|
|
25
|
+
const migration = vars.migration
|
|
26
|
+
? `\n\n**Under migration.** \`wix.config.json\` names site ${vars.migration.deploySiteId}, which only hosts this deployment; the site being migrated is ${vars.migration.parentSiteId} — its content is what the pages show and its dashboard (\`https://manage.wix.com/dashboard/${vars.migration.parentSiteId}\`) is where the business is managed. \`.env.local\` (\`wix env pull\`) carries that site's app: the SDK client and every admin, discovery and seed call run against it; only \`wix release\` goes to the deploy site. Completing the migration is a separate Wix CLI step, run by the user after approving the preview.`
|
|
27
|
+
: "";
|
|
28
|
+
const text = readFileSync(TEMPLATE, "utf8").replace(/\{\{(\w+)\}\}/g, (_, k) => (k === "MIGRATION" ? migration : (vars[k.toLowerCase()] ?? "")));
|
|
25
29
|
writeFileSync(agents, text); written.push("AGENTS.md");
|
|
26
30
|
}
|
|
27
31
|
const claude = join(projectDir, "CLAUDE.md");
|
|
@@ -45,6 +45,7 @@ import { cpSync, existsSync, mkdirSync, openSync, readFileSync, writeFileSync }
|
|
|
45
45
|
import { basename, dirname, join, resolve } from "node:path";
|
|
46
46
|
import { fileURLToPath } from "node:url";
|
|
47
47
|
import { writeAgentsMd } from "./agents-md.mjs";
|
|
48
|
+
import { siteContext } from "./context.mjs";
|
|
48
49
|
import { listVerticals, templatesDir } from "./templates.mjs";
|
|
49
50
|
|
|
50
51
|
const SKILL_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
@@ -76,6 +77,15 @@ const cwd = process.cwd();
|
|
|
76
77
|
const has = (p) => existsSync(join(cwd, p));
|
|
77
78
|
const cwdConfig = has("wix.config.json") ? JSON.parse(readFileSync(join(cwd, "wix.config.json"), "utf8")) : null;
|
|
78
79
|
const hasProject = has("package.json") || has("index.html");
|
|
80
|
+
// A migration preview (wix.config.json names a deploy-only site, .env.local names the site being
|
|
81
|
+
// migrated) is already provisioned: its app, hosting and credentials came with the folder. Nothing
|
|
82
|
+
// here applies — setup.mjs takes it (SKILL.md step 3).
|
|
83
|
+
if (cwdConfig) {
|
|
84
|
+
const ctx = siteContext({ cwd });
|
|
85
|
+
if (ctx.migration.active) {
|
|
86
|
+
fail("place", `this folder deploys a migration preview of site ${ctx.migration.parentSiteId} (deploy site ${ctx.deploy.siteId}): the app and hosting are provisioned already — run setup.mjs --vertical <vertical> [--stack <stack>] here instead; nothing is attached or re-pointed`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
79
89
|
const siteId = flag("site") ?? cwdConfig?.siteId ?? cwdConfig?.projectId ?? null;
|
|
80
90
|
const stack = stackFlag ?? "astro";
|
|
81
91
|
const knownVerticals = listVerticals(TEMPLATES);
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Is there a newer version? Read-only: compares this installed skill and its templates with the
|
|
2
|
+
// repository they came from and prints one JSON. It changes nothing and runs no update; the
|
|
3
|
+
// commands that would are in the output.
|
|
4
|
+
//
|
|
5
|
+
// node <SKILL_ROOT>/install/check.mjs
|
|
6
|
+
//
|
|
7
|
+
// Two layers, two answers:
|
|
8
|
+
// skill the installed skill folder (SKILL.md, guides/, install/, cold-start/) against the
|
|
9
|
+
// repository head: `current`, `behind` (with the files that differ), or `unknown`.
|
|
10
|
+
// templates `templates/.source` commit against the head: `current` or `behind`, with the
|
|
11
|
+
// verticals touched and the commit messages in between.
|
|
12
|
+
// Updating the skill changes how the agent works, not the project. Refreshing the templates changes
|
|
13
|
+
// what future deploys copy and what the agent reads, not `src/`. Nothing here touches `src/`.
|
|
14
|
+
import { mkdtempSync, readdirSync, rmSync } from "node:fs";
|
|
15
|
+
import { tmpdir } from "node:os";
|
|
16
|
+
import { join, relative } from "node:path";
|
|
17
|
+
import { spawnSync } from "node:child_process";
|
|
18
|
+
import { SKILL_ROOT, installSource, templatesDir, templatesSource } from "./templates.mjs";
|
|
19
|
+
|
|
20
|
+
const git = (args, opts = {}) => spawnSync("git", args, { encoding: "utf8", timeout: 120_000, ...opts });
|
|
21
|
+
const skillName = SKILL_ROOT.split("/").pop();
|
|
22
|
+
const { repo } = installSource();
|
|
23
|
+
|
|
24
|
+
const out = { skill: {}, templates: {}, update: {} };
|
|
25
|
+
|
|
26
|
+
// ---- the repository: one clone of its history without file contents (small), read for both layers
|
|
27
|
+
const tmp = mkdtempSync(join(tmpdir(), "wix-headless-fast-check-"));
|
|
28
|
+
const clone = git(["clone", "--quiet", "--filter=blob:none", "--no-checkout", "--single-branch", repo, tmp]);
|
|
29
|
+
if (clone.status !== 0) {
|
|
30
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
31
|
+
console.log(JSON.stringify({ error: `could not reach ${repo}: ${(clone.stderr || "").trim().slice(-200)}` }));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
const head = git(["-C", tmp, "rev-parse", "HEAD"]).stdout.trim();
|
|
35
|
+
|
|
36
|
+
// ---- skill folder: every installed file's blob hash against the head's tree -------------------
|
|
37
|
+
{
|
|
38
|
+
const tree = git(["-C", tmp, "ls-tree", "-r", head, "--", `skills/${skillName}`]).stdout;
|
|
39
|
+
const remote = new Map(tree.split("\n").filter(Boolean).map((l) => { const [meta, path] = l.split("\t"); return [path.slice(`skills/${skillName}/`.length), meta.split(" ")[2]]; }));
|
|
40
|
+
const local = [];
|
|
41
|
+
const walk = (dir) => {
|
|
42
|
+
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
|
43
|
+
const p = join(dir, e.name);
|
|
44
|
+
if (e.name === "templates" && dir === SKILL_ROOT) continue; // the templates are the other layer
|
|
45
|
+
if (e.isDirectory()) walk(p); else local.push(relative(SKILL_ROOT, p));
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
walk(SKILL_ROOT);
|
|
49
|
+
const differs = [], missingHere = [];
|
|
50
|
+
for (const f of local) if (remote.get(f) !== git(["hash-object", join(SKILL_ROOT, f)]).stdout.trim()) differs.push(f);
|
|
51
|
+
for (const f of remote.keys()) if (!local.includes(f)) missingHere.push(f);
|
|
52
|
+
out.skill = differs.length || missingHere.length
|
|
53
|
+
? { status: "behind", differs, ...(missingHere.length ? { missingHere } : {}), note: "a file that differs may also be one edited here; the update reinstalls the skill folder" }
|
|
54
|
+
: { status: "current" };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ---- templates: the recorded commit against the head ------------------------------------------
|
|
58
|
+
let dir = null;
|
|
59
|
+
try { dir = templatesDir(); } catch (e) { out.templates = { status: "unknown", detail: e.message }; }
|
|
60
|
+
if (dir) {
|
|
61
|
+
const src = templatesSource(dir);
|
|
62
|
+
if (src.checkout) out.templates = { status: "checkout", dir };
|
|
63
|
+
else if (!src.commit) out.templates = { status: "unknown", detail: "no commit recorded in templates/.source" };
|
|
64
|
+
else if (src.commit === head) out.templates = { status: "current", commit: src.commit };
|
|
65
|
+
else {
|
|
66
|
+
out.templates = { status: "behind", commit: src.commit, head };
|
|
67
|
+
const files = git(["-C", tmp, "diff", "--name-only", `${src.commit}..${head}`, "--", "templates"]).stdout.split("\n").filter(Boolean);
|
|
68
|
+
const log = git(["-C", tmp, "log", "--format=%s", `${src.commit}..${head}`, "--", "templates"]).stdout.split("\n").filter(Boolean);
|
|
69
|
+
if (files.length || log.length) {
|
|
70
|
+
out.templates.commitsBehind = log.length;
|
|
71
|
+
out.templates.verticals = [...new Set(files.map((f) => f.split("/")[1]).filter((v) => v && !v.endsWith(".mjs")))].sort();
|
|
72
|
+
out.templates.commits = log.slice(0, 20);
|
|
73
|
+
} else {
|
|
74
|
+
out.templates.detail = git(["-C", tmp, "cat-file", "-e", src.commit]).status === 0 ? "no template changes between the two commits" : "the recorded commit is not in the repository's history (a branch that was deleted?)";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
79
|
+
|
|
80
|
+
// ---- what would act on each ------------------------------------------------------------------
|
|
81
|
+
const rel = relative(process.cwd(), SKILL_ROOT) || ".";
|
|
82
|
+
out.update = {
|
|
83
|
+
skill: `CI=1 npx skills@latest update ${skillName} --yes (or: gh skill update ${skillName})`,
|
|
84
|
+
templates: `node ${rel}/install/templates.mjs --refresh (then commit the changed files under ${rel}/templates)`,
|
|
85
|
+
note: "neither touches src/; a shipped fix reaches the project by comparing its copy with the template",
|
|
86
|
+
};
|
|
87
|
+
console.log(JSON.stringify(out, null, 2));
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// The site context of a project folder — the one place that says which site a call targets.
|
|
2
|
+
//
|
|
3
|
+
// node <SKILL_ROOT>/install/context.mjs [--refresh] [--no-pull] (prints one JSON object)
|
|
4
|
+
//
|
|
5
|
+
// Two identities live in a Wix project and are the same site almost always:
|
|
6
|
+
// deploy — `wix.config.json` (`siteId`, `appId`): where `wix release` uploads. The CLI's business.
|
|
7
|
+
// content — `.env.local` (`WIX_CLIENT_ID`, what `wix env pull` writes): the app the SDK client
|
|
8
|
+
// runs as, on every stack (the Astro integration reads WIX_CLIENT_ID from the env and
|
|
9
|
+
// never the config; the other stacks get it copied into src/wix/config.ts by deploy.mjs).
|
|
10
|
+
// They differ on a MIGRATION PREVIEW: a project whose config points at a fresh site created only to
|
|
11
|
+
// host the deployment, while `.env.local` carries the credentials of the site being migrated (the
|
|
12
|
+
// parent) and says so. Then every admin, discovery and seed call targets the parent, the SDK client
|
|
13
|
+
// is the parent's, and only the release goes to the child. Completing the migration is a CLI step
|
|
14
|
+
// that does not exist yet; this module only reads the state.
|
|
15
|
+
//
|
|
16
|
+
// `env pull` runs here when `.env.local` is missing (or --refresh): it is the source of the content
|
|
17
|
+
// identity, and the Astro build refuses to run without it. Non-interactive (CI=1); a failure is
|
|
18
|
+
// reported in `pullError` and the config's ids stand in, so a caller can still work offline.
|
|
19
|
+
import { spawnSync } from "node:child_process";
|
|
20
|
+
import { copyFileSync, existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { tmpdir } from "node:os";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
|
|
24
|
+
// The variables `wix env pull` writes for a migration preview. The names are the platform's and are
|
|
25
|
+
// spelled once, here.
|
|
26
|
+
export const ENV = {
|
|
27
|
+
clientId: "WIX_CLIENT_ID",
|
|
28
|
+
parentSiteId: "WIX_MIGRATION_PARENT_SITE_ID",
|
|
29
|
+
migration: "WIX_MIGRATION",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** KEY=value lines of a dotenv file, quotes stripped; null when the file is absent. */
|
|
33
|
+
export function readEnvFile(file) {
|
|
34
|
+
if (!existsSync(file)) return null;
|
|
35
|
+
const out = {};
|
|
36
|
+
for (const raw of readFileSync(file, "utf8").split(/\r?\n/)) {
|
|
37
|
+
const line = raw.trim();
|
|
38
|
+
if (!line || line.startsWith("#")) continue;
|
|
39
|
+
const eq = line.indexOf("=");
|
|
40
|
+
if (eq === -1) continue;
|
|
41
|
+
const key = line.slice(0, eq).trim().replace(/^export\s+/, "");
|
|
42
|
+
let value = line.slice(eq + 1).trim();
|
|
43
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
44
|
+
value = value.slice(1, -1).replace(/\\"/g, '"');
|
|
45
|
+
}
|
|
46
|
+
out[key] = value;
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function readWixConfig(cwd) {
|
|
52
|
+
const file = join(cwd, "wix.config.json");
|
|
53
|
+
if (!existsSync(file)) return null;
|
|
54
|
+
try { return JSON.parse(readFileSync(file, "utf8")); } catch { return null; }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const truthy = (v) => typeof v === "string" && /^(1|true|yes|on)$/i.test(v.trim());
|
|
58
|
+
|
|
59
|
+
const ENV_PULL = ["-y", "@wix/cli@latest", "env", "pull"];
|
|
60
|
+
const runPull = (dir) => spawnSync("npx", ENV_PULL, { cwd: dir, env: { ...process.env, CI: "1" }, encoding: "utf8", timeout: 180_000 });
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* `wix env pull` into `cwd/.env.local`. The CLI mounts `env` only once the folder reads as an Astro
|
|
64
|
+
* project (an astro.config.* file, or `site.outputDirectory` in the config); a folder that holds just
|
|
65
|
+
* the downloaded `wix.config.json` gets "unknown command 'env'". Then the pull runs in a temp folder
|
|
66
|
+
* holding a copy of the config and an empty astro.config.mjs, and the `.env.local` comes back here.
|
|
67
|
+
*/
|
|
68
|
+
export function pullEnv(cwd) {
|
|
69
|
+
const envFile = join(cwd, ".env.local");
|
|
70
|
+
let r = runPull(cwd);
|
|
71
|
+
if (r.status === 0 && existsSync(envFile)) return { ok: true, via: "in place" };
|
|
72
|
+
const notAProjectYet = /unknown command 'env'/.test(`${r.stderr}${r.stdout}`);
|
|
73
|
+
if (notAProjectYet) {
|
|
74
|
+
const tmp = mkdtempSync(join(tmpdir(), "wix-env-pull-"));
|
|
75
|
+
try {
|
|
76
|
+
copyFileSync(join(cwd, "wix.config.json"), join(tmp, "wix.config.json"));
|
|
77
|
+
writeFileSync(join(tmp, "astro.config.mjs"), "");
|
|
78
|
+
r = runPull(tmp);
|
|
79
|
+
if (r.status === 0 && existsSync(join(tmp, ".env.local"))) {
|
|
80
|
+
copyFileSync(join(tmp, ".env.local"), envFile);
|
|
81
|
+
return { ok: true, via: "stub project" };
|
|
82
|
+
}
|
|
83
|
+
} finally {
|
|
84
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return { ok: false, error: (r.stderr || r.stdout || "env pull produced no .env.local — is the Wix CLI logged in? (npx @wix/cli@latest whoami)").trim().slice(-400) };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* `{ deploy: { siteId, appId }, content: { siteId, clientId }, migration: { active, parentSiteId },
|
|
92
|
+
* env: { file, present, pulled }, pullError?, warnings: [] }`.
|
|
93
|
+
* `pull`: "auto" (default) pulls when `.env.local` is missing; true always; false never.
|
|
94
|
+
*/
|
|
95
|
+
export function siteContext({ cwd = process.cwd(), pull = "auto" } = {}) {
|
|
96
|
+
const config = readWixConfig(cwd) ?? {};
|
|
97
|
+
const deploy = { siteId: config.siteId ?? config.projectId ?? null, appId: config.appId ?? null };
|
|
98
|
+
const envFile = join(cwd, ".env.local");
|
|
99
|
+
let pulled = false, pullError;
|
|
100
|
+
if (deploy.siteId && (pull === true || (pull === "auto" && !existsSync(envFile)))) {
|
|
101
|
+
const r = pullEnv(cwd);
|
|
102
|
+
if (r.ok) pulled = r.via;
|
|
103
|
+
else pullError = r.error;
|
|
104
|
+
}
|
|
105
|
+
const env = readEnvFile(envFile) ?? {};
|
|
106
|
+
const parentSiteId = env[ENV.parentSiteId] || null;
|
|
107
|
+
const active = !!parentSiteId && (truthy(env[ENV.migration]) || env[ENV.migration] === undefined);
|
|
108
|
+
const migration = { active, parentSiteId: active ? parentSiteId : null };
|
|
109
|
+
const clientId = env[ENV.clientId] || deploy.appId;
|
|
110
|
+
const content = { siteId: active ? parentSiteId : deploy.siteId, clientId };
|
|
111
|
+
const warnings = [];
|
|
112
|
+
if (env[ENV.clientId] && deploy.appId && env[ENV.clientId] !== deploy.appId && !active) {
|
|
113
|
+
warnings.push(`.env.local ${ENV.clientId} differs from wix.config.json appId and no migration is declared — the SDK client runs as the env's app, the release goes to the config's site`);
|
|
114
|
+
}
|
|
115
|
+
if (active && migration.parentSiteId === deploy.siteId) {
|
|
116
|
+
warnings.push("the migration's parent site is the deploy site itself — nothing is being migrated");
|
|
117
|
+
}
|
|
118
|
+
return { deploy, content, migration, env: { file: envFile, present: Object.keys(env).length > 0, pulled }, ...(pullError ? { pullError } : {}), warnings };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---- CLI ----------------------------------------------------------------------------------------
|
|
122
|
+
if (process.argv[1] && /context\.mjs$/.test(process.argv[1])) {
|
|
123
|
+
const argv = process.argv.slice(2);
|
|
124
|
+
const pull = argv.includes("--no-pull") ? false : argv.includes("--refresh") ? true : "auto";
|
|
125
|
+
console.log(JSON.stringify(siteContext({ pull }), null, 2));
|
|
126
|
+
}
|