@businessdash/sdk 0.9.60 → 0.9.80
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/README.md +991 -295
- package/assets/biab-sw.js +92 -0
- package/dist/account-core/index.d.ts +126 -0
- package/dist/account-core/index.d.ts.map +1 -0
- package/dist/account-core/index.js +191 -0
- package/dist/account-core/index.js.map +1 -0
- package/dist/account.d.ts +11 -0
- package/dist/account.d.ts.map +1 -0
- package/dist/account.js +11 -0
- package/dist/account.js.map +1 -0
- package/dist/auth-handler.d.ts +1 -1
- package/dist/auth-links.d.ts +33 -0
- package/dist/auth-links.d.ts.map +1 -0
- package/dist/auth-links.js +64 -0
- package/dist/auth-links.js.map +1 -0
- package/dist/blog-core/index.d.ts +112 -0
- package/dist/blog-core/index.d.ts.map +1 -0
- package/dist/blog-core/index.js +330 -0
- package/dist/blog-core/index.js.map +1 -0
- package/dist/blog.d.ts +11 -0
- package/dist/blog.d.ts.map +1 -0
- package/dist/blog.js +11 -0
- package/dist/blog.js.map +1 -0
- package/dist/chat-core/index.d.ts +97 -0
- package/dist/chat-core/index.d.ts.map +1 -0
- package/dist/chat-core/index.js +237 -0
- package/dist/chat-core/index.js.map +1 -0
- package/dist/chat.d.ts +11 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +11 -0
- package/dist/chat.js.map +1 -0
- package/dist/checkout-core/index.d.ts +65 -0
- package/dist/checkout-core/index.d.ts.map +1 -0
- package/dist/checkout-core/index.js +159 -0
- package/dist/checkout-core/index.js.map +1 -0
- package/dist/checkout.d.ts +10 -0
- package/dist/checkout.d.ts.map +1 -0
- package/dist/checkout.js +10 -0
- package/dist/checkout.js.map +1 -0
- package/dist/cli.js +133 -25
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +622 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +888 -1
- package/dist/client.js.map +1 -1
- package/dist/consent-core/index.cjs +107 -0
- package/dist/consent-core/index.d.ts +103 -0
- package/dist/consent-core/index.d.ts.map +1 -0
- package/dist/consent-core/index.js +157 -0
- package/dist/consent-core/index.js.map +1 -0
- package/dist/contracts.d.ts +3526 -767
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +571 -4
- package/dist/contracts.js.map +1 -1
- package/dist/data-model-schema.d.ts +1 -1
- package/dist/data-model-schema.d.ts.map +1 -1
- package/dist/element.d.ts +30 -0
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +56 -0
- package/dist/element.js.map +1 -1
- package/dist/embed-protocol.d.ts +86 -0
- package/dist/embed-protocol.d.ts.map +1 -0
- package/dist/embed-protocol.js +60 -0
- package/dist/embed-protocol.js.map +1 -0
- package/dist/framework-support.d.ts +122 -0
- package/dist/framework-support.d.ts.map +1 -0
- package/dist/framework-support.js +323 -0
- package/dist/framework-support.js.map +1 -0
- package/dist/index.cjs +1355 -24
- package/dist/legal-core/index.cjs +115 -0
- package/dist/legal-core/index.d.ts +160 -0
- package/dist/legal-core/index.d.ts.map +1 -0
- package/dist/legal-core/index.js +220 -0
- package/dist/legal-core/index.js.map +1 -0
- package/dist/marketing-core/index.d.ts +66 -0
- package/dist/marketing-core/index.d.ts.map +1 -0
- package/dist/marketing-core/index.js +154 -0
- package/dist/marketing-core/index.js.map +1 -0
- package/dist/marketing.d.ts +7 -0
- package/dist/marketing.d.ts.map +1 -0
- package/dist/marketing.js +7 -0
- package/dist/marketing.js.map +1 -0
- package/dist/mcp-adapters/index.d.ts +135 -0
- package/dist/mcp-adapters/index.d.ts.map +1 -0
- package/dist/mcp-adapters/index.js +187 -0
- package/dist/mcp-adapters/index.js.map +1 -0
- package/dist/mcp-adapters.d.ts +7 -0
- package/dist/mcp-adapters.d.ts.map +1 -0
- package/dist/mcp-adapters.js +7 -0
- package/dist/mcp-adapters.js.map +1 -0
- package/dist/model3d-core/index.cjs +95 -0
- package/dist/model3d-core/index.d.ts +167 -0
- package/dist/model3d-core/index.d.ts.map +1 -0
- package/dist/model3d-core/index.js +158 -0
- package/dist/model3d-core/index.js.map +1 -0
- package/dist/notifications-core/index.cjs +315 -0
- package/dist/notifications-core/index.d.ts +229 -0
- package/dist/notifications-core/index.d.ts.map +1 -0
- package/dist/notifications-core/index.js +445 -0
- package/dist/notifications-core/index.js.map +1 -0
- package/dist/portal-core/index.d.ts +451 -0
- package/dist/portal-core/index.d.ts.map +1 -0
- package/dist/portal-core/index.js +819 -0
- package/dist/portal-core/index.js.map +1 -0
- package/dist/portal.d.ts +7 -0
- package/dist/portal.d.ts.map +1 -0
- package/dist/portal.js +7 -0
- package/dist/portal.js.map +1 -0
- package/dist/push-core/index.cjs +171 -0
- package/dist/push-core/index.d.ts +149 -0
- package/dist/push-core/index.d.ts.map +1 -0
- package/dist/push-core/index.js +231 -0
- package/dist/push-core/index.js.map +1 -0
- package/dist/react-marketing.d.ts +1 -17
- package/dist/react-marketing.d.ts.map +1 -1
- package/dist/react-marketing.js +4 -0
- package/dist/react-marketing.js.map +1 -1
- package/dist/react.d.ts +294 -101
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +302 -246
- package/dist/react.js.map +1 -1
- package/dist/seed-core/index.d.ts +354 -0
- package/dist/seed-core/index.d.ts.map +1 -0
- package/dist/seed-core/index.js +420 -0
- package/dist/seed-core/index.js.map +1 -0
- package/dist/seed.d.ts +7 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +7 -0
- package/dist/seed.js.map +1 -0
- package/dist/seo-core/blog.d.ts +94 -0
- package/dist/seo-core/blog.d.ts.map +1 -0
- package/dist/seo-core/blog.js +149 -0
- package/dist/seo-core/blog.js.map +1 -0
- package/dist/seo-core/commerce.d.ts +64 -0
- package/dist/seo-core/commerce.d.ts.map +1 -0
- package/dist/seo-core/commerce.js +87 -0
- package/dist/seo-core/commerce.js.map +1 -0
- package/dist/seo-core/index.cjs +751 -0
- package/dist/seo-core/index.d.ts +226 -0
- package/dist/seo-core/index.d.ts.map +1 -0
- package/dist/seo-core/index.js +455 -0
- package/dist/seo-core/index.js.map +1 -0
- package/dist/seo-core/local.d.ts +181 -0
- package/dist/seo-core/local.d.ts.map +1 -0
- package/dist/seo-core/local.js +193 -0
- package/dist/seo-core/local.js.map +1 -0
- package/dist/sitemap-core/index.cjs +167 -0
- package/dist/sitemap-core/index.d.ts +155 -0
- package/dist/sitemap-core/index.d.ts.map +1 -0
- package/dist/sitemap-core/index.js +195 -0
- package/dist/sitemap-core/index.js.map +1 -0
- package/dist/social-core/index.d.ts +74 -0
- package/dist/social-core/index.d.ts.map +1 -0
- package/dist/social-core/index.js +72 -0
- package/dist/social-core/index.js.map +1 -0
- package/dist/social.d.ts +7 -0
- package/dist/social.d.ts.map +1 -0
- package/dist/social.js +7 -0
- package/dist/social.js.map +1 -0
- package/dist/solid-compat.d.ts +204 -0
- package/dist/solid-compat.d.ts.map +1 -0
- package/dist/solid-compat.js +221 -0
- package/dist/solid-compat.js.map +1 -0
- package/dist/store-core/index.d.ts +141 -0
- package/dist/store-core/index.d.ts.map +1 -0
- package/dist/store-core/index.js +277 -0
- package/dist/store-core/index.js.map +1 -0
- package/dist/store.d.ts +12 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +12 -0
- package/dist/store.js.map +1 -0
- package/dist/vanilla.d.ts +111 -0
- package/dist/vanilla.d.ts.map +1 -1
- package/dist/vanilla.js +206 -0
- package/dist/vanilla.js.map +1 -1
- package/dist/verify-core/index.d.ts +98 -0
- package/dist/verify-core/index.d.ts.map +1 -0
- package/dist/verify-core/index.js +186 -0
- package/dist/verify-core/index.js.map +1 -0
- package/dist/verify.d.ts +8 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +8 -0
- package/dist/verify.js.map +1 -0
- package/openapi.json +4516 -1311
- package/package.json +128 -11
- package/src/account-core/index.ts +309 -0
- package/src/account.ts +11 -0
- package/src/angular/headless.ts +519 -0
- package/src/angular/index.ts +3 -0
- package/src/auth-links.ts +76 -0
- package/src/blog-core/index.ts +423 -0
- package/src/blog.ts +11 -0
- package/src/chat-core/index.ts +335 -0
- package/src/chat.ts +11 -0
- package/src/checkout-core/index.ts +225 -0
- package/src/checkout.ts +10 -0
- package/src/cli.ts +184 -28
- package/src/client.ts +1097 -0
- package/src/consent-core/index.ts +213 -0
- package/src/contracts.ts +768 -11
- package/src/data-model-schema.ts +1 -1
- package/src/element.ts +102 -0
- package/src/embed-protocol.ts +100 -0
- package/src/framework-support.ts +440 -0
- package/src/legal-core/index.ts +276 -0
- package/src/marketing-core/index.ts +209 -0
- package/src/marketing.ts +7 -0
- package/src/mcp-adapters/index.ts +264 -0
- package/src/mcp-adapters.ts +7 -0
- package/src/model3d-core/index.ts +248 -0
- package/src/notifications-core/index.ts +650 -0
- package/src/portal-core/index.ts +1564 -0
- package/src/portal.ts +7 -0
- package/src/push-core/index.ts +335 -0
- package/src/qwik.tsx +602 -2
- package/src/react-marketing.tsx +5 -0
- package/src/react.tsx +434 -319
- package/src/seed-core/index.ts +735 -0
- package/src/seed.ts +7 -0
- package/src/seo-core/blog.ts +216 -0
- package/src/seo-core/commerce.ts +138 -0
- package/src/seo-core/index.ts +605 -0
- package/src/seo-core/local.ts +333 -0
- package/src/sitemap-core/index.ts +296 -0
- package/src/social-core/index.ts +103 -0
- package/src/social.ts +7 -0
- package/src/solid-compat.tsx +347 -0
- package/src/solid-surfaces.tsx +1311 -0
- package/src/solid.tsx +82 -60
- package/src/store-core/index.ts +426 -0
- package/src/store.ts +12 -0
- package/src/svelte/SocialLinks.svelte +84 -0
- package/src/svelte/headless.ts +435 -0
- package/src/svelte/index.ts +9 -0
- package/src/vanilla.ts +367 -0
- package/src/verify-core/index.ts +288 -0
- package/src/verify.ts +8 -0
- package/src/vue/headless.ts +527 -0
- package/src/vue/index.ts +3 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@businessdash/sdk/model3d` — 3D product models on the org's own site.
|
|
3
|
+
*
|
|
4
|
+
* ## This ships no renderer, and that is the point
|
|
5
|
+
*
|
|
6
|
+
* A three.js build is ~600KB before your model. `<model-viewer>` is ~300KB. An
|
|
7
|
+
* SDK that pulled either down on every product page would be an SDK people rip
|
|
8
|
+
* out, and it would make the choice of engine ours rather than yours.
|
|
9
|
+
*
|
|
10
|
+
* So this decides WHAT to show and hands you the facts. You mount whichever
|
|
11
|
+
* viewer you already use, or none.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* const plan = planProductModel(product);
|
|
15
|
+
* if (plan.ar) showArLink(plan.ar.url); // zero JS, native iOS
|
|
16
|
+
* if (plan.viewer) mountYourViewer(plan.viewer.url, plan.viewer.format);
|
|
17
|
+
* if (plan.download) showLink(plan.download.url, plan.download.label);
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* ## The three formats do genuinely different jobs
|
|
21
|
+
*
|
|
22
|
+
* **AR Quick Look (`.usdz`)** — the one worth having. On iOS, iPadOS and
|
|
23
|
+
* visionOS Safari an `<a rel="ar">` opens the model in the room, in the camera,
|
|
24
|
+
* with no JavaScript at all. No library, no canvas, no WebGL. Where it applies
|
|
25
|
+
* it beats an embedded viewer on every axis, so it is checked first.
|
|
26
|
+
*
|
|
27
|
+
* **Web viewer (`.glb`/`.gltf`/`.obj`/`.stl`)** — the rotatable canvas. `.glb`
|
|
28
|
+
* carries its own materials and is what you want; `.obj` and `.stl` are
|
|
29
|
+
* geometry only and render in a neutral grey, which is fine for a machined part
|
|
30
|
+
* and disappointing for a sofa.
|
|
31
|
+
*
|
|
32
|
+
* **CAD download (`.step`/`.stp`/`.iges`)** — NOT renderable, deliberately.
|
|
33
|
+
* STEP is a boundary-representation format needing a geometry kernel; the
|
|
34
|
+
* browser WASM builds of OpenCascade that can tessellate it are 10–30MB, which
|
|
35
|
+
* is not something to pull down for a product photo. It is also the wrong shape
|
|
36
|
+
* for who asks: an engineer evaluating a bracket wants the file in their own CAD
|
|
37
|
+
* package, not a spinning preview.
|
|
38
|
+
*
|
|
39
|
+
* ## Absent is the normal case
|
|
40
|
+
*
|
|
41
|
+
* These fields are stripped unless the org holds the `ecommerce.product_3d`
|
|
42
|
+
* add-on, and most businesses never attach a model. Every function here treats
|
|
43
|
+
* missing as ordinary and returns nothing to render.
|
|
44
|
+
*
|
|
45
|
+
* @module
|
|
46
|
+
*/
|
|
47
|
+
/** The product fields this reads. Anything with these keys works. */
|
|
48
|
+
export type ModelBearingProduct = {
|
|
49
|
+
id?: string;
|
|
50
|
+
model3dUrl?: string | null;
|
|
51
|
+
model3dUsdzUrl?: string | null;
|
|
52
|
+
/** The API reports availability, never the URL. See `download` below. */
|
|
53
|
+
model3dCadAvailable?: boolean;
|
|
54
|
+
model3dCadName?: string | null;
|
|
55
|
+
};
|
|
56
|
+
/** What a web viewer can load. */
|
|
57
|
+
export type WebModelFormat = "glb" | "gltf" | "obj" | "stl";
|
|
58
|
+
/** What a CAD download might be. */
|
|
59
|
+
export type CadFormat = "step" | "iges" | "native";
|
|
60
|
+
export type ModelPlan = {
|
|
61
|
+
/**
|
|
62
|
+
* Present when the model can be placed in the room on this device.
|
|
63
|
+
*
|
|
64
|
+
* Only on Apple platforms, and only with a `.usdz`. Render as
|
|
65
|
+
* `<a rel="ar" href={url}><img …/></a>` — the `<img>` child is required by
|
|
66
|
+
* Quick Look, and the link needs no JavaScript whatsoever.
|
|
67
|
+
*/
|
|
68
|
+
ar: {
|
|
69
|
+
url: string;
|
|
70
|
+
} | null;
|
|
71
|
+
/**
|
|
72
|
+
* Present when there is a model a WebGL viewer can load.
|
|
73
|
+
*
|
|
74
|
+
* `neutralMaterial` is true for `.obj` and `.stl`, which carry geometry but
|
|
75
|
+
* no materials — worth knowing, because a viewer that lights them like a
|
|
76
|
+
* `.glb` renders a black silhouette.
|
|
77
|
+
*/
|
|
78
|
+
viewer: {
|
|
79
|
+
url: string;
|
|
80
|
+
format: WebModelFormat;
|
|
81
|
+
neutralMaterial: boolean;
|
|
82
|
+
} | null;
|
|
83
|
+
/**
|
|
84
|
+
* Present when the org attached a CAD file.
|
|
85
|
+
*
|
|
86
|
+
* `url` points at the platform's gated endpoint, never at the file — that
|
|
87
|
+
* route requires cookies, rate-limits the caller and re-checks the
|
|
88
|
+
* entitlement before handing anything over. Render it as an ordinary link;
|
|
89
|
+
* the visitor's browser sends its cookie automatically.
|
|
90
|
+
*
|
|
91
|
+
* `format` is inferred from the filename when the org supplied one, so a
|
|
92
|
+
* page can say "STEP" rather than "CAD" without a round trip.
|
|
93
|
+
*/
|
|
94
|
+
download: {
|
|
95
|
+
url: string;
|
|
96
|
+
label: string;
|
|
97
|
+
format: CadFormat;
|
|
98
|
+
} | null;
|
|
99
|
+
/** True when there is nothing 3D at all — the common case. */
|
|
100
|
+
empty: boolean;
|
|
101
|
+
};
|
|
102
|
+
/** Which web viewer format a URL is, if any. */
|
|
103
|
+
export declare function webModelFormat(url: string | null | undefined): WebModelFormat | null;
|
|
104
|
+
/** Which CAD family a URL belongs to. Unknown extensions are `native`. */
|
|
105
|
+
export declare function cadFormat(url: string | null | undefined): CadFormat | null;
|
|
106
|
+
/**
|
|
107
|
+
* Does THIS browser support AR Quick Look?
|
|
108
|
+
*
|
|
109
|
+
* Feature-detected rather than sniffed: Safari advertises support by
|
|
110
|
+
* recognising `rel="ar"` on an anchor. That is Apple's own documented check and
|
|
111
|
+
* it survives the next user-agent change, which a regex on "iPhone" does not.
|
|
112
|
+
*
|
|
113
|
+
* False on the server, so a plan built during SSR omits the AR branch. Re-plan
|
|
114
|
+
* on the client if you want the link to appear without a navigation — the check
|
|
115
|
+
* is a few microseconds.
|
|
116
|
+
*/
|
|
117
|
+
export declare function supportsArQuickLook(): boolean;
|
|
118
|
+
export type PlanProductModelOptions = {
|
|
119
|
+
/**
|
|
120
|
+
* Override the AR capability check.
|
|
121
|
+
*
|
|
122
|
+
* Pass `true` during SSR when you know the request came from an Apple
|
|
123
|
+
* device, or `false` to suppress AR entirely.
|
|
124
|
+
*/
|
|
125
|
+
arSupported?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Where your app proxies `GET storefront/products/{id}/cad`.
|
|
128
|
+
*
|
|
129
|
+
* Required for `download` to appear, and required to be YOUR route rather
|
|
130
|
+
* than the platform's: the API key is a server secret, so the browser
|
|
131
|
+
* cannot call the platform endpoint directly. Proxy it and pass your own
|
|
132
|
+
* path — `cadDownloadUrl: `/api/products/${product.id}/cad``.
|
|
133
|
+
*
|
|
134
|
+
* Without it the plan reports no download even when a file exists, which is
|
|
135
|
+
* the safe direction: a dead link is worse than an absent one.
|
|
136
|
+
*/
|
|
137
|
+
cadDownloadUrl?: string;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Decide what to render for one product.
|
|
141
|
+
*
|
|
142
|
+
* AR is not exclusive with the viewer — a product can offer both, and on an
|
|
143
|
+
* iPhone the usual arrangement is a canvas with a "View in your room" button
|
|
144
|
+
* over it. The plan reports each independently rather than picking for you.
|
|
145
|
+
*/
|
|
146
|
+
export declare function planProductModel(product: ModelBearingProduct, options?: PlanProductModelOptions): ModelPlan;
|
|
147
|
+
/**
|
|
148
|
+
* The attributes an AR Quick Look anchor needs.
|
|
149
|
+
*
|
|
150
|
+
* Spread onto your own `<a>`. Quick Look requires an `<img>` or equivalent
|
|
151
|
+
* child — an empty anchor silently does nothing, which is the single most
|
|
152
|
+
* common way this gets implemented wrong.
|
|
153
|
+
*/
|
|
154
|
+
export declare function arLinkAttributes(url: string): {
|
|
155
|
+
href: string;
|
|
156
|
+
rel: string;
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* True when the org has the 3D add-on, judged from a product payload.
|
|
160
|
+
*
|
|
161
|
+
* The API omits these keys entirely without `ecommerce.product_3d`, so their
|
|
162
|
+
* absence distinguishes "not paid for" from "paid for, none attached" — where
|
|
163
|
+
* the key is present and null. Useful for deciding whether to show an upsell
|
|
164
|
+
* rather than nothing.
|
|
165
|
+
*/
|
|
166
|
+
export declare function hasModel3dEntitlement(product: object): boolean;
|
|
167
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/model3d-core/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,qEAAqE;AACrE,MAAM,MAAM,mBAAmB,GAAG;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,yEAAyE;IACzE,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,kCAAkC;AAClC,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;AAE5D,oCAAoC;AACpC,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEnD,MAAM,MAAM,SAAS,GAAG;IACvB;;;;;;OAMG;IACH,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3B;;;;;;OAMG;IACH,MAAM,EAAE;QACP,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,cAAc,CAAC;QACvB,eAAe,EAAE,OAAO,CAAC;KACzB,GAAG,IAAI,CAAC;IACT;;;;;;;;;;OAUG;IACH,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,SAAS,CAAA;KAAE,GAAG,IAAI,CAAC;IACnE,8DAA8D;IAC9D,KAAK,EAAE,OAAO,CAAC;CACf,CAAC;AAUF,gDAAgD;AAChD,wBAAgB,cAAc,CAC7B,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC5B,cAAc,GAAG,IAAI,CAMvB;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,GAAG,IAAI,CAM1E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAI7C;AAED,MAAM,MAAM,uBAAuB,GAAG;IACrC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,mBAAmB,EAC5B,OAAO,GAAE,uBAA4B,GACnC,SAAS,CAkCX;AAQD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACZ,CAEA;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAM9D"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@businessdash/sdk/model3d` — 3D product models on the org's own site.
|
|
3
|
+
*
|
|
4
|
+
* ## This ships no renderer, and that is the point
|
|
5
|
+
*
|
|
6
|
+
* A three.js build is ~600KB before your model. `<model-viewer>` is ~300KB. An
|
|
7
|
+
* SDK that pulled either down on every product page would be an SDK people rip
|
|
8
|
+
* out, and it would make the choice of engine ours rather than yours.
|
|
9
|
+
*
|
|
10
|
+
* So this decides WHAT to show and hands you the facts. You mount whichever
|
|
11
|
+
* viewer you already use, or none.
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* const plan = planProductModel(product);
|
|
15
|
+
* if (plan.ar) showArLink(plan.ar.url); // zero JS, native iOS
|
|
16
|
+
* if (plan.viewer) mountYourViewer(plan.viewer.url, plan.viewer.format);
|
|
17
|
+
* if (plan.download) showLink(plan.download.url, plan.download.label);
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* ## The three formats do genuinely different jobs
|
|
21
|
+
*
|
|
22
|
+
* **AR Quick Look (`.usdz`)** — the one worth having. On iOS, iPadOS and
|
|
23
|
+
* visionOS Safari an `<a rel="ar">` opens the model in the room, in the camera,
|
|
24
|
+
* with no JavaScript at all. No library, no canvas, no WebGL. Where it applies
|
|
25
|
+
* it beats an embedded viewer on every axis, so it is checked first.
|
|
26
|
+
*
|
|
27
|
+
* **Web viewer (`.glb`/`.gltf`/`.obj`/`.stl`)** — the rotatable canvas. `.glb`
|
|
28
|
+
* carries its own materials and is what you want; `.obj` and `.stl` are
|
|
29
|
+
* geometry only and render in a neutral grey, which is fine for a machined part
|
|
30
|
+
* and disappointing for a sofa.
|
|
31
|
+
*
|
|
32
|
+
* **CAD download (`.step`/`.stp`/`.iges`)** — NOT renderable, deliberately.
|
|
33
|
+
* STEP is a boundary-representation format needing a geometry kernel; the
|
|
34
|
+
* browser WASM builds of OpenCascade that can tessellate it are 10–30MB, which
|
|
35
|
+
* is not something to pull down for a product photo. It is also the wrong shape
|
|
36
|
+
* for who asks: an engineer evaluating a bracket wants the file in their own CAD
|
|
37
|
+
* package, not a spinning preview.
|
|
38
|
+
*
|
|
39
|
+
* ## Absent is the normal case
|
|
40
|
+
*
|
|
41
|
+
* These fields are stripped unless the org holds the `ecommerce.product_3d`
|
|
42
|
+
* add-on, and most businesses never attach a model. Every function here treats
|
|
43
|
+
* missing as ordinary and returns nothing to render.
|
|
44
|
+
*
|
|
45
|
+
* @module
|
|
46
|
+
*/
|
|
47
|
+
/** Lowercased extension, ignoring query strings and fragments. */
|
|
48
|
+
function extensionOf(url) {
|
|
49
|
+
const path = url.split(/[?#]/)[0] ?? "";
|
|
50
|
+
const last = path.slice(path.lastIndexOf("/") + 1);
|
|
51
|
+
const dot = last.lastIndexOf(".");
|
|
52
|
+
return dot === -1 ? "" : last.slice(dot + 1).toLowerCase();
|
|
53
|
+
}
|
|
54
|
+
/** Which web viewer format a URL is, if any. */
|
|
55
|
+
export function webModelFormat(url) {
|
|
56
|
+
if (!url)
|
|
57
|
+
return null;
|
|
58
|
+
const ext = extensionOf(url);
|
|
59
|
+
return ext === "glb" || ext === "gltf" || ext === "obj" || ext === "stl"
|
|
60
|
+
? ext
|
|
61
|
+
: null;
|
|
62
|
+
}
|
|
63
|
+
/** Which CAD family a URL belongs to. Unknown extensions are `native`. */
|
|
64
|
+
export function cadFormat(url) {
|
|
65
|
+
if (!url)
|
|
66
|
+
return null;
|
|
67
|
+
const ext = extensionOf(url);
|
|
68
|
+
if (ext === "step" || ext === "stp")
|
|
69
|
+
return "step";
|
|
70
|
+
if (ext === "iges" || ext === "igs")
|
|
71
|
+
return "iges";
|
|
72
|
+
return ext ? "native" : null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Does THIS browser support AR Quick Look?
|
|
76
|
+
*
|
|
77
|
+
* Feature-detected rather than sniffed: Safari advertises support by
|
|
78
|
+
* recognising `rel="ar"` on an anchor. That is Apple's own documented check and
|
|
79
|
+
* it survives the next user-agent change, which a regex on "iPhone" does not.
|
|
80
|
+
*
|
|
81
|
+
* False on the server, so a plan built during SSR omits the AR branch. Re-plan
|
|
82
|
+
* on the client if you want the link to appear without a navigation — the check
|
|
83
|
+
* is a few microseconds.
|
|
84
|
+
*/
|
|
85
|
+
export function supportsArQuickLook() {
|
|
86
|
+
if (typeof document === "undefined")
|
|
87
|
+
return false;
|
|
88
|
+
const anchor = document.createElement("a");
|
|
89
|
+
return Boolean(anchor.relList?.supports?.("ar"));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Decide what to render for one product.
|
|
93
|
+
*
|
|
94
|
+
* AR is not exclusive with the viewer — a product can offer both, and on an
|
|
95
|
+
* iPhone the usual arrangement is a canvas with a "View in your room" button
|
|
96
|
+
* over it. The plan reports each independently rather than picking for you.
|
|
97
|
+
*/
|
|
98
|
+
export function planProductModel(product, options = {}) {
|
|
99
|
+
const arSupported = options.arSupported ?? supportsArQuickLook();
|
|
100
|
+
const usdz = product.model3dUsdzUrl?.trim() || null;
|
|
101
|
+
const ar = arSupported && usdz ? { url: usdz } : null;
|
|
102
|
+
const viewerUrl = product.model3dUrl?.trim() || null;
|
|
103
|
+
const format = webModelFormat(viewerUrl);
|
|
104
|
+
const viewer = viewerUrl && format
|
|
105
|
+
? {
|
|
106
|
+
url: viewerUrl,
|
|
107
|
+
format,
|
|
108
|
+
// .obj and .stl are geometry only. A viewer lighting them like
|
|
109
|
+
// a .glb renders a black silhouette.
|
|
110
|
+
neutralMaterial: format === "obj" || format === "stl",
|
|
111
|
+
}
|
|
112
|
+
: null;
|
|
113
|
+
const cadName = product.model3dCadName?.trim() || null;
|
|
114
|
+
// Inferred from the filename, since the URL is never in the payload. Falls
|
|
115
|
+
// back to `native`, which only changes the default label.
|
|
116
|
+
const cad = product.model3dCadAvailable
|
|
117
|
+
? (cadFormat(cadName) ?? "native")
|
|
118
|
+
: null;
|
|
119
|
+
const download = cad && options.cadDownloadUrl
|
|
120
|
+
? {
|
|
121
|
+
url: options.cadDownloadUrl,
|
|
122
|
+
label: cadName || defaultCadLabel(cad),
|
|
123
|
+
format: cad,
|
|
124
|
+
}
|
|
125
|
+
: null;
|
|
126
|
+
return { ar, viewer, download, empty: !ar && !viewer && !download && !usdz };
|
|
127
|
+
}
|
|
128
|
+
function defaultCadLabel(format) {
|
|
129
|
+
if (format === "step")
|
|
130
|
+
return "Download STEP file";
|
|
131
|
+
if (format === "iges")
|
|
132
|
+
return "Download IGES file";
|
|
133
|
+
return "Download CAD file";
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The attributes an AR Quick Look anchor needs.
|
|
137
|
+
*
|
|
138
|
+
* Spread onto your own `<a>`. Quick Look requires an `<img>` or equivalent
|
|
139
|
+
* child — an empty anchor silently does nothing, which is the single most
|
|
140
|
+
* common way this gets implemented wrong.
|
|
141
|
+
*/
|
|
142
|
+
export function arLinkAttributes(url) {
|
|
143
|
+
return { href: url, rel: "ar" };
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* True when the org has the 3D add-on, judged from a product payload.
|
|
147
|
+
*
|
|
148
|
+
* The API omits these keys entirely without `ecommerce.product_3d`, so their
|
|
149
|
+
* absence distinguishes "not paid for" from "paid for, none attached" — where
|
|
150
|
+
* the key is present and null. Useful for deciding whether to show an upsell
|
|
151
|
+
* rather than nothing.
|
|
152
|
+
*/
|
|
153
|
+
export function hasModel3dEntitlement(product) {
|
|
154
|
+
return ("model3dUrl" in product ||
|
|
155
|
+
"model3dUsdzUrl" in product ||
|
|
156
|
+
"model3dCadAvailable" in product);
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/model3d-core/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/notifications-core/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
createNotificationFeed: () => createNotificationFeed,
|
|
24
|
+
portalSource: () => portalSource,
|
|
25
|
+
pushSource: () => pushSource,
|
|
26
|
+
toToast: () => toToast
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
function dedupeKey(input) {
|
|
30
|
+
if (input.id) return input.id;
|
|
31
|
+
const entity = typeof input.data?.entityId === "string" ? input.data.entityId : "";
|
|
32
|
+
return [input.kind ?? "unknown", input.orgId ?? "", entity, input.title].join(
|
|
33
|
+
"|"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
function createNotificationFeed(options = {}) {
|
|
37
|
+
const handlers = /* @__PURE__ */ new Set();
|
|
38
|
+
const history = [];
|
|
39
|
+
const seen = /* @__PURE__ */ new Map();
|
|
40
|
+
const teardowns = [];
|
|
41
|
+
const dedupeWindowMs = options.dedupeWindowMs ?? 5 * 60 * 1e3;
|
|
42
|
+
const historyLimit = options.historyLimit ?? 50;
|
|
43
|
+
let running = false;
|
|
44
|
+
let destroyed = false;
|
|
45
|
+
function fail(cause) {
|
|
46
|
+
options.onError?.(
|
|
47
|
+
cause instanceof Error ? cause : new Error(String(cause))
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
function deliver(input) {
|
|
51
|
+
if (destroyed) return;
|
|
52
|
+
const key = dedupeKey(input);
|
|
53
|
+
const now = Date.now();
|
|
54
|
+
for (const [id, at] of seen) {
|
|
55
|
+
if (now - at > dedupeWindowMs) seen.delete(id);
|
|
56
|
+
}
|
|
57
|
+
if (seen.has(key)) return;
|
|
58
|
+
seen.set(key, now);
|
|
59
|
+
const notification = {
|
|
60
|
+
id: input.id ?? key,
|
|
61
|
+
source: input.source ?? "local",
|
|
62
|
+
category: input.category ?? "transactional",
|
|
63
|
+
kind: input.kind ?? "notification",
|
|
64
|
+
title: input.title,
|
|
65
|
+
body: input.body ?? null,
|
|
66
|
+
url: input.url ?? null,
|
|
67
|
+
severity: input.severity ?? "info",
|
|
68
|
+
occurredAt: input.occurredAt ?? new Date(now).toISOString(),
|
|
69
|
+
orgId: input.orgId ?? null,
|
|
70
|
+
data: input.data ?? {}
|
|
71
|
+
};
|
|
72
|
+
history.unshift(notification);
|
|
73
|
+
if (history.length > historyLimit) history.length = historyLimit;
|
|
74
|
+
for (const handler of handlers) {
|
|
75
|
+
try {
|
|
76
|
+
handler(notification);
|
|
77
|
+
} catch (cause) {
|
|
78
|
+
fail(cause);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
subscribe(handler) {
|
|
84
|
+
handlers.add(handler);
|
|
85
|
+
return () => {
|
|
86
|
+
handlers.delete(handler);
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
emit(input) {
|
|
90
|
+
deliver({ ...input, source: input.source ?? "local" });
|
|
91
|
+
},
|
|
92
|
+
start() {
|
|
93
|
+
if (running || destroyed) return;
|
|
94
|
+
running = true;
|
|
95
|
+
const sources = [
|
|
96
|
+
pushSource({
|
|
97
|
+
...options.closeOsNotificationWhenFocused === void 0 ? {} : {
|
|
98
|
+
closeOsNotificationWhenFocused: options.closeOsNotificationWhenFocused
|
|
99
|
+
},
|
|
100
|
+
onError: fail
|
|
101
|
+
}),
|
|
102
|
+
...options.portal ? [
|
|
103
|
+
portalSource({
|
|
104
|
+
portal: options.portal,
|
|
105
|
+
...options.pollIntervalMs === void 0 ? {} : { intervalMs: options.pollIntervalMs }
|
|
106
|
+
})
|
|
107
|
+
] : [],
|
|
108
|
+
...options.sources ?? []
|
|
109
|
+
];
|
|
110
|
+
for (const source of sources) {
|
|
111
|
+
try {
|
|
112
|
+
teardowns.push(source(deliver));
|
|
113
|
+
} catch (cause) {
|
|
114
|
+
fail(cause);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
stop() {
|
|
119
|
+
running = false;
|
|
120
|
+
while (teardowns.length) {
|
|
121
|
+
try {
|
|
122
|
+
teardowns.pop()?.();
|
|
123
|
+
} catch (cause) {
|
|
124
|
+
fail(cause);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
recent() {
|
|
129
|
+
return [...history];
|
|
130
|
+
},
|
|
131
|
+
destroy() {
|
|
132
|
+
this.stop();
|
|
133
|
+
destroyed = true;
|
|
134
|
+
handlers.clear();
|
|
135
|
+
history.length = 0;
|
|
136
|
+
seen.clear();
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function pushSource(options = {}) {
|
|
141
|
+
return (emit) => {
|
|
142
|
+
if (typeof navigator === "undefined" || !("serviceWorker" in navigator) || typeof window === "undefined") {
|
|
143
|
+
return () => void 0;
|
|
144
|
+
}
|
|
145
|
+
const listener = (event) => {
|
|
146
|
+
const message = event.data;
|
|
147
|
+
if (message?.type !== "businessdash:push" || !message.payload) return;
|
|
148
|
+
const payload = message.payload;
|
|
149
|
+
const data = typeof payload.data === "object" && payload.data !== null ? payload.data : {};
|
|
150
|
+
emit({
|
|
151
|
+
...typeof payload.id === "string" ? { id: payload.id } : {},
|
|
152
|
+
source: "push",
|
|
153
|
+
...typeof payload.category === "string" ? { category: payload.category } : {},
|
|
154
|
+
...typeof payload.kind === "string" ? { kind: payload.kind } : {},
|
|
155
|
+
title: typeof payload.title === "string" ? payload.title : "Notification",
|
|
156
|
+
body: typeof payload.body === "string" ? payload.body : null,
|
|
157
|
+
url: typeof payload.url === "string" ? payload.url : null,
|
|
158
|
+
...typeof payload.orgId === "string" ? { orgId: payload.orgId } : {},
|
|
159
|
+
...typeof payload.occurredAt === "string" ? { occurredAt: payload.occurredAt } : {},
|
|
160
|
+
data
|
|
161
|
+
});
|
|
162
|
+
if (options.closeOsNotificationWhenFocused && document.visibilityState === "visible") {
|
|
163
|
+
navigator.serviceWorker.ready.then(
|
|
164
|
+
(registration) => registration.getNotifications(
|
|
165
|
+
typeof payload.tag === "string" ? { tag: payload.tag } : {}
|
|
166
|
+
)
|
|
167
|
+
).then((notifications) => {
|
|
168
|
+
for (const notification of notifications) notification.close();
|
|
169
|
+
}).catch((cause) => options.onError?.(cause));
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
navigator.serviceWorker.addEventListener("message", listener);
|
|
173
|
+
return () => {
|
|
174
|
+
navigator.serviceWorker.removeEventListener("message", listener);
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function portalSource(options) {
|
|
179
|
+
const intervalMs = Math.max(15e3, options.intervalMs ?? 6e4);
|
|
180
|
+
return (emit) => {
|
|
181
|
+
let stopped = false;
|
|
182
|
+
let baseline = null;
|
|
183
|
+
let timer = null;
|
|
184
|
+
function readWatermark() {
|
|
185
|
+
const snapshot = options.portal.snapshot();
|
|
186
|
+
return {
|
|
187
|
+
unreadMessages: snapshot.unreadMessageCount,
|
|
188
|
+
orderStatuses: new Map(
|
|
189
|
+
(snapshot.orders?.items ?? []).map((o) => [o.id, String(o.status)])
|
|
190
|
+
),
|
|
191
|
+
invoiceStatuses: new Map(
|
|
192
|
+
(snapshot.invoices?.items ?? []).map((i) => [i.id, String(i.status)])
|
|
193
|
+
),
|
|
194
|
+
shipmentStatuses: new Map(
|
|
195
|
+
(snapshot.shipments?.items ?? []).map((sh) => [
|
|
196
|
+
sh.id,
|
|
197
|
+
String(sh.status ?? "unknown")
|
|
198
|
+
])
|
|
199
|
+
)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function diff(previous, next) {
|
|
203
|
+
const orgId = options.portal.snapshot().context?.organization?.id ?? null;
|
|
204
|
+
if (next.unreadMessages > previous.unreadMessages) {
|
|
205
|
+
const delta = next.unreadMessages - previous.unreadMessages;
|
|
206
|
+
emit({
|
|
207
|
+
source: "poll",
|
|
208
|
+
category: "messages",
|
|
209
|
+
kind: "message.received",
|
|
210
|
+
title: delta === 1 ? "New message" : `${delta} new messages`,
|
|
211
|
+
body: "You have a new message from the team.",
|
|
212
|
+
severity: "info",
|
|
213
|
+
orgId,
|
|
214
|
+
// Keyed on the resulting count rather than the delta, so two
|
|
215
|
+
// tabs polling at different moments agree on the id.
|
|
216
|
+
data: { entityId: `unread:${next.unreadMessages}` }
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
for (const [id, status] of next.orderStatuses) {
|
|
220
|
+
const before = previous.orderStatuses.get(id);
|
|
221
|
+
if (before === void 0 || before === status) continue;
|
|
222
|
+
emit({
|
|
223
|
+
source: "poll",
|
|
224
|
+
category: "transactional",
|
|
225
|
+
kind: `order.${status}`,
|
|
226
|
+
title: orderTitle(status),
|
|
227
|
+
body: null,
|
|
228
|
+
severity: status === "cancelled" ? "warning" : "info",
|
|
229
|
+
orgId,
|
|
230
|
+
data: { entityId: `order:${id}:${status}`, orderId: id, status }
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
for (const [id, status] of next.invoiceStatuses) {
|
|
234
|
+
const before = previous.invoiceStatuses.get(id);
|
|
235
|
+
if (before === void 0 || before === status) continue;
|
|
236
|
+
emit({
|
|
237
|
+
source: "poll",
|
|
238
|
+
category: "transactional",
|
|
239
|
+
kind: `invoice.${status}`,
|
|
240
|
+
title: status === "paid" ? "Invoice paid" : status === "overdue" ? "Invoice overdue" : "Invoice updated",
|
|
241
|
+
body: null,
|
|
242
|
+
severity: status === "overdue" ? "error" : "info",
|
|
243
|
+
orgId,
|
|
244
|
+
data: { entityId: `invoice:${id}:${status}`, invoiceId: id, status }
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
for (const [id, status] of next.shipmentStatuses) {
|
|
248
|
+
const before = previous.shipmentStatuses.get(id);
|
|
249
|
+
if (before === void 0 || before === status) continue;
|
|
250
|
+
emit({
|
|
251
|
+
source: "poll",
|
|
252
|
+
category: "transactional",
|
|
253
|
+
kind: `shipment.${status}`,
|
|
254
|
+
title: status === "delivered" ? "Your order was delivered" : status === "out_for_delivery" ? "Out for delivery" : "Shipping update",
|
|
255
|
+
body: null,
|
|
256
|
+
severity: "info",
|
|
257
|
+
orgId,
|
|
258
|
+
data: {
|
|
259
|
+
entityId: `shipment:${id}:${status}`,
|
|
260
|
+
shipmentId: id,
|
|
261
|
+
status
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
async function tick() {
|
|
267
|
+
if (stopped) return;
|
|
268
|
+
await Promise.allSettled([
|
|
269
|
+
options.portal.loadOrders(),
|
|
270
|
+
options.portal.loadInvoices(),
|
|
271
|
+
options.portal.loadShipments(),
|
|
272
|
+
options.portal.loadMessages()
|
|
273
|
+
]);
|
|
274
|
+
if (stopped) return;
|
|
275
|
+
const next = readWatermark();
|
|
276
|
+
if (baseline) diff(baseline, next);
|
|
277
|
+
baseline = next;
|
|
278
|
+
}
|
|
279
|
+
baseline = readWatermark();
|
|
280
|
+
timer = setInterval(() => void tick(), intervalMs);
|
|
281
|
+
return () => {
|
|
282
|
+
stopped = true;
|
|
283
|
+
if (timer) clearInterval(timer);
|
|
284
|
+
};
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function orderTitle(status) {
|
|
288
|
+
switch (status) {
|
|
289
|
+
case "shipped":
|
|
290
|
+
return "Your order shipped";
|
|
291
|
+
case "delivered":
|
|
292
|
+
return "Your order was delivered";
|
|
293
|
+
case "cancelled":
|
|
294
|
+
return "Your order was cancelled";
|
|
295
|
+
case "refunded":
|
|
296
|
+
return "Your refund was issued";
|
|
297
|
+
default:
|
|
298
|
+
return "Order updated";
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function toToast(notification) {
|
|
302
|
+
return {
|
|
303
|
+
title: notification.title,
|
|
304
|
+
description: notification.body ?? void 0,
|
|
305
|
+
variant: notification.severity === "error" ? "destructive" : notification.severity === "warning" ? "warning" : notification.severity === "success" ? "success" : "default",
|
|
306
|
+
action: notification.url ? { label: "View", url: notification.url } : void 0
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
310
|
+
0 && (module.exports = {
|
|
311
|
+
createNotificationFeed,
|
|
312
|
+
portalSource,
|
|
313
|
+
pushSource,
|
|
314
|
+
toToast
|
|
315
|
+
});
|