Rp Quick Runtime
WixOfficialPublished by Wix
- Skill
- Tool integration
- Automation and integrations
- MIT
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The rp-quick-runtime provides a standardized set of utilities for Wix Stores import adapters to handle data constraints deterministically. It ensures that when source data exceeds platform limits—such as SKU length or tag counts—the data is safely trimmed or capped rather than silently skipped. The runtime manages the ledgering of these recovery actions, tracks import progress, and generates actionable final reports, ensuring consistency across different platform-specific adapters.
What it does
- Normalizes product data by applying deterministic caps and trims to SKUs and tag lists.
- Maintains a durable ledger of recovery actions for records written with data loss.
- Generates human-readable final reports detailing import success and specific recovery events.
- Enforces strict dry-run safety by preventing the creation of live artifacts during simulation.
- Provides progress tracking markers to identify interrupted imports.
- Validates adapter contracts and manifests to ensure compatibility before execution.
When to use it
- When developing a new Wix Stores import adapter that requires consistent data limit handling.
- When you need to ensure that data truncation or capping is logged and reported to the operator.
- When implementing import workflows that must distinguish between dry runs and live production writes.
- When you need a standardized way to generate final import reports and track recovery statistics.
Advantages
- Prevents silent data loss by explicitly reporting all modifications made to records.
- Ensures deterministic outcomes for SKU trimming and tag capping.
- Provides a unified reporting format across different platform adapters.
- Safeguards against accidental data corruption during dry runs.
- Simplifies the implementation of robust import state management.
Usage examples
- “How do I normalize a product SKU and tag list before writing to Wix Stores?”
- Use the normalizeProduct function to receive capped values and a list of recovery actions, then proceed to write the record and log the recoveries if any occurred.
- “How can I generate a final report after an import process completes?”
- Call writeFinalReport with the summary object and project directory to produce a markdown report detailing successful imports and recovery statistics.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/wix/rp-quick-runtime-ula6/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip rp-quick-runtime.zip -d .agents/skills
Documentation by Wix
rp-quick-runtime
Not invoked directly — this is a shared library (lib/limit-recovery.js), not a standalone
adapter. Every rp-quick-<platform> quick-mode adapter (rp-quick-shopify, rp-quick-woocommerce)
requires it from its own scripts/quick-mode.js and must use it from its first version. Platform adapters own source extraction and field semantics; this runtime owns
target-limit enforcement, recovery-ledger aggregation, and the human-readable final report — so a
Wix Stores validation limit never silently turns an otherwise-valid record into an opaque skip.
const recovery = require('../rp-quick-runtime/lib/limit-recovery.js');
const normalized = recovery.normalizeProduct({ sourceId, sku, tagIds });
// normalized.sku / normalized.tagIds are the capped/trimmed values to write;
// normalized.recoveries lists what was capped or trimmed, if anything.
await writeProduct({ ...product, sku: normalized.sku, tagIds: normalized.tagIds });
// Record written_with_loss only after the target write succeeds, and never in a dry run.
if (normalized.recoveries.length) {
if (!dryRun) await recovery.appendRecoveries(projectDir, adapterName, sourceId, normalized.recoveries);
recovery.recordRecoveredRecord(summary, normalized.recoveries);
}
// ...after the run:
await recovery.writeFinalReport(projectDir, summary, { dryRun });normalizeProduct applies the two verified Stores limits deterministically: caps tagIds at
STORES_PRODUCT_TAGS_MAX (first-seen source order, extras omitted) and trims sku to
STORES_SKU_MAX Unicode-safe characters with a stable source-ID-derived suffix so a trimmed SKU
stays unique. Every cap/trim is reported back in recoveries[], never applied silently.
appendRecoveries ledgers each recovery to execution/quick-limit-recoveries.ndjson under the
adapter's own name, tagged outcome: written_with_loss — the durable record of what was
preserved versus lost, per source record. Call it only after the target write succeeds.
recordRecoveredRecord increments summary.recovered once for the written source record and
increments summary.recoveryCounts once for each recovery event on that record.
writeFinalReport renders execution/quick-mode-final-report.md: a count of records imported
with deterministic recovery plus a per-recovery-code breakdown, so the operator gets a compact,
actionable account instead of a bare skip count. It adds a "Collections not migrated" section
from summary.excludedCollections, a "Products to check" section from
summary.writtenUnverified.product, and an inventory pointer when summary.inventory is set.
With { dryRun: true } it writes execution/dry-run-final-report.md instead and sets
summary.finalReport to that path, so a dry run can never overwrite or stand in for the live
report. A dry run must write no live artifact at all (report, ledger, audit log, crosswalk or
marker); adapters skip those writes when dryRun is true. writeFileAtomic (temp file, then
rename) is the shared way to publish a report that a crash must not leave half-written.
lib/import-progress.js holds three more shared pieces. Each adapter's import first calls
assertQuickImportAdapter, which fails unless the plan and extract manifest both carry exactly
the loaded contract's { id, version, fingerprint }. It then calls
writeQuickImportStartedMarker(projectDir, plan.adapter) before its first destination write.
It is skipped on a dry run and never rewritten on resume; concurrent first attempts publish
one marker (temp file, then a no-replace hard link). It writes
state/quick-import-started.json, which the router reads as evidence of an interrupted import.
validateQuickExecutionManifest is the router's check of the manifest that plan writes.
Files
- ATTRIBUTION.mdAdded by AutoGineer844 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md3.9 KiB
- lib/import-progress.jsScript4.4 KiB
- lib/limit-recovery.jsScript5.0 KiB
lib/import-progress.js
'use strict';
// Shared by both rp-quick-<platform> adapters (producers) and by
// lib/orchestration-router.js (consumer), so the quick manifest rule and the start-marker
// path live in one place.
const fs = require('node:fs/promises');
const path = require('node:path');
const crypto = require('node:crypto');
const QUICK_IMPORT_STARTED_MARKER = 'state/quick-import-started.json';
const MARKER_SCHEMA_VERSION = 1;
// The quick manifest written by `quick-mode.js plan`. It is deliberately not the full-mode
// `validateManifest` shape (no projectId/planVersion/tasks).
function validateQuickExecutionManifest(manifest) {
const errors = [];
if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) {
return { ok: false, errors: ['quick execution manifest must be an object'] };
}
if (manifest.schemaVersion !== undefined && manifest.schemaVersion !== 1) errors.push('schemaVersion must be absent or 1');
if (manifest.managementImportMode !== 'quick') errors.push("managementImportMode must be 'quick'");
if (typeof manifest.importCommand !== 'string' || !manifest.importCommand.trim()) errors.push('importCommand must be a non-empty string');
if (!Array.isArray(manifest.entities)) errors.push('entities must be an array');
return { ok: errors.length === 0, errors };
}
// Before the marker or any destination write, the loaded plan's adapter must be
// exactly the adapter's current contract `{ id, version, fingerprint }`, and the extract
// manifest must carry that same adapter. This is what makes the marker's copied `adapter` the
// current adapter-contract identity rather than whatever a stale or edited plan says.
const ADAPTER_KEYS = ['id', 'version', 'fingerprint'];
function sameAdapter(left, right) {
return Boolean(left && typeof left === 'object' && right && typeof right === 'object')
&& ADAPTER_KEYS.every((key) => typeof left[key] === 'string' && left[key] !== '' && left[key] === right[key]);
}
function assertQuickImportAdapter({ plan, manifest, current }) {
if (!sameAdapter(current, current)) throw new Error('quick adapter contract is missing id, version, or fingerprint');
if (!sameAdapter(plan && plan.adapter, current)) throw new Error('quick adapter contract changed; re-run preflight and plan');
if (!sameAdapter(manifest && manifest.adapter, current)) throw new Error('extraction does not match quick plan; re-extract');
}
// Durable pre-write evidence that a live quick import began. Written once, before the first
// destination create/update, never on a dry run, and never rewritten on resume: an existing
// marker keeps describing the first live attempt. `adapter` is the plan's own
// `{ id, version, fingerprint }`, already checked by `assertQuickImportAdapter` to equal the
// loaded adapter contract, copied verbatim.
//
// Publication is atomic and no-replace: the complete JSON is written and fsynced to a
// uniquely named temp file, then hard-linked to the marker path. `link` fails with EEXIST
// when a marker already exists, so concurrent first attempts cannot replace each other and
// a reader never sees a partial marker. The loser is an unchanged resume.
async function writeQuickImportStartedMarker(projectDir, adapter) {
const markerPath = path.join(projectDir, QUICK_IMPORT_STARTED_MARKER);
const unchanged = { written: false, path: QUICK_IMPORT_STARTED_MARKER };
try {
await fs.access(markerPath);
return unchanged;
} catch (error) {
if (!error || error.code !== 'ENOENT') throw error;
}
const { id, version, fingerprint } = adapter || {};
const marker = { schemaVersion: MARKER_SCHEMA_VERSION, adapter: { id, version, fingerprint }, startedAt: new Date().toISOString() };
await fs.mkdir(path.dirname(markerPath), { recursive: true });
const tempPath = `${markerPath}.${process.pid}.${crypto.randomUUID()}.tmp`;
const handle = await fs.open(tempPath, 'wx');
try {
await handle.writeFile(`${JSON.stringify(marker, null, 2)}\n`, 'utf8');
await handle.sync();
} finally {
await handle.close();
}
try {
await fs.link(tempPath, markerPath);
return { written: true, path: QUICK_IMPORT_STARTED_MARKER };
} catch (error) {
if (error && error.code === 'EEXIST') return unchanged;
throw error;
} finally {
await fs.rm(tempPath, { force: true });
}
}
module.exports = {
QUICK_IMPORT_STARTED_MARKER,
assertQuickImportAdapter,
validateQuickExecutionManifest,
writeQuickImportStartedMarker,
};
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Node.js
Security
What our scanner noticed· Low risk
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Wix
- Publisher
- Wix (Official)
- Source
- wix/skills · skills/wix-replatform/resources/rp-quick-runtime · commit d168bc2
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Wix.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download rp-quick-runtime.zip