wix-base44-connector

Par wix · skills

Construire sur Wix depuis le bac à sable du builder Base44 — ce fichier est le guide complet (contexte du site, découverte de la documentation, contrats d'API, qui appelle Wix depuis quel endroit), et scripts/utils.js est son module utilitaire, chargé depuis le disque à chaque exécution. Déclencheurs : construire une application Base44 sur un site Wix connecté, rechercher une API Wix depuis le bac à sable, décider entre frontend et backend pour un appel Wix.

npx skills add https://github.com/wix/skills --skill wix-base44-connector

Construire sur Wix à partir de Base44

Le connecteur Wix est connecté ; c'est ainsi que l'app se construit dessus — rassembler le contexte du site, trouver les APIs et apprendre leurs contrats depuis la doc, écrire le code. Découvrir tout : endpoints, chemins, URLs de doc, champs de requête et de réponse proviennent tous des appels ci-dessous, jamais de la mémoire ou du pattern. 404 ou vide ⇒ découvrir, pas permuter. Les exemples enseignent les mécaniques et deviennent obsolètes — vérifier avant de s'y fier.

Qu'est-ce que tu construis ?

L'audience de l'app choisit le token, et le token choisit l'architecture : le visitor token est public — n'importe qui peut le créer à partir du clientId du site — et l'admin token est un secret, celui du connecteur, côté serveur seulement.

browser            ──(visitor token)─► wixapis.com   lectures & actions du visiteur
base44/functions/* ──(admin token)───► wixapis.com   travail nécessitant l'identité du propriétaire
exec_tool          ──(admin token)───► wixapis.com   toi : sondage/gestion ad hoc pendant la construction

Un site pour les visiteurs — storefront, blog, réservation. Headless signifie que le site Wix n'a pas de pages propres : ton app EST son frontend. L'expérience visiteur complète — chaque page, chaque lecture, chaque action que le visiteur fait — est des appels navigateur sur le visitor token ; aucun d'eux n'a besoin d'une fonction backend. Les lectures publiques incluses (le visitor token interroge le catalogue directement), et carts/current/* + checkout agissent sur le panier de L'APPELANT, donc seul le visitor token accède au panier du visiteur. Un fichier porte tout : src/lib/wixClient.js (Écris le code, ci-dessous). base44/functions/* n'apparaissent que là où le travail nécessite l'identité du propriétaire — opérations à permissions élevées qu'un visiteur déclenche, webhooks, tâches planifiées — et pour le backend non-Wix de l'app.

Un outil admin pour le propriétaire — tableau de bord, back office. Les pages agissent comme le propriétaire, dont le token est un secret : pages → base44/functions/* ──(admin token)──► wixapis.com.

Les helpers

La recherche et le sondage s'exécutent dans exec_tool, et un loader ouvre chaque exec (les execs ne partagent pas d'état — recharger chaque round ; le module réside sur disque à côté de ce fichier, réseau uniquement comme fallback au premier accès) :

const fs = require("fs"), P = ".agents/skills/wix-base44-connector/utils.js";
if (!fs.existsSync(P)) { fs.mkdirSync(".agents/skills/wix-base44-connector", { recursive: true });
  fs.writeFileSync(P, await (await fetch("https://www.wix.com/skills/wix-base44-connector/scripts/utils.js")).text()); }
const wx = (() => { const m = { exports: {} };
  new Function("module", "exports", "require", fs.readFileSync(P, "utf8"))(m, m.exports, require); return m.exports; })();

Les résultats ≤ 4 000 caractères reviennent en ligne (les résultats exec sont limités à ~5 000) ; tout ce qui est plus grand est sauvegardé sous .agents/skills/wix-base44-connector/scratch/ et retourne { path, bytes, lines, outline } — l'outline est la carte. Lis un fichier sauvegardé comme tu sais déjà le faire : wx.bash("grep -n 'terme' <path> | head -40") pour trouver (GNU grep/sed, awk est mawk, pas rg ; sur tout ce qui est sauvegardé : grep -rn 'terme' .agents/skills/wix-base44-connector/scratch/), et read_file pour citer — une fenêtre via offset/limit aux lignes que grep a nommées, ou le fichier entier quand il tient dans la limite de 45K de read_file. Les réponses API sont des données de site et ne finissent jamais dans scratch — projette-les en faits. Récupère chaque URL à l'intérieur d'un exec avec fetch() — les outils website/browser sont limités à 10 000 caractères silencieusement. Un exec par round ; timeout 10s, jusqu'à 120 via {timeout}.

Rassembler le contexte — le rapport de contexte dynamique

const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
return await wx.context(accessToken, "Apps");   // pas d'arg section → l'outline du rapport

Un rapport : apps installées avec ids (incl. version du catalogue des Stores — V1 vs V3 décide ses endpoints), l'id de l'app OAuth (aussi le clientId du visiteur), locale, devise, collections CMS. Un rapport vide = mauvais token, jamais un site vide.

Apprendre Wix — trouver les APIs, apprendre leurs contrats

// connaître le produit ? browse est déterministe — menuUrl oriente seul (enfants + compte) ;
// filtrer avant de lister les méthodes, les listings non filtrés sont limités
await wx.browse("https://dev.wix.com/docs/api-reference/business-solutions/bookings/bookings",
                { include: ["METHOD"], filter: "resched", depth: 4 });

// ne pas savoir où ça vit ? search classe, ne dit jamais "pas de match" — éliminer les hits du mauvais produit
await wx.search("pause a pricing plan subscription and resume it");
// → { hits: [{ method, endpoint /* callable */, docsUrl, gist }] } — les hits sont souvent LA réponse

