@snowcone-app/sdk 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/{chunk-D5ZRGKA5.js → chunk-IMJKV4YO.js} +24 -1
- package/dist/index.cjs +70 -6
- package/dist/index.d.cts +21 -4
- package/dist/index.d.ts +21 -4
- package/dist/index.js +47 -6
- package/dist/react.cjs +24 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{websocket-Poy8LZNA.d.cts → websocket-DTRStLRH.d.cts} +6 -2
- package/dist/{websocket-Poy8LZNA.d.ts → websocket-DTRStLRH.d.ts} +6 -2
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#288](https://github.com/snowcone-app/snowcone-monorepo/pull/288) [`7ccfe17`](https://github.com/snowcone-app/snowcone-monorepo/commit/7ccfe1724b166719e2aac4e0f5e9a2a5c2c440a4) Thanks [@kevinsproles](https://github.com/kevinsproles)! - `getProduct()` now throws an actionable error for an unknown product id/slug (naming the id, the index, and pointing to `listProducts()`) instead of a bare "Not found", so a typo'd catalog id no longer surfaces as the raw Meilisearch `document_not_found` shape.
|
|
8
|
+
|
|
9
|
+
- [#281](https://github.com/snowcone-app/snowcone-monorepo/pull/281) [`9e3d941`](https://github.com/snowcone-app/snowcone-monorepo/commit/9e3d941419b3df004feb47369692d30a4a8a2938) Thanks [@kevinsproles](https://github.com/kevinsproles)! - Coalesce duplicate realtime `connect()` calls while a grant is pending or the WebSocket is still connecting, preventing competing initial sockets and benign 1006 close noise during first render.
|
|
10
|
+
|
|
11
|
+
- [#293](https://github.com/snowcone-app/snowcone-monorepo/pull/293) [`64bcaf0`](https://github.com/snowcone-app/snowcone-monorepo/commit/64bcaf0b61dcd611a81d9a9cb013ab89dc8003f7) Thanks [@kevinsproles](https://github.com/kevinsproles)! - Stop idle canvases from continuously streaming server renders (which silently burned the realtime render budget). `serializeStateForServer` now quantizes element/artboard geometry to 0.01px so the render loop's sub-pixel float drift no longer produces a new wire state every frame, and `RealtimeMockupService` drops duplicate consecutive canvas states per placement. A naive `onChange → renderState` wiring no longer bills the integrator's realtime quota while the user is doing nothing.
|
|
12
|
+
|
|
13
|
+
- [#300](https://github.com/snowcone-app/snowcone-monorepo/pull/300) [`f80f00a`](https://github.com/snowcone-app/snowcone-monorepo/commit/f80f00a0e38631aac936bfe60de14e7d47be3e6a) Thanks [@kevinsproles](https://github.com/kevinsproles)! - `mockupUrl()` now resolves the shop id and mockup base URL by reading `process.env.NEXT_PUBLIC_*` directly instead of through an aliased `const env = process.env`. The alias defeated Next/webpack's static inlining, so in browser bundles the values came back `undefined` and the live-preview URL fell through to the `SHOP_NOT_CONFIGURED` placeholder. Consumers relying on `NEXT_PUBLIC_SNOWCONE_SHOP_ID` / `NEXT_PUBLIC_MERCH_MOCKUP_URL` env fallbacks (rather than the explicit `shop` prop / `window.snowcone`) now get the configured values.
|
|
14
|
+
|
|
3
15
|
## 0.3.3
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -19,6 +19,10 @@ var RealtimeMockupService = class {
|
|
|
19
19
|
lastBlobSentAt = 0;
|
|
20
20
|
canvasBlobs = /* @__PURE__ */ new Map();
|
|
21
21
|
canvasStates = /* @__PURE__ */ new Map();
|
|
22
|
+
// Serialized JSON of the last canvas state ACTUALLY SENT per placement. Used to
|
|
23
|
+
// drop duplicate consecutive states so an idle canvas (whose onChange keeps
|
|
24
|
+
// firing identical states) doesn't stream renders and burn the realtime budget.
|
|
25
|
+
lastSentStateJson = {};
|
|
22
26
|
colors = /* @__PURE__ */ new Map();
|
|
23
27
|
lastSendTime = {};
|
|
24
28
|
throttleTimeouts = {};
|
|
@@ -39,6 +43,8 @@ var RealtimeMockupService = class {
|
|
|
39
43
|
// the WS with `?token=`, and renews ~15s before expiry via a `renew` message.
|
|
40
44
|
tokenProvider;
|
|
41
45
|
renewTimer = null;
|
|
46
|
+
isConnecting = false;
|
|
47
|
+
connectAttempt = 0;
|
|
42
48
|
setCallbacks(callbacks) {
|
|
43
49
|
this.callbacks = callbacks;
|
|
44
50
|
}
|
|
@@ -65,16 +71,21 @@ var RealtimeMockupService = class {
|
|
|
65
71
|
this.callbacks.onLog?.(message, type);
|
|
66
72
|
}
|
|
67
73
|
connect() {
|
|
68
|
-
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
74
|
+
if (this.ws?.readyState === WebSocket.OPEN || this.ws?.readyState === WebSocket.CONNECTING || this.isConnecting) {
|
|
69
75
|
return;
|
|
70
76
|
}
|
|
77
|
+
const attempt = ++this.connectAttempt;
|
|
78
|
+
this.isConnecting = true;
|
|
71
79
|
if (this.tokenProvider) {
|
|
72
80
|
this.tokenProvider().then((grant) => {
|
|
81
|
+
if (attempt !== this.connectAttempt || !this.isConnecting) return;
|
|
73
82
|
this.openSocket(grant.token);
|
|
74
83
|
this.scheduleRenew(grant.expiresAt);
|
|
75
84
|
}).catch((err) => {
|
|
85
|
+
if (attempt !== this.connectAttempt) return;
|
|
76
86
|
this.addLog(`Failed to obtain realtime grant: ${err}`);
|
|
77
87
|
this.status = "Disconnected";
|
|
88
|
+
this.isConnecting = false;
|
|
78
89
|
});
|
|
79
90
|
return;
|
|
80
91
|
}
|
|
@@ -88,6 +99,7 @@ var RealtimeMockupService = class {
|
|
|
88
99
|
console.log(`[WS] connection OPENED to ${this.wsUrl}`);
|
|
89
100
|
this.addLog("WebSocket connection opened");
|
|
90
101
|
this.status = "Connected";
|
|
102
|
+
this.isConnecting = false;
|
|
91
103
|
};
|
|
92
104
|
this.ws.onmessage = (event) => {
|
|
93
105
|
try {
|
|
@@ -106,11 +118,13 @@ var RealtimeMockupService = class {
|
|
|
106
118
|
this.isConfigured = false;
|
|
107
119
|
this.configSent = false;
|
|
108
120
|
this.clearRenew();
|
|
121
|
+
this.isConnecting = false;
|
|
109
122
|
this.callbacks.onDisconnected?.();
|
|
110
123
|
};
|
|
111
124
|
this.ws.onerror = (error) => {
|
|
112
125
|
this.addLog(`WebSocket error: ${error}`);
|
|
113
126
|
this.status = "Disconnected";
|
|
127
|
+
this.isConnecting = false;
|
|
114
128
|
};
|
|
115
129
|
}
|
|
116
130
|
scheduleRenew(expiresAt) {
|
|
@@ -260,6 +274,8 @@ var RealtimeMockupService = class {
|
|
|
260
274
|
}
|
|
261
275
|
}
|
|
262
276
|
disconnect() {
|
|
277
|
+
this.connectAttempt++;
|
|
278
|
+
this.isConnecting = false;
|
|
263
279
|
this.clearRenew();
|
|
264
280
|
if (this.ws) {
|
|
265
281
|
this.ws.close();
|
|
@@ -298,6 +314,7 @@ var RealtimeMockupService = class {
|
|
|
298
314
|
this.canvasStates.clear();
|
|
299
315
|
this.colors.clear();
|
|
300
316
|
this.lastSendTime = {};
|
|
317
|
+
this.lastSentStateJson = {};
|
|
301
318
|
Object.values(this.throttleTimeouts).forEach((timeout) => clearTimeout(timeout));
|
|
302
319
|
this.throttleTimeouts = {};
|
|
303
320
|
this.requestVersion = 0;
|
|
@@ -503,6 +520,11 @@ var RealtimeMockupService = class {
|
|
|
503
520
|
console.log(`[WS] sendCanvasStateImmediately ABORTED: ws not open`);
|
|
504
521
|
return;
|
|
505
522
|
}
|
|
523
|
+
const stateJson = JSON.stringify(state);
|
|
524
|
+
if (this.lastSentStateJson[placement] === stateJson) {
|
|
525
|
+
this.addLog(`Skipped duplicate canvas state for "${placement}" (unchanged)`, "sent");
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
506
528
|
this.lastSentVersion = ++this.requestVersion;
|
|
507
529
|
this.latestSentVersionByPlacement[placement] = this.lastSentVersion;
|
|
508
530
|
const message = JSON.stringify({
|
|
@@ -512,6 +534,7 @@ var RealtimeMockupService = class {
|
|
|
512
534
|
state
|
|
513
535
|
});
|
|
514
536
|
this.ws.send(message);
|
|
537
|
+
this.lastSentStateJson[placement] = stateJson;
|
|
515
538
|
this.lastBlobSentAt = Date.now();
|
|
516
539
|
this.addLog(`Sent canvas state for "${placement}" (${(message.length / 1024).toFixed(1)}KB, v${this.lastSentVersion})`, "sent");
|
|
517
540
|
this.callbacks.onBlobSent?.(placement);
|
package/dist/index.cjs
CHANGED
|
@@ -1797,14 +1797,12 @@ function buildMockupUrl2(options, cfg) {
|
|
|
1797
1797
|
);
|
|
1798
1798
|
}
|
|
1799
1799
|
function resolveMockupBaseUrl() {
|
|
1800
|
-
const env = typeof process !== "undefined" ? process.env : void 0;
|
|
1801
1800
|
const winConfig = typeof window !== "undefined" && window.snowcone || {};
|
|
1802
|
-
return winConfig.mockupUrl || env?.SNOWCONE_IMAGE_URL || env?.NEXT_PUBLIC_MERCH_MOCKUP_URL || "https://cdn.snowcone.app";
|
|
1801
|
+
return winConfig.mockupUrl || typeof process !== "undefined" && process.env?.SNOWCONE_IMAGE_URL || typeof process !== "undefined" && process.env?.NEXT_PUBLIC_MERCH_MOCKUP_URL || "https://cdn.snowcone.app";
|
|
1803
1802
|
}
|
|
1804
1803
|
function resolveShop() {
|
|
1805
|
-
const env = typeof process !== "undefined" ? process.env : void 0;
|
|
1806
1804
|
const winConfig = typeof window !== "undefined" && window.snowcone || {};
|
|
1807
|
-
return winConfig.shop || env?.SNOWCONE_SHOP_ID || env?.NEXT_PUBLIC_SNOWCONE_SHOP_ID || "SHOP_NOT_CONFIGURED";
|
|
1805
|
+
return winConfig.shop || typeof process !== "undefined" && process.env?.SNOWCONE_SHOP_ID || typeof process !== "undefined" && process.env?.NEXT_PUBLIC_SNOWCONE_SHOP_ID || "SHOP_NOT_CONFIGURED";
|
|
1808
1806
|
}
|
|
1809
1807
|
function mockupUrl(options) {
|
|
1810
1808
|
const mockupBaseUrl = resolveMockupBaseUrl();
|
|
@@ -4837,6 +4835,10 @@ var RealtimeMockupService = class {
|
|
|
4837
4835
|
lastBlobSentAt = 0;
|
|
4838
4836
|
canvasBlobs = /* @__PURE__ */ new Map();
|
|
4839
4837
|
canvasStates = /* @__PURE__ */ new Map();
|
|
4838
|
+
// Serialized JSON of the last canvas state ACTUALLY SENT per placement. Used to
|
|
4839
|
+
// drop duplicate consecutive states so an idle canvas (whose onChange keeps
|
|
4840
|
+
// firing identical states) doesn't stream renders and burn the realtime budget.
|
|
4841
|
+
lastSentStateJson = {};
|
|
4840
4842
|
colors = /* @__PURE__ */ new Map();
|
|
4841
4843
|
lastSendTime = {};
|
|
4842
4844
|
throttleTimeouts = {};
|
|
@@ -4857,6 +4859,8 @@ var RealtimeMockupService = class {
|
|
|
4857
4859
|
// the WS with `?token=`, and renews ~15s before expiry via a `renew` message.
|
|
4858
4860
|
tokenProvider;
|
|
4859
4861
|
renewTimer = null;
|
|
4862
|
+
isConnecting = false;
|
|
4863
|
+
connectAttempt = 0;
|
|
4860
4864
|
setCallbacks(callbacks) {
|
|
4861
4865
|
this.callbacks = callbacks;
|
|
4862
4866
|
}
|
|
@@ -4883,16 +4887,21 @@ var RealtimeMockupService = class {
|
|
|
4883
4887
|
this.callbacks.onLog?.(message, type);
|
|
4884
4888
|
}
|
|
4885
4889
|
connect() {
|
|
4886
|
-
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
4890
|
+
if (this.ws?.readyState === WebSocket.OPEN || this.ws?.readyState === WebSocket.CONNECTING || this.isConnecting) {
|
|
4887
4891
|
return;
|
|
4888
4892
|
}
|
|
4893
|
+
const attempt = ++this.connectAttempt;
|
|
4894
|
+
this.isConnecting = true;
|
|
4889
4895
|
if (this.tokenProvider) {
|
|
4890
4896
|
this.tokenProvider().then((grant) => {
|
|
4897
|
+
if (attempt !== this.connectAttempt || !this.isConnecting) return;
|
|
4891
4898
|
this.openSocket(grant.token);
|
|
4892
4899
|
this.scheduleRenew(grant.expiresAt);
|
|
4893
4900
|
}).catch((err) => {
|
|
4901
|
+
if (attempt !== this.connectAttempt) return;
|
|
4894
4902
|
this.addLog(`Failed to obtain realtime grant: ${err}`);
|
|
4895
4903
|
this.status = "Disconnected";
|
|
4904
|
+
this.isConnecting = false;
|
|
4896
4905
|
});
|
|
4897
4906
|
return;
|
|
4898
4907
|
}
|
|
@@ -4906,6 +4915,7 @@ var RealtimeMockupService = class {
|
|
|
4906
4915
|
console.log(`[WS] connection OPENED to ${this.wsUrl}`);
|
|
4907
4916
|
this.addLog("WebSocket connection opened");
|
|
4908
4917
|
this.status = "Connected";
|
|
4918
|
+
this.isConnecting = false;
|
|
4909
4919
|
};
|
|
4910
4920
|
this.ws.onmessage = (event) => {
|
|
4911
4921
|
try {
|
|
@@ -4924,11 +4934,13 @@ var RealtimeMockupService = class {
|
|
|
4924
4934
|
this.isConfigured = false;
|
|
4925
4935
|
this.configSent = false;
|
|
4926
4936
|
this.clearRenew();
|
|
4937
|
+
this.isConnecting = false;
|
|
4927
4938
|
this.callbacks.onDisconnected?.();
|
|
4928
4939
|
};
|
|
4929
4940
|
this.ws.onerror = (error) => {
|
|
4930
4941
|
this.addLog(`WebSocket error: ${error}`);
|
|
4931
4942
|
this.status = "Disconnected";
|
|
4943
|
+
this.isConnecting = false;
|
|
4932
4944
|
};
|
|
4933
4945
|
}
|
|
4934
4946
|
scheduleRenew(expiresAt) {
|
|
@@ -5078,6 +5090,8 @@ var RealtimeMockupService = class {
|
|
|
5078
5090
|
}
|
|
5079
5091
|
}
|
|
5080
5092
|
disconnect() {
|
|
5093
|
+
this.connectAttempt++;
|
|
5094
|
+
this.isConnecting = false;
|
|
5081
5095
|
this.clearRenew();
|
|
5082
5096
|
if (this.ws) {
|
|
5083
5097
|
this.ws.close();
|
|
@@ -5116,6 +5130,7 @@ var RealtimeMockupService = class {
|
|
|
5116
5130
|
this.canvasStates.clear();
|
|
5117
5131
|
this.colors.clear();
|
|
5118
5132
|
this.lastSendTime = {};
|
|
5133
|
+
this.lastSentStateJson = {};
|
|
5119
5134
|
Object.values(this.throttleTimeouts).forEach((timeout) => clearTimeout(timeout));
|
|
5120
5135
|
this.throttleTimeouts = {};
|
|
5121
5136
|
this.requestVersion = 0;
|
|
@@ -5321,6 +5336,11 @@ var RealtimeMockupService = class {
|
|
|
5321
5336
|
console.log(`[WS] sendCanvasStateImmediately ABORTED: ws not open`);
|
|
5322
5337
|
return;
|
|
5323
5338
|
}
|
|
5339
|
+
const stateJson = JSON.stringify(state);
|
|
5340
|
+
if (this.lastSentStateJson[placement] === stateJson) {
|
|
5341
|
+
this.addLog(`Skipped duplicate canvas state for "${placement}" (unchanged)`, "sent");
|
|
5342
|
+
return;
|
|
5343
|
+
}
|
|
5324
5344
|
this.lastSentVersion = ++this.requestVersion;
|
|
5325
5345
|
this.latestSentVersionByPlacement[placement] = this.lastSentVersion;
|
|
5326
5346
|
const message = JSON.stringify({
|
|
@@ -5330,6 +5350,7 @@ var RealtimeMockupService = class {
|
|
|
5330
5350
|
state
|
|
5331
5351
|
});
|
|
5332
5352
|
this.ws.send(message);
|
|
5353
|
+
this.lastSentStateJson[placement] = stateJson;
|
|
5333
5354
|
this.lastBlobSentAt = Date.now();
|
|
5334
5355
|
this.addLog(`Sent canvas state for "${placement}" (${(message.length / 1024).toFixed(1)}KB, v${this.lastSentVersion})`, "sent");
|
|
5335
5356
|
this.callbacks.onBlobSent?.(placement);
|
|
@@ -5548,6 +5569,20 @@ function assertVariantSelected(product) {
|
|
|
5548
5569
|
);
|
|
5549
5570
|
}
|
|
5550
5571
|
}
|
|
5572
|
+
function warnUnknownPlacement(product, placement) {
|
|
5573
|
+
const placements = product.placements;
|
|
5574
|
+
if (!placements?.length) return null;
|
|
5575
|
+
if (placements.includes(placement)) return null;
|
|
5576
|
+
return `RenderSession: placement "${placement}" doesn't match product ${product.productId}'s placements: [${placements.join(", ")}]. Your artboard name + renderState placement arg must equal the catalog placements[].label.`;
|
|
5577
|
+
}
|
|
5578
|
+
function warnPlacementArtboardMismatch(placement, state) {
|
|
5579
|
+
const artboards = state.artboards;
|
|
5580
|
+
if (!Array.isArray(artboards) || artboards.length === 0) return null;
|
|
5581
|
+
const names = artboards.map((a) => a.name).filter((n) => typeof n === "string");
|
|
5582
|
+
if (names.length === 0) return null;
|
|
5583
|
+
if (names.includes(placement)) return null;
|
|
5584
|
+
return `RenderSession: renderState placement "${placement}" has no matching artboard in the state (artboards: [${names.join(", ")}]). The artboard name must equal the placement arg.`;
|
|
5585
|
+
}
|
|
5551
5586
|
var DEFAULT_RENDER_TIMEOUT_MS = 3e4;
|
|
5552
5587
|
var RenderSession = class {
|
|
5553
5588
|
svc;
|
|
@@ -5657,6 +5692,7 @@ var RenderSession = class {
|
|
|
5657
5692
|
*/
|
|
5658
5693
|
async renderState(placement, state, throttleMs = 0) {
|
|
5659
5694
|
await this.connect();
|
|
5695
|
+
this.warnPlacement(placement, state);
|
|
5660
5696
|
this.svc.sendCanvasState(placement, state, this.product?.mockupIds.length ?? 1, throttleMs);
|
|
5661
5697
|
this.armWatchdog();
|
|
5662
5698
|
}
|
|
@@ -5669,6 +5705,7 @@ var RenderSession = class {
|
|
|
5669
5705
|
*/
|
|
5670
5706
|
async renderSavedState(placement, stateId) {
|
|
5671
5707
|
await this.connect();
|
|
5708
|
+
this.warnPlacement(placement);
|
|
5672
5709
|
this.svc.sendCanvasStateRef(placement, stateId);
|
|
5673
5710
|
this.armWatchdog();
|
|
5674
5711
|
}
|
|
@@ -5687,6 +5724,28 @@ var RenderSession = class {
|
|
|
5687
5724
|
this.svc.disconnect();
|
|
5688
5725
|
this.ready = null;
|
|
5689
5726
|
}
|
|
5727
|
+
/**
|
|
5728
|
+
* Emit loud, non-fatal warnings for the two ways a placement name silently
|
|
5729
|
+
* produces a blank mockup: (1) the name isn't one of the product's known
|
|
5730
|
+
* `placements` labels, and (2) no artboard in `state` is named after it. Each
|
|
5731
|
+
* warning goes to BOTH `console.warn` and the `onError` callback (if wired) so
|
|
5732
|
+
* a dev sees it whichever channel they watch. Never throws.
|
|
5733
|
+
*/
|
|
5734
|
+
warnPlacement(placement, state) {
|
|
5735
|
+
const warnings = [];
|
|
5736
|
+
if (this.product) {
|
|
5737
|
+
const w = warnUnknownPlacement(this.product, placement);
|
|
5738
|
+
if (w) warnings.push(w);
|
|
5739
|
+
}
|
|
5740
|
+
if (state) {
|
|
5741
|
+
const w = warnPlacementArtboardMismatch(placement, state);
|
|
5742
|
+
if (w) warnings.push(w);
|
|
5743
|
+
}
|
|
5744
|
+
for (const w of warnings) {
|
|
5745
|
+
console.warn(w);
|
|
5746
|
+
this.errorCb?.(w);
|
|
5747
|
+
}
|
|
5748
|
+
}
|
|
5690
5749
|
/**
|
|
5691
5750
|
* (Re)arm the render watchdog. Clears any prior timer and, unless disabled
|
|
5692
5751
|
* (`renderTimeoutMs <= 0`), starts a fresh one. If it fires before a mockup
|
|
@@ -5802,7 +5861,12 @@ async function getProduct(idOrSlug, config) {
|
|
|
5802
5861
|
const url = `${meilisearchHost}/indexes/${meilisearchIndex}/documents/${idOrSlug}`;
|
|
5803
5862
|
const res = await f(url, { method: "GET", headers });
|
|
5804
5863
|
if (res.status === 404)
|
|
5805
|
-
throw Object.assign(
|
|
5864
|
+
throw Object.assign(
|
|
5865
|
+
new Error(
|
|
5866
|
+
`getProduct: no catalog product "${idOrSlug}" in index "${meilisearchIndex}". Pass a valid product id/slug (browse with listProducts()).`
|
|
5867
|
+
),
|
|
5868
|
+
{ code: "NOT_FOUND", productId: idOrSlug }
|
|
5869
|
+
);
|
|
5806
5870
|
if (!res.ok) throw new Error(`getProduct failed: ${res.status}`);
|
|
5807
5871
|
const raw = await res.json();
|
|
5808
5872
|
return validateProductLoose(raw);
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { createDevFetcher } from './dev-fetcher.cjs';
|
|
2
2
|
import { PublicDesign, PublicFill, PublicOptions } from '@snowcone-app/mockup-url';
|
|
3
|
-
import { O as OptionAttribute, a as OptionSelection, C as Combination, F as FrameworkAdapter, b as ComponentProps, c as ComponentDescriptor, d as ComponentState, e as ComponentContext, f as ComponentLifecycleHooks, E as EventHandler, g as FrameworkUtilities, P as PlacementSettings, M as MockupResult, R as RealtimeMockupService } from './websocket-
|
|
4
|
-
export { A as AdapterRegistry, h as ProductComponentContext, i as RealtimeMockupCallbacks, j as RealtimeMockupState, k as RenderResult, W as WebSocketConfig, l as WebSocketMessage, m as adapterRegistry, n as computeDisabledChoices, o as createComponent, p as defineComponent, q as deriveDefaultSelection, r as findBestCombination, s as getPricePreview, t as isOptionAvailable, u as resolveBestCombination } from './websocket-
|
|
3
|
+
import { O as OptionAttribute, a as OptionSelection, C as Combination, F as FrameworkAdapter, b as ComponentProps, c as ComponentDescriptor, d as ComponentState, e as ComponentContext, f as ComponentLifecycleHooks, E as EventHandler, g as FrameworkUtilities, P as PlacementSettings, M as MockupResult, R as RealtimeMockupService } from './websocket-DTRStLRH.cjs';
|
|
4
|
+
export { A as AdapterRegistry, h as ProductComponentContext, i as RealtimeMockupCallbacks, j as RealtimeMockupState, k as RenderResult, W as WebSocketConfig, l as WebSocketMessage, m as adapterRegistry, n as computeDisabledChoices, o as createComponent, p as defineComponent, q as deriveDefaultSelection, r as findBestCombination, s as getPricePreview, t as isOptionAvailable, u as resolveBestCombination } from './websocket-DTRStLRH.cjs';
|
|
5
5
|
|
|
6
6
|
interface CatalogProduct$2 {
|
|
7
7
|
id: string;
|
|
@@ -2584,8 +2584,7 @@ declare function fetchRealtimeGrant(grantUrl: string, shop: string, fetchImpl?:
|
|
|
2584
2584
|
* product: { productId: 'BEEB77', mockupIds: ['front'] },
|
|
2585
2585
|
* });
|
|
2586
2586
|
* session.onMockups((results) => { img.src = results[0].imageUrl; });
|
|
2587
|
-
* await session.
|
|
2588
|
-
* session.renderState('Front', serializeStateForServer(canvasState));
|
|
2587
|
+
* await session.renderState('Front', serializeStateForServer(canvasState));
|
|
2589
2588
|
*
|
|
2590
2589
|
* For the low-level escape hatch, use {@link RealtimeMockupService} directly.
|
|
2591
2590
|
*/
|
|
@@ -2656,6 +2655,16 @@ interface RenderProduct {
|
|
|
2656
2655
|
* catalog product's `options` object verbatim.
|
|
2657
2656
|
*/
|
|
2658
2657
|
options?: RenderProductOptions;
|
|
2658
|
+
/**
|
|
2659
|
+
* The product's valid placement labels (the catalog product's
|
|
2660
|
+
* `placements[].label`, e.g. `['Front']`). Optional, but passing it lets the
|
|
2661
|
+
* session WARN LOUDLY when a `renderState(placement, ...)` call uses a name
|
|
2662
|
+
* that isn't a real placement — turning the classic silent blank-mockup
|
|
2663
|
+
* (server waits on `incomplete_canvas_placements`) into an actionable
|
|
2664
|
+
* dev-time message. You can pass the catalog product's
|
|
2665
|
+
* `placements.map(p => p.label)` verbatim.
|
|
2666
|
+
*/
|
|
2667
|
+
placements?: string[];
|
|
2659
2668
|
/** Render width in px (default 1000). */
|
|
2660
2669
|
width?: number;
|
|
2661
2670
|
/** Per-placement settings (tiling, scale mode). */
|
|
@@ -2756,6 +2765,14 @@ declare class RenderSession {
|
|
|
2756
2765
|
getMockups(): MockupResult[];
|
|
2757
2766
|
/** Close the WebSocket and stop auto-renew. */
|
|
2758
2767
|
close(): void;
|
|
2768
|
+
/**
|
|
2769
|
+
* Emit loud, non-fatal warnings for the two ways a placement name silently
|
|
2770
|
+
* produces a blank mockup: (1) the name isn't one of the product's known
|
|
2771
|
+
* `placements` labels, and (2) no artboard in `state` is named after it. Each
|
|
2772
|
+
* warning goes to BOTH `console.warn` and the `onError` callback (if wired) so
|
|
2773
|
+
* a dev sees it whichever channel they watch. Never throws.
|
|
2774
|
+
*/
|
|
2775
|
+
private warnPlacement;
|
|
2759
2776
|
/**
|
|
2760
2777
|
* (Re)arm the render watchdog. Clears any prior timer and, unless disabled
|
|
2761
2778
|
* (`renderTimeoutMs <= 0`), starts a fresh one. If it fires before a mockup
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { createDevFetcher } from './dev-fetcher.js';
|
|
2
2
|
import { PublicDesign, PublicFill, PublicOptions } from '@snowcone-app/mockup-url';
|
|
3
|
-
import { O as OptionAttribute, a as OptionSelection, C as Combination, F as FrameworkAdapter, b as ComponentProps, c as ComponentDescriptor, d as ComponentState, e as ComponentContext, f as ComponentLifecycleHooks, E as EventHandler, g as FrameworkUtilities, P as PlacementSettings, M as MockupResult, R as RealtimeMockupService } from './websocket-
|
|
4
|
-
export { A as AdapterRegistry, h as ProductComponentContext, i as RealtimeMockupCallbacks, j as RealtimeMockupState, k as RenderResult, W as WebSocketConfig, l as WebSocketMessage, m as adapterRegistry, n as computeDisabledChoices, o as createComponent, p as defineComponent, q as deriveDefaultSelection, r as findBestCombination, s as getPricePreview, t as isOptionAvailable, u as resolveBestCombination } from './websocket-
|
|
3
|
+
import { O as OptionAttribute, a as OptionSelection, C as Combination, F as FrameworkAdapter, b as ComponentProps, c as ComponentDescriptor, d as ComponentState, e as ComponentContext, f as ComponentLifecycleHooks, E as EventHandler, g as FrameworkUtilities, P as PlacementSettings, M as MockupResult, R as RealtimeMockupService } from './websocket-DTRStLRH.js';
|
|
4
|
+
export { A as AdapterRegistry, h as ProductComponentContext, i as RealtimeMockupCallbacks, j as RealtimeMockupState, k as RenderResult, W as WebSocketConfig, l as WebSocketMessage, m as adapterRegistry, n as computeDisabledChoices, o as createComponent, p as defineComponent, q as deriveDefaultSelection, r as findBestCombination, s as getPricePreview, t as isOptionAvailable, u as resolveBestCombination } from './websocket-DTRStLRH.js';
|
|
5
5
|
|
|
6
6
|
interface CatalogProduct$2 {
|
|
7
7
|
id: string;
|
|
@@ -2584,8 +2584,7 @@ declare function fetchRealtimeGrant(grantUrl: string, shop: string, fetchImpl?:
|
|
|
2584
2584
|
* product: { productId: 'BEEB77', mockupIds: ['front'] },
|
|
2585
2585
|
* });
|
|
2586
2586
|
* session.onMockups((results) => { img.src = results[0].imageUrl; });
|
|
2587
|
-
* await session.
|
|
2588
|
-
* session.renderState('Front', serializeStateForServer(canvasState));
|
|
2587
|
+
* await session.renderState('Front', serializeStateForServer(canvasState));
|
|
2589
2588
|
*
|
|
2590
2589
|
* For the low-level escape hatch, use {@link RealtimeMockupService} directly.
|
|
2591
2590
|
*/
|
|
@@ -2656,6 +2655,16 @@ interface RenderProduct {
|
|
|
2656
2655
|
* catalog product's `options` object verbatim.
|
|
2657
2656
|
*/
|
|
2658
2657
|
options?: RenderProductOptions;
|
|
2658
|
+
/**
|
|
2659
|
+
* The product's valid placement labels (the catalog product's
|
|
2660
|
+
* `placements[].label`, e.g. `['Front']`). Optional, but passing it lets the
|
|
2661
|
+
* session WARN LOUDLY when a `renderState(placement, ...)` call uses a name
|
|
2662
|
+
* that isn't a real placement — turning the classic silent blank-mockup
|
|
2663
|
+
* (server waits on `incomplete_canvas_placements`) into an actionable
|
|
2664
|
+
* dev-time message. You can pass the catalog product's
|
|
2665
|
+
* `placements.map(p => p.label)` verbatim.
|
|
2666
|
+
*/
|
|
2667
|
+
placements?: string[];
|
|
2659
2668
|
/** Render width in px (default 1000). */
|
|
2660
2669
|
width?: number;
|
|
2661
2670
|
/** Per-placement settings (tiling, scale mode). */
|
|
@@ -2756,6 +2765,14 @@ declare class RenderSession {
|
|
|
2756
2765
|
getMockups(): MockupResult[];
|
|
2757
2766
|
/** Close the WebSocket and stop auto-renew. */
|
|
2758
2767
|
close(): void;
|
|
2768
|
+
/**
|
|
2769
|
+
* Emit loud, non-fatal warnings for the two ways a placement name silently
|
|
2770
|
+
* produces a blank mockup: (1) the name isn't one of the product's known
|
|
2771
|
+
* `placements` labels, and (2) no artboard in `state` is named after it. Each
|
|
2772
|
+
* warning goes to BOTH `console.warn` and the `onError` callback (if wired) so
|
|
2773
|
+
* a dev sees it whichever channel they watch. Never throws.
|
|
2774
|
+
*/
|
|
2775
|
+
private warnPlacement;
|
|
2759
2776
|
/**
|
|
2760
2777
|
* (Re)arm the render watchdog. Clears any prior timer and, unless disabled
|
|
2761
2778
|
* (`renderTimeoutMs <= 0`), starts a fresh one. If it fires before a mockup
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
REALTIME_WS_URL,
|
|
6
6
|
RealtimeMockupService
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-IMJKV4YO.js";
|
|
8
8
|
|
|
9
9
|
// src/validation.ts
|
|
10
10
|
import { z } from "zod";
|
|
@@ -1642,14 +1642,12 @@ function buildMockupUrl2(options, cfg) {
|
|
|
1642
1642
|
);
|
|
1643
1643
|
}
|
|
1644
1644
|
function resolveMockupBaseUrl() {
|
|
1645
|
-
const env = typeof process !== "undefined" ? process.env : void 0;
|
|
1646
1645
|
const winConfig = typeof window !== "undefined" && window.snowcone || {};
|
|
1647
|
-
return winConfig.mockupUrl || env?.SNOWCONE_IMAGE_URL || env?.NEXT_PUBLIC_MERCH_MOCKUP_URL || "https://cdn.snowcone.app";
|
|
1646
|
+
return winConfig.mockupUrl || typeof process !== "undefined" && process.env?.SNOWCONE_IMAGE_URL || typeof process !== "undefined" && process.env?.NEXT_PUBLIC_MERCH_MOCKUP_URL || "https://cdn.snowcone.app";
|
|
1648
1647
|
}
|
|
1649
1648
|
function resolveShop() {
|
|
1650
|
-
const env = typeof process !== "undefined" ? process.env : void 0;
|
|
1651
1649
|
const winConfig = typeof window !== "undefined" && window.snowcone || {};
|
|
1652
|
-
return winConfig.shop || env?.SNOWCONE_SHOP_ID || env?.NEXT_PUBLIC_SNOWCONE_SHOP_ID || "SHOP_NOT_CONFIGURED";
|
|
1650
|
+
return winConfig.shop || typeof process !== "undefined" && process.env?.SNOWCONE_SHOP_ID || typeof process !== "undefined" && process.env?.NEXT_PUBLIC_SNOWCONE_SHOP_ID || "SHOP_NOT_CONFIGURED";
|
|
1653
1651
|
}
|
|
1654
1652
|
function mockupUrl(options) {
|
|
1655
1653
|
const mockupBaseUrl = resolveMockupBaseUrl();
|
|
@@ -4714,6 +4712,20 @@ function assertVariantSelected(product) {
|
|
|
4714
4712
|
);
|
|
4715
4713
|
}
|
|
4716
4714
|
}
|
|
4715
|
+
function warnUnknownPlacement(product, placement) {
|
|
4716
|
+
const placements = product.placements;
|
|
4717
|
+
if (!placements?.length) return null;
|
|
4718
|
+
if (placements.includes(placement)) return null;
|
|
4719
|
+
return `RenderSession: placement "${placement}" doesn't match product ${product.productId}'s placements: [${placements.join(", ")}]. Your artboard name + renderState placement arg must equal the catalog placements[].label.`;
|
|
4720
|
+
}
|
|
4721
|
+
function warnPlacementArtboardMismatch(placement, state) {
|
|
4722
|
+
const artboards = state.artboards;
|
|
4723
|
+
if (!Array.isArray(artboards) || artboards.length === 0) return null;
|
|
4724
|
+
const names = artboards.map((a) => a.name).filter((n) => typeof n === "string");
|
|
4725
|
+
if (names.length === 0) return null;
|
|
4726
|
+
if (names.includes(placement)) return null;
|
|
4727
|
+
return `RenderSession: renderState placement "${placement}" has no matching artboard in the state (artboards: [${names.join(", ")}]). The artboard name must equal the placement arg.`;
|
|
4728
|
+
}
|
|
4717
4729
|
var DEFAULT_RENDER_TIMEOUT_MS = 3e4;
|
|
4718
4730
|
var RenderSession = class {
|
|
4719
4731
|
svc;
|
|
@@ -4823,6 +4835,7 @@ var RenderSession = class {
|
|
|
4823
4835
|
*/
|
|
4824
4836
|
async renderState(placement, state, throttleMs = 0) {
|
|
4825
4837
|
await this.connect();
|
|
4838
|
+
this.warnPlacement(placement, state);
|
|
4826
4839
|
this.svc.sendCanvasState(placement, state, this.product?.mockupIds.length ?? 1, throttleMs);
|
|
4827
4840
|
this.armWatchdog();
|
|
4828
4841
|
}
|
|
@@ -4835,6 +4848,7 @@ var RenderSession = class {
|
|
|
4835
4848
|
*/
|
|
4836
4849
|
async renderSavedState(placement, stateId) {
|
|
4837
4850
|
await this.connect();
|
|
4851
|
+
this.warnPlacement(placement);
|
|
4838
4852
|
this.svc.sendCanvasStateRef(placement, stateId);
|
|
4839
4853
|
this.armWatchdog();
|
|
4840
4854
|
}
|
|
@@ -4853,6 +4867,28 @@ var RenderSession = class {
|
|
|
4853
4867
|
this.svc.disconnect();
|
|
4854
4868
|
this.ready = null;
|
|
4855
4869
|
}
|
|
4870
|
+
/**
|
|
4871
|
+
* Emit loud, non-fatal warnings for the two ways a placement name silently
|
|
4872
|
+
* produces a blank mockup: (1) the name isn't one of the product's known
|
|
4873
|
+
* `placements` labels, and (2) no artboard in `state` is named after it. Each
|
|
4874
|
+
* warning goes to BOTH `console.warn` and the `onError` callback (if wired) so
|
|
4875
|
+
* a dev sees it whichever channel they watch. Never throws.
|
|
4876
|
+
*/
|
|
4877
|
+
warnPlacement(placement, state) {
|
|
4878
|
+
const warnings = [];
|
|
4879
|
+
if (this.product) {
|
|
4880
|
+
const w = warnUnknownPlacement(this.product, placement);
|
|
4881
|
+
if (w) warnings.push(w);
|
|
4882
|
+
}
|
|
4883
|
+
if (state) {
|
|
4884
|
+
const w = warnPlacementArtboardMismatch(placement, state);
|
|
4885
|
+
if (w) warnings.push(w);
|
|
4886
|
+
}
|
|
4887
|
+
for (const w of warnings) {
|
|
4888
|
+
console.warn(w);
|
|
4889
|
+
this.errorCb?.(w);
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4856
4892
|
/**
|
|
4857
4893
|
* (Re)arm the render watchdog. Clears any prior timer and, unless disabled
|
|
4858
4894
|
* (`renderTimeoutMs <= 0`), starts a fresh one. If it fires before a mockup
|
|
@@ -4968,7 +5004,12 @@ async function getProduct(idOrSlug, config) {
|
|
|
4968
5004
|
const url = `${meilisearchHost}/indexes/${meilisearchIndex}/documents/${idOrSlug}`;
|
|
4969
5005
|
const res = await f(url, { method: "GET", headers });
|
|
4970
5006
|
if (res.status === 404)
|
|
4971
|
-
throw Object.assign(
|
|
5007
|
+
throw Object.assign(
|
|
5008
|
+
new Error(
|
|
5009
|
+
`getProduct: no catalog product "${idOrSlug}" in index "${meilisearchIndex}". Pass a valid product id/slug (browse with listProducts()).`
|
|
5010
|
+
),
|
|
5011
|
+
{ code: "NOT_FOUND", productId: idOrSlug }
|
|
5012
|
+
);
|
|
4972
5013
|
if (!res.ok) throw new Error(`getProduct failed: ${res.status}`);
|
|
4973
5014
|
const raw = await res.json();
|
|
4974
5015
|
return validateProductLoose(raw);
|
package/dist/react.cjs
CHANGED
|
@@ -51,6 +51,10 @@ var RealtimeMockupService = class {
|
|
|
51
51
|
lastBlobSentAt = 0;
|
|
52
52
|
canvasBlobs = /* @__PURE__ */ new Map();
|
|
53
53
|
canvasStates = /* @__PURE__ */ new Map();
|
|
54
|
+
// Serialized JSON of the last canvas state ACTUALLY SENT per placement. Used to
|
|
55
|
+
// drop duplicate consecutive states so an idle canvas (whose onChange keeps
|
|
56
|
+
// firing identical states) doesn't stream renders and burn the realtime budget.
|
|
57
|
+
lastSentStateJson = {};
|
|
54
58
|
colors = /* @__PURE__ */ new Map();
|
|
55
59
|
lastSendTime = {};
|
|
56
60
|
throttleTimeouts = {};
|
|
@@ -71,6 +75,8 @@ var RealtimeMockupService = class {
|
|
|
71
75
|
// the WS with `?token=`, and renews ~15s before expiry via a `renew` message.
|
|
72
76
|
tokenProvider;
|
|
73
77
|
renewTimer = null;
|
|
78
|
+
isConnecting = false;
|
|
79
|
+
connectAttempt = 0;
|
|
74
80
|
setCallbacks(callbacks) {
|
|
75
81
|
this.callbacks = callbacks;
|
|
76
82
|
}
|
|
@@ -97,16 +103,21 @@ var RealtimeMockupService = class {
|
|
|
97
103
|
this.callbacks.onLog?.(message, type);
|
|
98
104
|
}
|
|
99
105
|
connect() {
|
|
100
|
-
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
106
|
+
if (this.ws?.readyState === WebSocket.OPEN || this.ws?.readyState === WebSocket.CONNECTING || this.isConnecting) {
|
|
101
107
|
return;
|
|
102
108
|
}
|
|
109
|
+
const attempt = ++this.connectAttempt;
|
|
110
|
+
this.isConnecting = true;
|
|
103
111
|
if (this.tokenProvider) {
|
|
104
112
|
this.tokenProvider().then((grant) => {
|
|
113
|
+
if (attempt !== this.connectAttempt || !this.isConnecting) return;
|
|
105
114
|
this.openSocket(grant.token);
|
|
106
115
|
this.scheduleRenew(grant.expiresAt);
|
|
107
116
|
}).catch((err) => {
|
|
117
|
+
if (attempt !== this.connectAttempt) return;
|
|
108
118
|
this.addLog(`Failed to obtain realtime grant: ${err}`);
|
|
109
119
|
this.status = "Disconnected";
|
|
120
|
+
this.isConnecting = false;
|
|
110
121
|
});
|
|
111
122
|
return;
|
|
112
123
|
}
|
|
@@ -120,6 +131,7 @@ var RealtimeMockupService = class {
|
|
|
120
131
|
console.log(`[WS] connection OPENED to ${this.wsUrl}`);
|
|
121
132
|
this.addLog("WebSocket connection opened");
|
|
122
133
|
this.status = "Connected";
|
|
134
|
+
this.isConnecting = false;
|
|
123
135
|
};
|
|
124
136
|
this.ws.onmessage = (event) => {
|
|
125
137
|
try {
|
|
@@ -138,11 +150,13 @@ var RealtimeMockupService = class {
|
|
|
138
150
|
this.isConfigured = false;
|
|
139
151
|
this.configSent = false;
|
|
140
152
|
this.clearRenew();
|
|
153
|
+
this.isConnecting = false;
|
|
141
154
|
this.callbacks.onDisconnected?.();
|
|
142
155
|
};
|
|
143
156
|
this.ws.onerror = (error) => {
|
|
144
157
|
this.addLog(`WebSocket error: ${error}`);
|
|
145
158
|
this.status = "Disconnected";
|
|
159
|
+
this.isConnecting = false;
|
|
146
160
|
};
|
|
147
161
|
}
|
|
148
162
|
scheduleRenew(expiresAt) {
|
|
@@ -292,6 +306,8 @@ var RealtimeMockupService = class {
|
|
|
292
306
|
}
|
|
293
307
|
}
|
|
294
308
|
disconnect() {
|
|
309
|
+
this.connectAttempt++;
|
|
310
|
+
this.isConnecting = false;
|
|
295
311
|
this.clearRenew();
|
|
296
312
|
if (this.ws) {
|
|
297
313
|
this.ws.close();
|
|
@@ -330,6 +346,7 @@ var RealtimeMockupService = class {
|
|
|
330
346
|
this.canvasStates.clear();
|
|
331
347
|
this.colors.clear();
|
|
332
348
|
this.lastSendTime = {};
|
|
349
|
+
this.lastSentStateJson = {};
|
|
333
350
|
Object.values(this.throttleTimeouts).forEach((timeout) => clearTimeout(timeout));
|
|
334
351
|
this.throttleTimeouts = {};
|
|
335
352
|
this.requestVersion = 0;
|
|
@@ -535,6 +552,11 @@ var RealtimeMockupService = class {
|
|
|
535
552
|
console.log(`[WS] sendCanvasStateImmediately ABORTED: ws not open`);
|
|
536
553
|
return;
|
|
537
554
|
}
|
|
555
|
+
const stateJson = JSON.stringify(state);
|
|
556
|
+
if (this.lastSentStateJson[placement] === stateJson) {
|
|
557
|
+
this.addLog(`Skipped duplicate canvas state for "${placement}" (unchanged)`, "sent");
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
538
560
|
this.lastSentVersion = ++this.requestVersion;
|
|
539
561
|
this.latestSentVersionByPlacement[placement] = this.lastSentVersion;
|
|
540
562
|
const message = JSON.stringify({
|
|
@@ -544,6 +566,7 @@ var RealtimeMockupService = class {
|
|
|
544
566
|
state
|
|
545
567
|
});
|
|
546
568
|
this.ws.send(message);
|
|
569
|
+
this.lastSentStateJson[placement] = stateJson;
|
|
547
570
|
this.lastBlobSentAt = Date.now();
|
|
548
571
|
this.addLog(`Sent canvas state for "${placement}" (${(message.length / 1024).toFixed(1)}KB, v${this.lastSentVersion})`, "sent");
|
|
549
572
|
this.callbacks.onBlobSent?.(placement);
|
package/dist/react.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as MockupResult, W as WebSocketConfig, P as PlacementSettings, F as FrameworkAdapter, b as ComponentProps, d as ComponentState, e as ComponentContext, f as ComponentLifecycleHooks, E as EventHandler, c as ComponentDescriptor, g as FrameworkUtilities } from './websocket-
|
|
1
|
+
import { M as MockupResult, W as WebSocketConfig, P as PlacementSettings, F as FrameworkAdapter, b as ComponentProps, d as ComponentState, e as ComponentContext, f as ComponentLifecycleHooks, E as EventHandler, c as ComponentDescriptor, g as FrameworkUtilities } from './websocket-DTRStLRH.cjs';
|
|
2
2
|
|
|
3
3
|
interface UseRealtimeMockupOptions {
|
|
4
4
|
wsUrl?: string;
|
package/dist/react.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as MockupResult, W as WebSocketConfig, P as PlacementSettings, F as FrameworkAdapter, b as ComponentProps, d as ComponentState, e as ComponentContext, f as ComponentLifecycleHooks, E as EventHandler, c as ComponentDescriptor, g as FrameworkUtilities } from './websocket-
|
|
1
|
+
import { M as MockupResult, W as WebSocketConfig, P as PlacementSettings, F as FrameworkAdapter, b as ComponentProps, d as ComponentState, e as ComponentContext, f as ComponentLifecycleHooks, E as EventHandler, c as ComponentDescriptor, g as FrameworkUtilities } from './websocket-DTRStLRH.js';
|
|
2
2
|
|
|
3
3
|
interface UseRealtimeMockupOptions {
|
|
4
4
|
wsUrl?: string;
|
package/dist/react.js
CHANGED
|
@@ -17,9 +17,10 @@ declare function resolveBestCombination(selection: OptionSelection, attributes:
|
|
|
17
17
|
*/
|
|
18
18
|
declare function computeDisabledChoices(selection: OptionSelection, attributes: Record<string, OptionAttribute>, combinations: Combination[]): Record<string, string[]>;
|
|
19
19
|
/**
|
|
20
|
-
* Derives a
|
|
20
|
+
* Derives a complete default selection based on the best combination.
|
|
21
21
|
* - For attributes that affect combinations, choose the value from the best combo.
|
|
22
|
-
* - For
|
|
22
|
+
* - For every remaining attribute, fall back to a default (color pickers get
|
|
23
|
+
* black; otherwise the first choice) so the returned selection is complete.
|
|
23
24
|
*/
|
|
24
25
|
declare function deriveDefaultSelection(attributes: Record<string, OptionAttribute>, combinations: Combination[]): OptionSelection;
|
|
25
26
|
/**
|
|
@@ -306,6 +307,7 @@ declare class RealtimeMockupService {
|
|
|
306
307
|
private lastBlobSentAt;
|
|
307
308
|
private canvasBlobs;
|
|
308
309
|
private canvasStates;
|
|
310
|
+
private lastSentStateJson;
|
|
309
311
|
private colors;
|
|
310
312
|
private lastSendTime;
|
|
311
313
|
private throttleTimeouts;
|
|
@@ -316,6 +318,8 @@ declare class RealtimeMockupService {
|
|
|
316
318
|
private sendVersionInBlob;
|
|
317
319
|
private tokenProvider?;
|
|
318
320
|
private renewTimer;
|
|
321
|
+
private isConnecting;
|
|
322
|
+
private connectAttempt;
|
|
319
323
|
constructor(wsUrl?: string);
|
|
320
324
|
setCallbacks(callbacks: RealtimeMockupCallbacks): void;
|
|
321
325
|
/** Provide a grant fetcher to authorize the session (per-shop, renewable). */
|
|
@@ -17,9 +17,10 @@ declare function resolveBestCombination(selection: OptionSelection, attributes:
|
|
|
17
17
|
*/
|
|
18
18
|
declare function computeDisabledChoices(selection: OptionSelection, attributes: Record<string, OptionAttribute>, combinations: Combination[]): Record<string, string[]>;
|
|
19
19
|
/**
|
|
20
|
-
* Derives a
|
|
20
|
+
* Derives a complete default selection based on the best combination.
|
|
21
21
|
* - For attributes that affect combinations, choose the value from the best combo.
|
|
22
|
-
* - For
|
|
22
|
+
* - For every remaining attribute, fall back to a default (color pickers get
|
|
23
|
+
* black; otherwise the first choice) so the returned selection is complete.
|
|
23
24
|
*/
|
|
24
25
|
declare function deriveDefaultSelection(attributes: Record<string, OptionAttribute>, combinations: Combination[]): OptionSelection;
|
|
25
26
|
/**
|
|
@@ -306,6 +307,7 @@ declare class RealtimeMockupService {
|
|
|
306
307
|
private lastBlobSentAt;
|
|
307
308
|
private canvasBlobs;
|
|
308
309
|
private canvasStates;
|
|
310
|
+
private lastSentStateJson;
|
|
309
311
|
private colors;
|
|
310
312
|
private lastSendTime;
|
|
311
313
|
private throttleTimeouts;
|
|
@@ -316,6 +318,8 @@ declare class RealtimeMockupService {
|
|
|
316
318
|
private sendVersionInBlob;
|
|
317
319
|
private tokenProvider?;
|
|
318
320
|
private renewTimer;
|
|
321
|
+
private isConnecting;
|
|
322
|
+
private connectAttempt;
|
|
319
323
|
constructor(wsUrl?: string);
|
|
320
324
|
setCallbacks(callbacks: RealtimeMockupCallbacks): void;
|
|
321
325
|
/** Provide a grant fetcher to authorize the session (per-shop, renewable). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snowcone-app/sdk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Snowcone SDK for product mockups and print-on-demand",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"merch",
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
"scripts": {
|
|
82
82
|
"build": "tsup src/index.ts src/react.ts src/dev-fetcher.ts --format esm,cjs --dts --external zod --external react",
|
|
83
83
|
"clean": "rm -rf dist",
|
|
84
|
-
"typecheck": "tsc --project ./tsconfig.json --noEmit"
|
|
84
|
+
"typecheck": "tsc --project ./tsconfig.json --noEmit",
|
|
85
|
+
"test": "vitest run"
|
|
85
86
|
}
|
|
86
87
|
}
|