@wix/agent-skills 1.31.0 → 1.31.2
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 +7 -8
- package/skills/wix-app/references/DASHBOARD_PAGE.md +1 -1
- package/skills/wix-app/references/STORES_VERSIONING.md +1 -1
- package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +11 -50
- package/skills/wix-app/references/dashboard-page/DATA_SOURCES.md +14 -10
- package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE.md +5 -8
- package/skills/wix-app/references/dashboard-page/TABLE_STATE.md +5 -4
- package/skills/wix-headless-kit/SKILL.md +31 -14
- package/skills/wix-headless-kit/guides/api-run.md +116 -31
- package/skills/wix-headless-kit/guides/existing-site.md +9 -2
- package/skills/wix-headless-kit/guides/reference-mode.md +9 -1
- package/skills/wix-headless-kit/install/AGENTS.md +10 -7
- package/skills/wix-headless-kit/install/attach.mjs +35 -30
- package/skills/wix-headless-kit/install/background.mjs +32 -0
- package/skills/wix-headless-kit/install/bootstrap.mjs +28 -11
- package/skills/wix-headless-kit/install/check.mjs +2 -2
- package/skills/wix-headless-kit/install/context.mjs +62 -8
- package/skills/wix-headless-kit/install/deploy.mjs +36 -11
- package/skills/wix-headless-kit/install/env-ignore.mjs +16 -0
- package/skills/wix-headless-kit/install/install.mjs +95 -0
- package/skills/wix-headless-kit/install/pins.json +1 -1
- package/skills/wix-headless-kit/install/proc.mjs +46 -0
- package/skills/wix-headless-kit/install/setup.mjs +18 -27
- package/skills/wix-headless-kit/install/templates.mjs +4 -3
- package/skills/wix-headless-templates/blog/app/components/blog/RichContent.tsx +3 -2
- package/skills/wix-headless-templates/blog/project/package-lock.json +1287 -1299
- package/skills/wix-headless-templates/blog/project/package.json +1 -1
- package/skills/wix-headless-templates/blog/project/src/components/blog/RichContent.tsx +3 -2
- package/skills/wix-headless-templates/blog/rest/authors.ts +5 -5
- package/skills/wix-headless-templates/blog/rest/comments.ts +4 -4
- package/skills/wix-headless-templates/blog/rest/post-likes.ts +3 -3
- package/skills/wix-headless-templates/blog/rest/posts.ts +4 -4
- package/skills/wix-headless-templates/blog/rest/taxonomy.ts +5 -5
- package/skills/wix-headless-templates/blog/seed/SEED.md +1 -1
- package/skills/wix-headless-templates/blog/seed/seed-blog.mjs +4 -3
- package/skills/wix-headless-templates/bookings/rest/booking.ts +4 -4
- package/skills/wix-headless-templates/bookings/rest/services.ts +4 -4
- package/skills/wix-headless-templates/bookings/seed/SEED.md +1 -1
- package/skills/wix-headless-templates/bookings/seed/seed-bookings.mjs +4 -2
- package/skills/wix-headless-templates/cms/rest/collections.ts +3 -3
- package/skills/wix-headless-templates/cms/rest/items.ts +4 -4
- package/skills/wix-headless-templates/cms/seed/seed-cms.mjs +2 -1
- package/skills/wix-headless-templates/donations/rest/campaigns.ts +4 -4
- package/skills/wix-headless-templates/donations/rest/donate.ts +3 -3
- package/skills/wix-headless-templates/donations/seed/SEED.md +1 -1
- package/skills/wix-headless-templates/donations/seed/seed-donations.mjs +5 -3
- package/skills/wix-headless-templates/events/rest/events.ts +4 -4
- package/skills/wix-headless-templates/events/rest/orders.ts +4 -4
- package/skills/wix-headless-templates/events/rest/registration.ts +4 -4
- package/skills/wix-headless-templates/events/seed/SEED.md +1 -1
- package/skills/wix-headless-templates/events/seed/seed-events.mjs +5 -3
- package/skills/wix-headless-templates/faq/rest/faq.ts +4 -4
- package/skills/wix-headless-templates/faq/seed/seed-faq.mjs +2 -1
- package/skills/wix-headless-templates/forms/rest/forms.ts +4 -4
- package/skills/wix-headless-templates/forms/rest/submissions.ts +4 -4
- package/skills/wix-headless-templates/forms/seed/seed-forms.mjs +2 -1
- package/skills/wix-headless-templates/members/rest/auth.ts +3 -3
- package/skills/wix-headless-templates/members/rest/members.ts +4 -4
- package/skills/wix-headless-templates/members/seed/seed-members.mjs +2 -1
- package/skills/wix-headless-templates/portfolio/rest/portfolio.ts +4 -4
- package/skills/wix-headless-templates/portfolio/seed/SEED.md +4 -2
- package/skills/wix-headless-templates/portfolio/seed/seed-portfolio.mjs +8 -7
- package/skills/wix-headless-templates/pricing-plans/rest/plan-access.ts +3 -3
- package/skills/wix-headless-templates/pricing-plans/rest/plans.ts +4 -4
- package/skills/wix-headless-templates/pricing-plans/rest/purchase.ts +3 -3
- package/skills/wix-headless-templates/pricing-plans/seed/seed-pricing-plans.mjs +2 -1
- package/skills/wix-headless-templates/rentals/rest/rentals.ts +4 -4
- package/skills/wix-headless-templates/rentals/seed/SEED.md +1 -1
- package/skills/wix-headless-templates/rentals/seed/seed-rentals.mjs +4 -2
- package/skills/wix-headless-templates/restaurants/rest/menu.ts +4 -4
- package/skills/wix-headless-templates/restaurants/rest/ordering.ts +5 -5
- package/skills/wix-headless-templates/restaurants/rest/reservations.ts +4 -4
- package/skills/wix-headless-templates/restaurants/seed/SEED.md +1 -1
- package/skills/wix-headless-templates/restaurants/seed/seed-restaurants.mjs +6 -4
- package/skills/wix-headless-templates/shared/capabilities/site-search/rest/search.ts +4 -4
- package/skills/wix-headless-templates/shared/capabilities/site-search/seed/install.mjs +6 -4
- package/skills/wix-headless-templates/shared/seed/images.mjs +41 -5
- package/skills/wix-headless-templates/shared/seed/main.mjs +15 -0
- package/skills/wix-headless-templates/shared/seed/wix-cli.mjs +43 -11
- package/skills/wix-headless-templates/storefront/INSTRUCTIONS.md +1 -1
- package/skills/wix-headless-templates/storefront/rest/cart.ts +5 -5
- package/skills/wix-headless-templates/storefront/rest/catalog.ts +5 -5
- package/skills/wix-headless-templates/storefront/seed/SEED.md +44 -14
- package/skills/wix-headless-templates/storefront/seed/seed-store.mjs +23 -15
- package/skills/wix-manage/SKILL.md +28 -18
- package/skills/wix-manage/references/app-installation/install-wix-apps.md +5 -1
- package/skills/wix-manage/references/app-installation/list-installed-apps.md +19 -9
- package/skills/wix-manage/references/automations/activation-status.md +52 -0
- package/skills/wix-manage/references/automations/api-catalog.md +81 -0
- package/skills/wix-manage/references/automations/build-and-manage-automations.md +34 -0
- package/skills/wix-manage/references/bookings/booking-service-policy-setup.md +3 -0
- package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +277 -363
- package/skills/wix-manage/references/bookings/external-calendar-integration.md +3 -0
- package/skills/wix-manage/references/bookings/multi-resource-service-creation.md +3 -0
- package/skills/wix-manage/references/cms/cms-data-items-crud.md +11 -2
- package/skills/wix-manage/references/cms/cms-publishing-flow.md +63 -16
- package/skills/wix-manage/references/custom-apps/use-app-skills-and-app-tools.md +3 -1
- package/skills/wix-manage/references/forms/update-form.md +3 -0
- package/skills/wix-manage/references/pricing-plans/create-and-update-pricing-plans.md +3 -0
- package/skills/wix-manage/references/restaurants/wix-restaurants-setup.md +3 -0
- package/skills/wix-manage/references/sites/create-headless-site.md +7 -50
- package/skills/wix-manage/references/sites/manage-oauth-apps.md +4 -4
- package/skills/wix-manage/references/sites/read-site-context.md +4 -0
- package/skills/wix-manage/references/sites/send-feedback-to-wix.md +92 -0
- package/skills/wix-manage/references/sites/upload-static-site.md +298 -283
- package/skills/wix-manage/references/stores/add-store-pages-to-site.md +3 -0
- package/skills/wix-manage/references/stores/change-store-currency.md +3 -0
- package/skills/wix-manage/references/stores/create-product-catalog-v3.md +3 -0
- package/skills/wix-manage/references/stores/find-products-query-and-search-catalog-v3.md +3 -0
- package/skills/wix-manage/references/stores/update-product-with-options.md +192 -22
- package/skills/wix-manage/references/bookings/create-appointment-service.md +0 -243
- package/skills/wix-manage/references/bookings/create-booking-service-from-prompt.md +0 -31
- package/skills/wix-manage/references/bookings/create-class-service.md +0 -222
- package/skills/wix-manage/references/bookings/create-course-service.md +0 -251
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
// Seeding is ADDITIVE — never deletes or overwrites existing forms.
|
|
28
28
|
import { seedSiteId } from "../../shared/seed/site-context.mjs";
|
|
29
29
|
import { wixToken } from "../../shared/seed/wix-cli.mjs";
|
|
30
|
+
import { isMain } from "../../shared/seed/main.mjs";
|
|
30
31
|
import { readFileSync } from "node:fs";
|
|
31
32
|
import { randomUUID } from "node:crypto";
|
|
32
33
|
|
|
@@ -556,7 +557,7 @@ export async function setupForms(ctx, plan) {
|
|
|
556
557
|
|
|
557
558
|
// ---- CLI entry -------------------------------------------------------------------------------
|
|
558
559
|
|
|
559
|
-
const invokedDirectly =
|
|
560
|
+
const invokedDirectly = isMain(import.meta.url);
|
|
560
561
|
if (invokedDirectly) {
|
|
561
562
|
const planPath = process.argv[2];
|
|
562
563
|
if (!planPath) {
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
// client id; nothing elevated. Porting: keep the paths and bodies; the exchange is three calls.
|
|
9
9
|
// docs: https://dev.wix.com/docs/go-headless/authentication/members/custom-login-page/build-a-custom-login-page-rest.md
|
|
10
10
|
// docs: https://dev.wix.com/docs/api-reference/business-management/headless/redirects/create-redirect-session.md
|
|
11
|
-
import { WIX_CLIENT_ID } from "
|
|
12
|
-
import { WixApiError, clearTokens, loadTokens, mintTokens, setTokens, wixRequest, type Tokens } from "
|
|
11
|
+
import { WIX_CLIENT_ID } from "../../shared/rest/config.js";
|
|
12
|
+
import { WixApiError, clearTokens, loadTokens, mintTokens, setTokens, wixRequest, type Tokens } from "../../shared/rest/client.js";
|
|
13
13
|
import {
|
|
14
14
|
NO_SESSION_ERROR,
|
|
15
15
|
authOutcome,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
type LoginResult,
|
|
21
21
|
type LoginState,
|
|
22
22
|
type RawAuth,
|
|
23
|
-
} from "
|
|
23
|
+
} from "../app/wix/members/auth-core.js";
|
|
24
24
|
|
|
25
25
|
export type { LoginResult, LoginState };
|
|
26
26
|
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
// whatever token ./client.js holds — a member's after a rest/auth.ts login → the profile; a
|
|
5
5
|
// visitor's → 403 → null. Porting: keep the path and the fieldsets, port the core once.
|
|
6
6
|
// docs: https://dev.wix.com/docs/api-reference/crm/members-contacts/members/member-management/members/get-my-member.md
|
|
7
|
-
import { wixRequest } from "
|
|
8
|
-
import { imgSrc } from "
|
|
9
|
-
import { MEMBER_FIELDSETS, memberId, toCurrentMember, type Raw } from "
|
|
10
|
-
import type { CurrentMember } from "
|
|
7
|
+
import { wixRequest } from "../../shared/rest/client.js";
|
|
8
|
+
import { imgSrc } from "../../shared/rest/media.js";
|
|
9
|
+
import { MEMBER_FIELDSETS, memberId, toCurrentMember, type Raw } from "../app/wix/members/members-core.js";
|
|
10
|
+
import type { CurrentMember } from "../app/wix/members/types.js";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* The current member, or null: for a visitor (the call is refused — "Missing site member id"), for
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
// Members Area app's App Market id; the identity/profile split is described beside it.
|
|
16
16
|
import { seedSiteId } from "../../shared/seed/site-context.mjs";
|
|
17
17
|
import { wixToken } from "../../shared/seed/wix-cli.mjs";
|
|
18
|
+
import { isMain } from "../../shared/seed/main.mjs";
|
|
18
19
|
import { readFileSync } from "node:fs";
|
|
19
20
|
|
|
20
21
|
const API = "https://www.wixapis.com";
|
|
@@ -77,7 +78,7 @@ export async function setupMembers(ctx, { installMembersArea = true } = {}) {
|
|
|
77
78
|
|
|
78
79
|
// ---- CLI entry ----------------------------------------------------------------------------------
|
|
79
80
|
|
|
80
|
-
const invokedDirectly =
|
|
81
|
+
const invokedDirectly = isMain(import.meta.url);
|
|
81
82
|
if (invokedDirectly) {
|
|
82
83
|
const planPath = process.argv[2];
|
|
83
84
|
const plan = planPath ? JSON.parse(readFileSync(planPath, "utf8")) : {};
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/portfolio/projects/list-projects.md
|
|
9
9
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/portfolio/projects/query-projects.md
|
|
10
10
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/portfolio/project-items/list-project-items.md
|
|
11
|
-
import { wixRequest } from "
|
|
12
|
-
import { imgSrc } from "
|
|
11
|
+
import { wixRequest } from "../../shared/rest/client.js";
|
|
12
|
+
import { imgSrc } from "../../shared/rest/media.js";
|
|
13
13
|
import {
|
|
14
14
|
galleryItems,
|
|
15
15
|
visibleCollection,
|
|
@@ -18,8 +18,8 @@ import {
|
|
|
18
18
|
visibleProjects,
|
|
19
19
|
type Raw,
|
|
20
20
|
type VideoSrc,
|
|
21
|
-
} from "
|
|
22
|
-
import type { CollectionSummary, GalleryItem, ProjectDetail, ProjectSummary } from "
|
|
21
|
+
} from "../app/wix/portfolio/portfolio-core.js";
|
|
22
|
+
import type { CollectionSummary, GalleryItem, ProjectDetail, ProjectSummary } from "../app/wix/portfolio/types.js";
|
|
23
23
|
|
|
24
24
|
// REST never returns the bare wix:video:// string — videoInfo arrives as { resolutions, posters },
|
|
25
25
|
// which the core reads directly. A string here has no resolver on this transport → dropped.
|
|
@@ -53,8 +53,10 @@ rows (Role, Year, Client…).
|
|
|
53
53
|
- Every image field's default is a prompt (`coverImagePrompt` / `items[].imagePrompt` —
|
|
54
54
|
AI-generated, ~1 Wix AI credit per image, account-billed): brand-contextual — subject,
|
|
55
55
|
aesthetic/mood, palette, lighting — always ending "no text, no watermarks". At least one image in the set shows the real subject of the business — the actual product/space/service, not abstract decoration. For an asset the user actually supplied
|
|
56
|
-
use a
|
|
57
|
-
|
|
56
|
+
use a Media Manager file (`coverImageMediaId` / `items[].imageMediaId` — a file already in the
|
|
57
|
+
site's Media Manager, such as one the user uploaded there: used as it is, no copy; a
|
|
58
|
+
`static.wixstatic.com/media/<file id>` URL of a site file counts as one), a path
|
|
59
|
+
(`coverImagePath` / `items[].imagePath` — a file on this machine, uploaded to Wix Media) or a url (`coverImageUrl` / `items[].imageUrl` — their own hosted URL; verify it
|
|
58
60
|
with `curl -sI` → 200) — never a stock-photo or guessed URL. All images —
|
|
59
61
|
covers and gallery items alike — resolve in one parallel wave and never block the seed; a
|
|
60
62
|
failed image skips just that item/cover. The **cover** is the
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
// project's gallery items, and imports+attaches cover images. Prints a JSON result to stdout.
|
|
10
10
|
//
|
|
11
11
|
// Plan shape (see SEED.md):
|
|
12
|
-
// { "collections": [{ "title", "description"?, "hidden"?, "coverImageUrl"? | "coverImagePrompt"? }],
|
|
12
|
+
// { "collections": [{ "title", "description"?, "hidden"?, "coverImageMediaId"? | "coverImageUrl"? | "coverImagePrompt"? }],
|
|
13
13
|
// "projects": [{ "title", "description"?, "hidden"?,
|
|
14
14
|
// "collection"? (title), // resolved to that collection's id
|
|
15
15
|
// "details"?: [{ "label", "text" }],
|
|
16
|
-
// "coverImageUrl"? | "coverImagePrompt"?,
|
|
17
|
-
// "items"?: [{ "sortOrder", "title"?, "imageUrl" | "imagePrompt" }] }] }
|
|
16
|
+
// "coverImageMediaId"? | "coverImageUrl"? | "coverImagePrompt"?,
|
|
17
|
+
// "items"?: [{ "sortOrder", "title"?, "imageMediaId" | "imageUrl" | "imagePrompt" }] }] }
|
|
18
18
|
//
|
|
19
19
|
// Seeding is ADDITIVE — never deletes or overwrites existing content. A fresh Portfolio
|
|
20
20
|
// install ships its own sample content ("My Portfolio" + sample projects); removing it is the
|
|
@@ -24,6 +24,7 @@ import { readFileSync } from "node:fs";
|
|
|
24
24
|
import { resolveItemImages } from "../../shared/seed/images.mjs";
|
|
25
25
|
import { seedSiteId } from "../../shared/seed/site-context.mjs";
|
|
26
26
|
import { wixToken } from "../../shared/seed/wix-cli.mjs";
|
|
27
|
+
import { isMain } from "../../shared/seed/main.mjs";
|
|
27
28
|
|
|
28
29
|
const API = "https://www.wixapis.com";
|
|
29
30
|
const PORTFOLIO_APP_ID = "d90652a2-f5a1-4c7c-84c4-d4cdcc41f130";
|
|
@@ -186,13 +187,13 @@ export async function setupPortfolio(ctx, { collections = [], projects = [] } =
|
|
|
186
187
|
projects.forEach((p, pi) => {
|
|
187
188
|
for (const it of p.items ?? []) {
|
|
188
189
|
galleryRefs.push({ pi, it, spec: specs.length });
|
|
189
|
-
specs.push({ path: it.imagePath, url: it.imageUrl, prompt: it.imagePrompt, displayName: `${it.title || "item"}.png` });
|
|
190
|
+
specs.push({ mediaId: it.imageMediaId, path: it.imagePath, url: it.imageUrl, prompt: it.imagePrompt, displayName: `${it.title || "item"}.png` });
|
|
190
191
|
}
|
|
191
192
|
});
|
|
192
193
|
const projCoverAt = specs.length;
|
|
193
|
-
projects.forEach((p) => specs.push({ path: p.coverImagePath, url: p.coverImageUrl, prompt: p.coverImagePrompt, displayName: `${p.title || "project"}-cover.png` }));
|
|
194
|
+
projects.forEach((p) => specs.push({ mediaId: p.coverImageMediaId, path: p.coverImagePath, url: p.coverImageUrl, prompt: p.coverImagePrompt, displayName: `${p.title || "project"}-cover.png` }));
|
|
194
195
|
const colCoverAt = specs.length;
|
|
195
|
-
collections.forEach((c) => specs.push({ path: c.coverImagePath, url: c.coverImageUrl, prompt: c.coverImagePrompt, displayName: `${c.title || "collection"}-cover.png` }));
|
|
196
|
+
collections.forEach((c) => specs.push({ mediaId: c.coverImageMediaId, path: c.coverImagePath, url: c.coverImageUrl, prompt: c.coverImagePrompt, displayName: `${c.title || "collection"}-cover.png` }));
|
|
196
197
|
const files = await resolveItemImages(ctx, specs);
|
|
197
198
|
const dims = { height: 1024, width: 1024 };
|
|
198
199
|
|
|
@@ -235,7 +236,7 @@ export async function setupPortfolio(ctx, { collections = [], projects = [] } =
|
|
|
235
236
|
|
|
236
237
|
// ---- CLI entry ----------------------------------------------------------------------------------
|
|
237
238
|
|
|
238
|
-
const invokedDirectly =
|
|
239
|
+
const invokedDirectly = isMain(import.meta.url);
|
|
239
240
|
if (invokedDirectly) {
|
|
240
241
|
const planPath = process.argv[2];
|
|
241
242
|
if (!planPath) {
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
// visitor, so callers check the session first (the store does) and never call for a visitor.
|
|
6
6
|
// Porting: keep the path and the query string; the rule is one line in the core.
|
|
7
7
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/pricing-plans/orders/member-list-orders.md
|
|
8
|
-
import { wixRequest } from "
|
|
9
|
-
import { hasPlanAccess, memberOrdersQuery, type MemberOrdersQuery } from "
|
|
10
|
-
import type { Raw } from "
|
|
8
|
+
import { wixRequest } from "../../shared/rest/client.js";
|
|
9
|
+
import { hasPlanAccess, memberOrdersQuery, type MemberOrdersQuery } from "../app/wix/pricing-plans/plan-access-core.js";
|
|
10
|
+
import type { Raw } from "../app/wix/pricing-plans/plans-core.js";
|
|
11
11
|
|
|
12
12
|
export type { MemberOrdersQuery };
|
|
13
13
|
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
// A plan without a numeric price is malformed and never returned (plans-core `planAmount`).
|
|
6
6
|
// Porting: keep the path, keep the body, port the core once.
|
|
7
7
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/pricing-plans/plans-v3/query-plans.md
|
|
8
|
-
import { wixRequest } from "
|
|
9
|
-
import { imgSrc } from "
|
|
10
|
-
import { plansQuery, toDetail, toSummaries, type FormatOptions, type Raw } from "
|
|
11
|
-
import type { PlanDetail, PlanSummary } from "
|
|
8
|
+
import { wixRequest } from "../../shared/rest/client.js";
|
|
9
|
+
import { imgSrc } from "../../shared/rest/media.js";
|
|
10
|
+
import { plansQuery, toDetail, toSummaries, type FormatOptions, type Raw } from "../app/wix/pricing-plans/plans-core.js";
|
|
11
|
+
import type { PlanDetail, PlanSummary } from "../app/wix/pricing-plans/types.js";
|
|
12
12
|
|
|
13
13
|
export type { FormatOptions };
|
|
14
14
|
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
// order form, and payment itself, so a visitor token starts it. Never create the order yourself,
|
|
5
5
|
// never hand-build a checkout URL.
|
|
6
6
|
// docs: https://dev.wix.com/docs/api-reference/business-management/headless/redirects/create-redirect-session.md
|
|
7
|
-
import { wixRequest } from "
|
|
8
|
-
import { purchaseBody, redirectUrl, type PurchaseOptions } from "
|
|
9
|
-
import type { Raw } from "
|
|
7
|
+
import { wixRequest } from "../../shared/rest/client.js";
|
|
8
|
+
import { purchaseBody, redirectUrl, type PurchaseOptions } from "../app/wix/pricing-plans/purchase-core.js";
|
|
9
|
+
import type { Raw } from "../app/wix/pricing-plans/plans-core.js";
|
|
10
10
|
|
|
11
11
|
export type { PurchaseOptions };
|
|
12
12
|
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
import { setSiteCurrency } from "../../shared/seed/site.mjs";
|
|
24
24
|
import { seedSiteId } from "../../shared/seed/site-context.mjs";
|
|
25
25
|
import { wixToken } from "../../shared/seed/wix-cli.mjs";
|
|
26
|
+
import { isMain } from "../../shared/seed/main.mjs";
|
|
26
27
|
import { readFileSync } from "node:fs";
|
|
27
28
|
import { randomUUID } from "node:crypto";
|
|
28
29
|
|
|
@@ -283,7 +284,7 @@ export async function setupPricingPlans(ctx, { plans = [], currency } = {}) {
|
|
|
283
284
|
|
|
284
285
|
// ---- CLI entry ----------------------------------------------------------------------------------
|
|
285
286
|
|
|
286
|
-
const invokedDirectly =
|
|
287
|
+
const invokedDirectly = isMain(import.meta.url);
|
|
287
288
|
if (invokedDirectly) {
|
|
288
289
|
const planPath = process.argv[2];
|
|
289
290
|
if (!planPath) {
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings/bookings-writer-v2/create-booking.md
|
|
11
11
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart-v2/create-cart.md
|
|
12
12
|
// docs: https://dev.wix.com/docs/api-reference/business-management/headless/redirects/create-redirect-session.md
|
|
13
|
-
import { wixRequest } from "
|
|
14
|
-
import { imgSrc } from "
|
|
13
|
+
import { wixRequest } from "../../shared/rest/client.js";
|
|
14
|
+
import { imgSrc } from "../../shared/rest/media.js";
|
|
15
15
|
import {
|
|
16
16
|
FALLBACK_FIELDS,
|
|
17
17
|
RENTALS_APP_ID,
|
|
@@ -42,8 +42,8 @@ import {
|
|
|
42
42
|
toSummary,
|
|
43
43
|
type Raw,
|
|
44
44
|
type StartWindow,
|
|
45
|
-
} from "
|
|
46
|
-
import type { EndOption, RentalDetail, RentalFormField, RentalQuote, RentalResult, RentalSummary, StartOption } from "
|
|
45
|
+
} from "../app/wix/rentals/rentals-core.js";
|
|
46
|
+
import type { EndOption, RentalDetail, RentalFormField, RentalQuote, RentalResult, RentalSummary, StartOption } from "../app/wix/rentals/types.js";
|
|
47
47
|
|
|
48
48
|
export { RENTALS_APP_ID, RENTALS_FORM_ID, RENTALS_PAGE_SIZE, dailyEndOptions, defaultTimeZone };
|
|
49
49
|
|
|
@@ -19,7 +19,7 @@ exercise the UI: one hourly and one daily rental when the business allows it, a
|
|
|
19
19
|
approval-gated one where it fits, and an image per rental — the default is an `imagePrompt`
|
|
20
20
|
(AI-generated, ~1 Wix AI credit per image, account-billed): brand-contextual — subject,
|
|
21
21
|
aesthetic/mood, palette, lighting — always ending "no text, no watermarks". At least one image shows
|
|
22
|
-
the real thing being rented. For an asset the user actually supplied use `imagePath` (a file on this
|
|
22
|
+
the real thing being rented. For an asset the user actually supplied use `imageMediaId` (a file already in the site's Media Manager, such as one the user uploaded there: used as it is, no copy; a `static.wixstatic.com/media/<file id>` URL of a site file counts as one), `imagePath` (a file on this
|
|
23
23
|
machine — uploaded to Wix Media) or `imageUrl` (their own hosted URL; verify it with `curl -sI` →
|
|
24
24
|
200) — never a stock-photo or guessed URL. Images resolve in parallel and never block the seed.
|
|
25
25
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// "resourceTypes": [{ "name": "Kayaks", "resources": ["Kayak 1", "Kayak 2"] }],
|
|
18
18
|
// "rentals": [{ "name", "description", "tagLine"?, "unit": "HOUR"|"DAY", "rate": 25, "min"?, "max"?,
|
|
19
19
|
// "resourceType": "Kayaks", "resources"?: ["Kayak 1"], "free"?, "requireManualApproval"?,
|
|
20
|
-
// "imageUrl"? | "imagePath"? | "imagePrompt"? }] }
|
|
20
|
+
// "imageMediaId"? | "imageUrl"? | "imagePath"? | "imagePrompt"? }] }
|
|
21
21
|
//
|
|
22
22
|
// Seeding is ADDITIVE — never deletes or overwrites existing content. Unexpected shapes →
|
|
23
23
|
// read the live API reference; every call below carries a docs: line with its reference page.
|
|
@@ -26,6 +26,7 @@ import { readFileSync } from "node:fs";
|
|
|
26
26
|
import { resolveItemImages } from "../../shared/seed/images.mjs";
|
|
27
27
|
import { seedSiteId } from "../../shared/seed/site-context.mjs";
|
|
28
28
|
import { wixToken } from "../../shared/seed/wix-cli.mjs";
|
|
29
|
+
import { isMain } from "../../shared/seed/main.mjs";
|
|
29
30
|
|
|
30
31
|
const API = "https://www.wixapis.com";
|
|
31
32
|
/** The Wix Rentals app: the service's immutable appId, the catalog filter, the cart's appId. */
|
|
@@ -268,6 +269,7 @@ export async function setupRentals(ctx, { resourceTypes = [], rentals = [], curr
|
|
|
268
269
|
// Pass 2 — images: resolve (import by url / generate by prompt) in one parallel wave, then attach.
|
|
269
270
|
// Failures leave the rental text-only; the seed's exit never depends on images.
|
|
270
271
|
const files = await resolveItemImages(ctx, created.map((c, i) => ({
|
|
272
|
+
mediaId: rentals[i]?.imageMediaId,
|
|
271
273
|
url: rentals[i]?.imageUrl,
|
|
272
274
|
path: rentals[i]?.imagePath,
|
|
273
275
|
prompt: rentals[i]?.imagePrompt,
|
|
@@ -300,7 +302,7 @@ export async function setupRentals(ctx, { resourceTypes = [], rentals = [], curr
|
|
|
300
302
|
|
|
301
303
|
// ---- CLI entry ----------------------------------------------------------------------------------
|
|
302
304
|
|
|
303
|
-
const invokedDirectly =
|
|
305
|
+
const invokedDirectly = isMain(import.meta.url);
|
|
304
306
|
if (invokedDirectly) {
|
|
305
307
|
const planPath = process.argv[2];
|
|
306
308
|
if (!planPath) {
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/menus/list-menus.md
|
|
8
8
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/menus/items/items/list-items.md
|
|
9
9
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/ecommerce-settings/get-ecommerce-settings.md
|
|
10
|
-
import { wixRequest } from "
|
|
11
|
-
import { imgSrc } from "
|
|
10
|
+
import { wixRequest } from "../../shared/rest/client.js";
|
|
11
|
+
import { imgSrc } from "../../shared/rest/media.js";
|
|
12
12
|
import {
|
|
13
13
|
anyLabels,
|
|
14
14
|
assembleMenus,
|
|
@@ -19,8 +19,8 @@ import {
|
|
|
19
19
|
nextCursor,
|
|
20
20
|
toSiteMoney,
|
|
21
21
|
type Raw,
|
|
22
|
-
} from "
|
|
23
|
-
import type { MenuData, SiteMoney } from "
|
|
22
|
+
} from "../app/wix/restaurants/menu-core.js";
|
|
23
|
+
import type { MenuData, SiteMoney } from "../app/wix/restaurants/types.js";
|
|
24
24
|
|
|
25
25
|
const get = (path: string, query?: Record<string, string | readonly string[]>) => wixRequest<Raw>(path, { method: "GET", query });
|
|
26
26
|
|
|
@@ -12,10 +12,10 @@
|
|
|
12
12
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/online-orders/fulfillment-methods/list-fulfillment-methods.md
|
|
13
13
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/purchase-flow/cart-v2/add-line-items-to-current-cart.md
|
|
14
14
|
// docs: https://dev.wix.com/docs/api-reference/business-management/headless/redirects/create-redirect-session.md
|
|
15
|
-
import { WixApiError, wixRequest } from "
|
|
16
|
-
import { imgSrc } from "
|
|
15
|
+
import { WixApiError, wixRequest } from "../../shared/rest/client.js";
|
|
16
|
+
import { imgSrc } from "../../shared/rest/media.js";
|
|
17
17
|
import { fetchSiteMoney } from "./menu.js";
|
|
18
|
-
import { initialSelection, nextCursor, validateSelection, type Raw } from "
|
|
18
|
+
import { initialSelection, nextCursor, validateSelection, type Raw } from "../app/wix/restaurants/menu-core.js";
|
|
19
19
|
import {
|
|
20
20
|
RESTAURANTS_ORDERS_APP_ID,
|
|
21
21
|
assertOrderAdded,
|
|
@@ -29,8 +29,8 @@ import {
|
|
|
29
29
|
toFulfillmentMethods,
|
|
30
30
|
toMenuOrderingMap,
|
|
31
31
|
toOrderCart,
|
|
32
|
-
} from "
|
|
33
|
-
import type { FulfillmentMethodInfo, MenuItem, MenuOrderingInfo, OrderCart, OrderSelection, OrderingStatus } from "
|
|
32
|
+
} from "../app/wix/restaurants/ordering-core.js";
|
|
33
|
+
import type { FulfillmentMethodInfo, MenuItem, MenuOrderingInfo, OrderCart, OrderSelection, OrderingStatus } from "../app/wix/restaurants/types.js";
|
|
34
34
|
|
|
35
35
|
export { RESTAURANTS_ORDERS_APP_ID };
|
|
36
36
|
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/reservations/create-held-reservation.md
|
|
10
10
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/reservations/reserve-reservation.md
|
|
11
11
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/reservations/create-reservation.md
|
|
12
|
-
import { wixRequest } from "
|
|
13
|
-
import { availableSlots, reservationBody, reserveeBody, toConfirmation, toHold, toLocations } from "
|
|
14
|
-
import type { Raw } from "
|
|
12
|
+
import { wixRequest } from "../../shared/rest/client.js";
|
|
13
|
+
import { availableSlots, reservationBody, reserveeBody, toConfirmation, toHold, toLocations } from "../app/wix/restaurants/reservations-core.js";
|
|
14
|
+
import type { Raw } from "../app/wix/restaurants/menu-core.js";
|
|
15
15
|
import type {
|
|
16
16
|
ReservationConfirmation,
|
|
17
17
|
ReservationFormConfig,
|
|
@@ -19,7 +19,7 @@ import type {
|
|
|
19
19
|
ReservationLocationInfo,
|
|
20
20
|
ReservationReservee,
|
|
21
21
|
ReservationSlot,
|
|
22
|
-
} from "
|
|
22
|
+
} from "../app/wix/restaurants/types.js";
|
|
23
23
|
|
|
24
24
|
const RESERVATIONS = "/table-reservations/reservations/v1";
|
|
25
25
|
|
|
@@ -16,7 +16,7 @@ of 2–3 items each** (the seed shows the shape; the owner adds the rest in the
|
|
|
16
16
|
every item with an image (a menu without photos looks broken) — the default is an
|
|
17
17
|
`imagePrompt` (AI-generated, ~1 Wix AI credit per image, account-billed): brand-contextual —
|
|
18
18
|
subject, aesthetic/mood, palette, lighting — always ending "no text, no watermarks". At least one image in the set shows the real subject of the business — the actual product/space/service, not abstract decoration. For an asset the user actually
|
|
19
|
-
supplied use `imagePath` (a file on this machine — uploaded to Wix Media) or `imageUrl`
|
|
19
|
+
supplied use `imageMediaId` (a file already in the site's Media Manager, such as one the user uploaded there: used as it is, no copy; a `static.wixstatic.com/media/<file id>` URL of a site file counts as one), `imagePath` (a file on this machine — uploaded to Wix Media) or `imageUrl`
|
|
20
20
|
(their own hosted URL; verify it with `curl -sI` → 200) — never a stock-photo or guessed URL. Images resolve in parallel and never
|
|
21
21
|
block the seed, a failed image leaves that item text-only — and both add-ons on for a
|
|
22
22
|
restaurant that takes orders and reservations. Give at least one dish a choice (a `variants`
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// "items": [{ "name", "description"?, "price"? | "variants"?: [{ "name", "price" }],
|
|
15
15
|
// "modifierGroups"?: [{ "name", "required"?, "min"?, "max"?,
|
|
16
16
|
// "modifiers": [{ "name", "price"?, "preSelected"?, "inStock"? }] }],
|
|
17
|
-
// "acceptSpecialRequests"?, "imageUrl"? | "imagePath"? | "imagePrompt"? }] }] }],
|
|
17
|
+
// "acceptSpecialRequests"?, "imageMediaId"? | "imageUrl"? | "imagePath"? | "imagePrompt"? }] }] }],
|
|
18
18
|
// "ordering"?: true | { "address"? }, // menu-first add-on; address is STEP 0
|
|
19
19
|
// "reservations"?: true | { "partySize"? { "min","max" }, "address"? } }
|
|
20
20
|
//
|
|
@@ -28,6 +28,7 @@ import { readFileSync } from "node:fs";
|
|
|
28
28
|
import { resolveItemImages } from "../../shared/seed/images.mjs";
|
|
29
29
|
import { seedSiteId } from "../../shared/seed/site-context.mjs";
|
|
30
30
|
import { wixToken } from "../../shared/seed/wix-cli.mjs";
|
|
31
|
+
import { isMain } from "../../shared/seed/main.mjs";
|
|
31
32
|
|
|
32
33
|
const API = "https://www.wixapis.com";
|
|
33
34
|
const MENUS_APP_ID = "b278a256-2757-4f19-9313-c05c783bec92";
|
|
@@ -459,12 +460,13 @@ export async function setupRestaurants(ctx, plan) {
|
|
|
459
460
|
const flat = m.sections.flatMap((s) => s.items || []);
|
|
460
461
|
flat.forEach((it, i) => {
|
|
461
462
|
const created = createdMenus[mi]?.items?.[i];
|
|
462
|
-
if ((it.imageUrl || it.imagePath || it.imagePrompt) && created?.id) {
|
|
463
|
-
imageItems.push({ ...created, imageUrl: it.imageUrl, imagePath: it.imagePath, imagePrompt: it.imagePrompt, name: it.name });
|
|
463
|
+
if ((it.imageMediaId || it.imageUrl || it.imagePath || it.imagePrompt) && created?.id) {
|
|
464
|
+
imageItems.push({ ...created, imageMediaId: it.imageMediaId, imageUrl: it.imageUrl, imagePath: it.imagePath, imagePrompt: it.imagePrompt, name: it.name });
|
|
464
465
|
}
|
|
465
466
|
});
|
|
466
467
|
});
|
|
467
468
|
const files = await resolveItemImages(ctx, imageItems.map((it) => ({
|
|
469
|
+
mediaId: it.imageMediaId,
|
|
468
470
|
url: it.imageUrl,
|
|
469
471
|
path: it.imagePath,
|
|
470
472
|
prompt: it.imagePrompt,
|
|
@@ -559,7 +561,7 @@ export async function setupRestaurants(ctx, plan) {
|
|
|
559
561
|
|
|
560
562
|
// ---- CLI entry ----------------------------------------------------------------------------------
|
|
561
563
|
|
|
562
|
-
const invokedDirectly =
|
|
564
|
+
const invokedDirectly = isMain(import.meta.url);
|
|
563
565
|
if (invokedDirectly) {
|
|
564
566
|
const planPath = process.argv[2];
|
|
565
567
|
if (!planPath) {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
// a literal body per function. Every call here answers a visitor token (observed live). Porting:
|
|
5
5
|
// keep the paths, keep the bodies, port the core once.
|
|
6
6
|
// docs: https://dev.wix.com/docs/api-reference/business-management/site-search/wix-site-search/search.md
|
|
7
|
-
import { wixRequest } from "
|
|
8
|
-
import { imgSrc } from "
|
|
7
|
+
import { wixRequest } from "../../../rest/client.js";
|
|
8
|
+
import { imgSrc } from "../../../rest/media.js";
|
|
9
9
|
import { siteSearchPlan } from "./site-search-config.generated.js";
|
|
10
10
|
import {
|
|
11
11
|
GROUP_LIMIT,
|
|
@@ -23,8 +23,8 @@ import {
|
|
|
23
23
|
toSearchAll,
|
|
24
24
|
toSuggestGroups,
|
|
25
25
|
type Raw,
|
|
26
|
-
} from "
|
|
27
|
-
import type { SearchAllResult, SearchOptions, SearchPage, SiteSearchConfig, SuggestGroup } from "
|
|
26
|
+
} from "../app/wix/site-search/search-core.js";
|
|
27
|
+
import type { SearchAllResult, SearchOptions, SearchPage, SiteSearchConfig, SuggestGroup } from "../app/wix/site-search/types.js";
|
|
28
28
|
|
|
29
29
|
/** The plan's siteSearch entry resolved — types in order, routes, labels. */
|
|
30
30
|
export const config: SiteSearchConfig = resolveConfig(siteSearchPlan);
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
// (searchAll("<a seeded title word>")) and expect a non-empty group before wiring the UI. Zero
|
|
12
12
|
// documents after a minute means the app is not installed or the content is not visible to the
|
|
13
13
|
// index (hidden products, drafts), not that the query is wrong.
|
|
14
|
-
import { execFileSync } from "node:child_process";
|
|
15
14
|
import { seedSiteId } from "../../../seed/site-context.mjs";
|
|
16
15
|
import { existsSync, readFileSync } from "node:fs";
|
|
17
16
|
import { installSiteSearch } from "../../../seed/site.mjs";
|
|
17
|
+
import { wixToken } from "../../../seed/wix-cli.mjs";
|
|
18
18
|
|
|
19
19
|
function flag(name) {
|
|
20
20
|
const i = process.argv.indexOf(`--${name}`);
|
|
@@ -29,9 +29,11 @@ if (!siteId) {
|
|
|
29
29
|
console.log(JSON.stringify({ error: "no site: pass --site <siteId> or run in a folder with wix.config.json" }));
|
|
30
30
|
process.exit(1);
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
let token;
|
|
33
|
+
try {
|
|
34
|
+
token = wixToken(siteId);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.log(JSON.stringify({ error: String(e.message).slice(0, 400) }));
|
|
35
37
|
process.exit(1);
|
|
36
38
|
}
|
|
37
39
|
try {
|
|
@@ -124,6 +124,37 @@ async function findExisting(ctx, displayName) {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
/**
|
|
128
|
+
* The Media Manager file a spec names, when it names one: `mediaId`, or the file id at the end of a
|
|
129
|
+
* `static.wixstatic.com/media/<fileId>` URL. Returns { id, url, existing: true } for a file on this
|
|
130
|
+
* site, waiting out a short PENDING (an upload still processing), or null when the URL's file is
|
|
131
|
+
* not on this site (it is then imported like any external URL). An explicit `mediaId` that is not
|
|
132
|
+
* on the site throws, so the product is reported text-only with the reason.
|
|
133
|
+
*/
|
|
134
|
+
// docs: https://dev.wix.com/docs/api-reference/assets/media/media-manager/files/get-file-descriptor.md
|
|
135
|
+
const WIX_MEDIA_URL = /^https?:\/\/static\.wixstatic\.com\/media\/([^/?#]+)/;
|
|
136
|
+
async function existingMediaFile(ctx, s) {
|
|
137
|
+
const id = s.mediaId ?? s.url?.match(WIX_MEDIA_URL)?.[1];
|
|
138
|
+
if (!id) return null;
|
|
139
|
+
for (let attempt = 0; attempt < 10; attempt++) {
|
|
140
|
+
const res = await fetch(`${API}/site-media/v1/files/get-file-by-id?fileId=${encodeURIComponent(id)}`, {
|
|
141
|
+
headers: { Authorization: `Bearer ${ctx.token}`, "wix-site-id": ctx.siteId },
|
|
142
|
+
signal: AbortSignal.timeout(20_000),
|
|
143
|
+
});
|
|
144
|
+
if (res.status === 404) {
|
|
145
|
+
if (s.mediaId) throw new Error(`no Media Manager file ${id} on this site`);
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const json = await res.json().catch(() => ({}));
|
|
149
|
+
if (!res.ok) throw new Error(`GET get-file-by-id -> ${res.status}: ${JSON.stringify(json).slice(0, 200)}`);
|
|
150
|
+
const f = json.file;
|
|
151
|
+
if (f?.operationStatus === "FAILED") throw new Error(`Media Manager file ${id} failed processing`);
|
|
152
|
+
if (f?.url && f.operationStatus !== "PENDING") return { id: f.id, url: f.url, existing: true };
|
|
153
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
154
|
+
}
|
|
155
|
+
throw new Error(`Media Manager file ${id} still processing`);
|
|
156
|
+
}
|
|
157
|
+
|
|
127
158
|
/** Short stable fingerprint of an image's source (url, prompt + size, or file bytes). */
|
|
128
159
|
function sourceHash(s) {
|
|
129
160
|
const h = createHash("sha1");
|
|
@@ -149,8 +180,10 @@ export async function importImage(ctx, url, displayName = "image.png") {
|
|
|
149
180
|
|
|
150
181
|
/**
|
|
151
182
|
* THE seed entry point. Resolves a batch of image specs to Wix Media files in ONE parallel
|
|
152
|
-
* wave. Each spec: {
|
|
153
|
-
*
|
|
183
|
+
* wave. Each spec: { mediaId } (a file already in this site's Media Manager — used as it is,
|
|
184
|
+
* as is a static.wixstatic.com/media URL of one) OR { path } (LOCAL file — the user's own asset,
|
|
185
|
+
* uploaded) OR { url } (verified external URL — imported) OR { prompt } (generated, ~1 credit) —
|
|
186
|
+
* plus optional
|
|
154
187
|
* displayName, width, height. Returns an array aligned with the input: { file, error } per
|
|
155
188
|
* spec — `file` is { id, url } on success and null otherwise; `error` is the reason it is null
|
|
156
189
|
* (the thrown message, "empty spec", or "timed out"). Never throws. Every failure is also
|
|
@@ -166,8 +199,11 @@ export async function resolveItemImagesDetailed(ctx, specs, { perImageBudgetMs =
|
|
|
166
199
|
});
|
|
167
200
|
const results = await Promise.allSettled(
|
|
168
201
|
(specs ?? []).map(async (s) => {
|
|
169
|
-
if (!s || (!s.path && !s.url && !s.prompt)) return null;
|
|
202
|
+
if (!s || (!s.path && !s.url && !s.prompt && !s.mediaId)) return null;
|
|
170
203
|
const resolve = (async () => {
|
|
204
|
+
// A file already in this site's Media Manager is used as it is: no import, no copy.
|
|
205
|
+
const inMedia = await existingMediaFile(ctx, s);
|
|
206
|
+
if (inMedia) return inMedia;
|
|
171
207
|
const name = stableName(s);
|
|
172
208
|
// uploadImage swaps in the file's own extension; look for what it will actually be named
|
|
173
209
|
const uploadedName = s.path ? name.replace(/\.[a-z0-9]+$/i, "") + extname(s.path).toLowerCase() : name;
|
|
@@ -184,10 +220,10 @@ export async function resolveItemImagesDetailed(ctx, specs, { perImageBudgetMs =
|
|
|
184
220
|
);
|
|
185
221
|
return results.map((r, i) => {
|
|
186
222
|
const spec = specs?.[i] ?? {};
|
|
187
|
-
const label = spec.displayName ?? spec.path ?? spec.url ?? (spec.prompt ? "(prompt)" : "(empty)");
|
|
223
|
+
const label = spec.displayName ?? spec.mediaId ?? spec.path ?? spec.url ?? (spec.prompt ? "(prompt)" : "(empty)");
|
|
188
224
|
if (r.status === "fulfilled") {
|
|
189
225
|
if (r.value) return { file: r.value, error: null };
|
|
190
|
-
const error = !spec.path && !spec.url && !spec.prompt ? "empty spec" : `timed out after ${perImageBudgetMs} ms`;
|
|
226
|
+
const error = !spec.path && !spec.url && !spec.prompt && !spec.mediaId ? "empty spec" : `timed out after ${perImageBudgetMs} ms`;
|
|
191
227
|
if (error !== "empty spec") console.error(`image ${label}: ${error}`);
|
|
192
228
|
return { file: null, error };
|
|
193
229
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Whether a seed module is the script node was started with (`node seed-<vertical>.mjs plan.json`)
|
|
2
|
+
// rather than imported. Compares real paths, so Windows backslashes, relative invocations and
|
|
3
|
+
// symlinked folders all match (node reports the module's real path in import.meta.url).
|
|
4
|
+
import { realpathSync } from "node:fs";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
export function isMain(moduleUrl) {
|
|
9
|
+
if (!process.argv[1]) return false;
|
|
10
|
+
try {
|
|
11
|
+
return realpathSync(resolve(process.argv[1])) === realpathSync(fileURLToPath(moduleUrl));
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|