Approfondis pour les champs, les énums ou l'absence — seul l'index spec prouve l'absence.

Lire une page de doc

Les pages Method font 100 KB+, les halves REST et SDK jumelles répètent les noms de champs à des types différents — page récupère, sauvegarde et mappe en un round ; cite seulement ta moitié :

const pg = await wx.page(docsUrl);   // texte entier quand petit ; sinon { path, bytes, lines, outline }
await wx.bash(`grep -in 'refund' ${pg.path} | head -40`);   // grep -n 'terme|^#' garde les sections visibles
// cite avec read_file(pg.path) + offset/limit aux lignes que grep a nommées — l'exemple REST EN PREMIER :
// sous ### Examples ci-dessous ## REST API se trouve une requête complète de travail
await wx.bash(`sed -n '53,949p' ${pg.path} | grep -oE '"[a-zA-Z]+":' | sort -u | head -60`);
//  ↑ un énorme exemple clôturé → son vocabulaire de champs en un round ; le lire entièrement seulement si tu dois

search sauvegarde aussi son contenu brut à côté des hits en ligne — grep son path quand les six lignes d'un hit n'étaient pas assez.

L'index spec

Répond aux questions sur les pages que tu as déjà trouvées — arrive avec un docsUrl, apparie par lui. Aussi la seule preuve qu'une API N'EXISTE PAS :

await wx.spec(`
  const r = lightIndex.find(x => x.docsUrl === "<docsUrl from browse/search>");
  return r.methods.map(m => ({ op: m.operationId.split(".").pop(), verb: m.httpMethod,
                               call: m.publicUrl }));   // publicUrl est callable — path est PARTIEL
`);
// champs de requête le round suivant : getResourceSchemaByUrl(docsUrl) →
//   m.requestBody.content["application/json"].schema.properties — noms et types, creuse plus profond
//   par round ; $circular stubs résolvent via s.components.schemas["<nom>"]

Recettes de gestion — vérifier avant de composer les flux admin

~100 recettes multi-étapes curées (installer apps, seeder les catalogues, configurer des verticals entiers) :

await wx.recipes();           // catégories avec comptes
await wx.recipes("stores");   // liste d'une catégorie — ou n'importe quel mot tâche : wx.recipes("coupon")
await wx.page(url);           // lire la recette choisie — entière quand petite, sauvegardée + outline quand
                              // grande ; ensuite grep / read_file windows aux lignes de l'outline

Une recette correspondante vaut mieux que composer le flux à partir de endpoints isolés : elle porte l'ordre et les pièges multi-étapes qu'aucune page method ne mentionne.

Écris le code

Les formes de réponse obéissent à la règle de découverte dans chaque lane : code contre les champs que tu as vus dans une réponse en direct ou la schema — les noms mémorisés proviennent souvent de versions plus anciennes. Sonde d'abord une vraie ligne.

Appels admin — exec ad hoc, fonctions backend déployées

const { accessToken } = await base44.asServiceRole.connectors.getConnection("wix");
const data = await wx.post("https://www.wixapis.com/contacts/v5/contacts/query",   // spec-index publicUrl
  { query: { cursorPaging: { limit: 10 } } }, accessToken);
return { count: data.contacts?.length, keys: Object.keys(data.contacts?.[0] || {}) };   // projette, ne déverse pas

Le même appel se déploie sous base44/functions/* (travail que l'app fait comme le propriétaire) — le code livré porte son propre fetch de quatre lignes ; les helpers sont un outil de build, pas une dépendance runtime.

Un client visiteur — src/lib/wixClient.js

La forme "site pour visiteurs" (Qu'est-ce que tu construis ?), en code — un fichier que les pages importent. Ni clientId (du rapport de contexte) ni le token créé ne sont un secret ; ensemble ils sont "un visiteur anonyme", sûr dans le code livré :

// src/lib/wixClient.js
let token;
const mint = async (body) => {
  const r = await (await fetch("https://www.wixapis.com/oauth2/token", { method: "POST",
    headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) })).json();
  token = r.access_token; sessionStorage.setItem("wixRefresh", r.refresh_token);   // expires_in: 14400s = 4h
};
// première visite :  mint({ clientId: WIX_CLIENT_ID, grantType: "anonymous" });
// à l'expiration :    mint({ refreshToken: sessionStorage.getItem("wixRefresh"), grantType: "refresh_token" });
//                    une fraîche création anonyme est un NOUVEAU visiteur — le panier du vieux va avec
export const wix = (path, opts = {}) => fetch("https://www.wixapis.com" + path, { ...opts,
  headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" } });

Contrat de token : …/headless/authentication/retrieve-tokens. Prouve la lane en un exec avant d'écrire les pages — crée un visiteur, fais une lecture publique avec :

const { access_token } = await wx.post("https://www.wixapis.com/oauth2/token",
  { clientId: WIX_CLIENT_ID, grantType: "anonymous" });   // clientId : du rapport de contexte
return await wx.post("<une lecture publique de Learn Wix>", { query: {} }, access_token);
// 200 ⇒ chaque page orientée visiteur de l'app est cet appel identique, pas de serveur entre

Skills similaires