threeu-sdk 0.1.0

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.
Files changed (64) hide show
  1. package/README.md +176 -0
  2. package/dist/admin/index.d.ts +22 -0
  3. package/dist/admin/index.js +17 -0
  4. package/dist/admin/index.js.map +1 -0
  5. package/dist/chunk-2TGBSV6L.js +197 -0
  6. package/dist/chunk-2TGBSV6L.js.map +1 -0
  7. package/dist/chunk-6S7OFN23.js +81 -0
  8. package/dist/chunk-6S7OFN23.js.map +1 -0
  9. package/dist/chunk-6ZCBDWWQ.js +140 -0
  10. package/dist/chunk-6ZCBDWWQ.js.map +1 -0
  11. package/dist/chunk-BCUODRZW.js +78 -0
  12. package/dist/chunk-BCUODRZW.js.map +1 -0
  13. package/dist/chunk-H3XILKGI.js +70 -0
  14. package/dist/chunk-H3XILKGI.js.map +1 -0
  15. package/dist/chunk-HYSJ6YPN.js +425 -0
  16. package/dist/chunk-HYSJ6YPN.js.map +1 -0
  17. package/dist/chunk-LFF5LPWT.js +122 -0
  18. package/dist/chunk-LFF5LPWT.js.map +1 -0
  19. package/dist/chunk-OXAQGEMQ.js +3 -0
  20. package/dist/chunk-OXAQGEMQ.js.map +1 -0
  21. package/dist/chunk-USFJIM5K.js +195 -0
  22. package/dist/chunk-USFJIM5K.js.map +1 -0
  23. package/dist/chunk-ZWLFJIAM.js +69 -0
  24. package/dist/chunk-ZWLFJIAM.js.map +1 -0
  25. package/dist/define-B6ZJMWDI.d.ts +24 -0
  26. package/dist/developer/index.d.ts +120 -0
  27. package/dist/developer/index.js +106 -0
  28. package/dist/developer/index.js.map +1 -0
  29. package/dist/errors/index.d.ts +83 -0
  30. package/dist/errors/index.js +4 -0
  31. package/dist/errors/index.js.map +1 -0
  32. package/dist/index-ksmDFDZc.d.ts +90 -0
  33. package/dist/index.d.ts +68 -0
  34. package/dist/index.js +8 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/plugin/index.d.ts +13 -0
  37. package/dist/plugin/index.js +27 -0
  38. package/dist/plugin/index.js.map +1 -0
  39. package/dist/plugin/server/index.d.ts +70 -0
  40. package/dist/plugin/server/index.js +105 -0
  41. package/dist/plugin/server/index.js.map +1 -0
  42. package/dist/pos/index.d.ts +26 -0
  43. package/dist/pos/index.js +24 -0
  44. package/dist/pos/index.js.map +1 -0
  45. package/dist/react/index.d.ts +5 -0
  46. package/dist/react/index.js +7 -0
  47. package/dist/react/index.js.map +1 -0
  48. package/dist/session-BFDRm-KJ.d.ts +94 -0
  49. package/dist/storefront/index.d.ts +76 -0
  50. package/dist/storefront/index.js +6 -0
  51. package/dist/storefront/index.js.map +1 -0
  52. package/dist/testing/index.d.ts +82 -0
  53. package/dist/testing/index.js +106 -0
  54. package/dist/testing/index.js.map +1 -0
  55. package/dist/theme/index.d.ts +109 -0
  56. package/dist/theme/index.js +152 -0
  57. package/dist/theme/index.js.map +1 -0
  58. package/dist/threeu-BesFjXdw.d.ts +143 -0
  59. package/dist/types-BoGD3IXz.d.ts +173 -0
  60. package/dist/types-DfyYnoLn.d.ts +248 -0
  61. package/dist/webhooks/index.d.ts +36 -0
  62. package/dist/webhooks/index.js +4 -0
  63. package/dist/webhooks/index.js.map +1 -0
  64. package/package.json +110 -0
@@ -0,0 +1,94 @@
1
+ import { B as BaseRecord, T as ThreeuHttpClient, I as Id, R as RequestOptions, L as ListParams, f as Page } from './types-DfyYnoLn.js';
2
+
3
+ /**
4
+ * Generic CRUD resource bound to a base path and a brand scope. Each high-level
5
+ * resource (products, orders, ...) is a thin subclass that adds domain-specific
6
+ * actions. Paths are centralized so they're trivial to adjust as backend routes
7
+ * are confirmed.
8
+ */
9
+
10
+ interface ResourceContext {
11
+ http: ThreeuHttpClient;
12
+ /** Brand scope applied as `platform-id` on every call (undefined = client default). */
13
+ brand?: Id;
14
+ }
15
+ declare class CrudResource<T extends BaseRecord = BaseRecord, TInput = Partial<T>> {
16
+ protected readonly ctx: ResourceContext;
17
+ protected readonly path: string;
18
+ constructor(ctx: ResourceContext, path: string);
19
+ protected opts(extra?: RequestOptions): RequestOptions;
20
+ list(params?: ListParams): Promise<Page<T>>;
21
+ iterate(params?: ListParams): AsyncGenerator<T, void, void>;
22
+ get(id: Id): Promise<T>;
23
+ create(input: TInput, options?: {
24
+ idempotencyKey?: string;
25
+ }): Promise<T>;
26
+ update(id: Id, input: Partial<TInput>): Promise<T>;
27
+ delete(id: Id): Promise<void>;
28
+ /** POST a custom sub-action, e.g. `action(id, "fulfill", {...})`. */
29
+ protected action<R = unknown>(id: Id, verb: string, body?: unknown): Promise<R>;
30
+ }
31
+
32
+ /**
33
+ * POS session + cart. A session captures the location/cashier context; carts are
34
+ * created within it and drive the add-item → discount → checkout flow.
35
+ * Shared by `brand.pos.session(...)` and the standalone `ThreeuPOS` client.
36
+ */
37
+
38
+ interface PosSessionConfig {
39
+ locationId?: Id;
40
+ cashierId?: Id;
41
+ }
42
+ interface CartItemInput {
43
+ productId: Id;
44
+ quantity: number;
45
+ variantId?: Id;
46
+ price?: number;
47
+ }
48
+ interface CartDiscountInput {
49
+ type: "percentage" | "fixed";
50
+ value: number;
51
+ code?: string;
52
+ }
53
+ interface CheckoutInput {
54
+ method: "card" | "cash" | "wallet" | string;
55
+ amountTendered?: number;
56
+ customerId?: Id;
57
+ }
58
+ interface CartData {
59
+ id: Id;
60
+ [key: string]: unknown;
61
+ }
62
+ declare class PosCart {
63
+ private readonly ctx;
64
+ readonly id: Id;
65
+ private data;
66
+ constructor(ctx: ResourceContext, id: Id, data: CartData);
67
+ private opts;
68
+ get snapshot(): CartData;
69
+ addItem(item: CartItemInput): Promise<CartData>;
70
+ removeItem(itemId: Id): Promise<CartData>;
71
+ applyDiscount(discount: CartDiscountInput): Promise<CartData>;
72
+ checkout(input: CheckoutInput): Promise<Record<string, unknown>>;
73
+ }
74
+ declare class PosCartNamespace {
75
+ private readonly ctx;
76
+ private readonly config;
77
+ constructor(ctx: ResourceContext, config: PosSessionConfig);
78
+ create(): Promise<PosCart>;
79
+ get(id: Id): Promise<PosCart>;
80
+ }
81
+ declare class PosSession {
82
+ private readonly ctx;
83
+ readonly config: PosSessionConfig;
84
+ readonly cart: PosCartNamespace;
85
+ constructor(ctx: ResourceContext, config: PosSessionConfig);
86
+ }
87
+ /** Factory used by both the brand POS namespace and ThreeuPOS. */
88
+ declare class PosNamespace {
89
+ private readonly ctx;
90
+ constructor(ctx: ResourceContext);
91
+ session(config?: PosSessionConfig): PosSession;
92
+ }
93
+
94
+ export { type CartData as C, type PosSessionConfig as P, type ResourceContext as R, PosSession as a, type CartDiscountInput as b, type CartItemInput as c, type CheckoutInput as d, PosCart as e, PosCartNamespace as f, PosNamespace as g, CrudResource as h };
@@ -0,0 +1,76 @@
1
+ import { T as ThreeuHttpClient, I as Id, f as Page, d as Product, C as Collection, g as ThreeuLogger, h as ThreeuTelemetryOptions } from '../types-DfyYnoLn.js';
2
+
3
+ /**
4
+ * @threeu/threeu/storefront — public-token storefront client.
5
+ *
6
+ * const storefront = new ThreeuStorefront({
7
+ * publicToken: import.meta.env.VITE_THREEU_PUBLIC_KEY,
8
+ * brand: "my-store",
9
+ * });
10
+ * const products = await storefront.products.list({ collection: "featured", limit: 8 });
11
+ * const cart = await storefront.cart.create();
12
+ *
13
+ * Public surface lives under `/api/marketplace/storefront/*` and resolves the
14
+ * brand purely from the `platform-id` header (docs/ARCHITECTURE_MAP.md §14).
15
+ * Safe to use in the browser — it never carries a secret token.
16
+ */
17
+
18
+ interface ThreeuStorefrontOptions {
19
+ brand: Id;
20
+ publicToken?: string;
21
+ baseUrl?: string;
22
+ fetch?: typeof fetch;
23
+ logger?: ThreeuLogger;
24
+ telemetry?: ThreeuTelemetryOptions;
25
+ timeoutMs?: number;
26
+ }
27
+ interface StorefrontProductParams {
28
+ collection?: string;
29
+ limit?: number;
30
+ page?: number;
31
+ search?: string;
32
+ [key: string]: unknown;
33
+ }
34
+ declare class StorefrontProducts {
35
+ private readonly http;
36
+ private readonly brand;
37
+ constructor(http: ThreeuHttpClient, brand: Id);
38
+ list(params?: StorefrontProductParams): Promise<Page<Product>>;
39
+ get(id: Id): Promise<Product>;
40
+ }
41
+ declare class StorefrontCollections {
42
+ private readonly http;
43
+ private readonly brand;
44
+ constructor(http: ThreeuHttpClient, brand: Id);
45
+ list(params?: {
46
+ limit?: number;
47
+ page?: number;
48
+ }): Promise<Page<Collection>>;
49
+ }
50
+ declare class StorefrontCart {
51
+ private readonly http;
52
+ private readonly brand;
53
+ constructor(http: ThreeuHttpClient, brand: Id);
54
+ create(): Promise<Record<string, unknown>>;
55
+ get(id: Id): Promise<Record<string, unknown>>;
56
+ addItem(id: Id, item: {
57
+ productId: Id;
58
+ quantity: number;
59
+ }): Promise<Record<string, unknown>>;
60
+ }
61
+ declare class ThreeuStorefront {
62
+ readonly http: ThreeuHttpClient;
63
+ readonly products: StorefrontProducts;
64
+ readonly collections: StorefrontCollections;
65
+ readonly cart: StorefrontCart;
66
+ private readonly brand;
67
+ constructor(options: ThreeuStorefrontOptions);
68
+ /** Fetch the unified storefront config (delivery, payments, theme tokens). */
69
+ getConfig(params?: {
70
+ website?: boolean;
71
+ pos?: boolean;
72
+ }): Promise<Record<string, unknown>>;
73
+ close(): Promise<void>;
74
+ }
75
+
76
+ export { type StorefrontProductParams, ThreeuStorefront, type ThreeuStorefrontOptions };
@@ -0,0 +1,6 @@
1
+ export { ThreeuStorefront } from '../chunk-6S7OFN23.js';
2
+ import '../chunk-HYSJ6YPN.js';
3
+ import '../chunk-H3XILKGI.js';
4
+ import '../chunk-LFF5LPWT.js';
5
+ //# sourceMappingURL=index.js.map
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -0,0 +1,82 @@
1
+ import { PluginServer } from '../plugin/server/index.js';
2
+ import { P as PluginManifest } from '../types-BoGD3IXz.js';
3
+ import '../define-B6ZJMWDI.js';
4
+
5
+ interface MockRoute {
6
+ method?: string;
7
+ /** Match against the request path (substring) or a RegExp on the full URL. */
8
+ path: string | RegExp;
9
+ status?: number;
10
+ body?: unknown;
11
+ /** Dynamic responder; takes precedence over `body`. */
12
+ respond?: (req: {
13
+ method: string;
14
+ url: string;
15
+ body: unknown;
16
+ }) => {
17
+ status?: number;
18
+ body?: unknown;
19
+ };
20
+ }
21
+ interface RecordedRequest {
22
+ method: string;
23
+ url: string;
24
+ headers: Record<string, string>;
25
+ body: unknown;
26
+ }
27
+ interface MockFetch {
28
+ (input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
29
+ /** Every request the mock received, in order. */
30
+ readonly requests: RecordedRequest[];
31
+ }
32
+ /** Build a `fetch` implementation that answers from a route table and records calls. */
33
+ declare function createMockFetch(routes?: MockRoute[]): MockFetch;
34
+ interface TestActionOptions {
35
+ brandId?: string | number;
36
+ verb?: string;
37
+ secret?: string;
38
+ }
39
+ /** Invoke a plugin-server action locally, signing the request when a secret is given. */
40
+ declare function testPluginAction(server: PluginServer, action: string, payload: Record<string, unknown>, options?: TestActionOptions): Promise<{
41
+ status: number;
42
+ body: unknown;
43
+ }>;
44
+ /** Invoke a plugin-server health check locally. */
45
+ declare function testPluginHealth(server: PluginServer, healthPath?: string): Promise<{
46
+ status: number;
47
+ body: unknown;
48
+ }>;
49
+ declare const fixtures: {
50
+ product: {
51
+ id: string;
52
+ name: string;
53
+ price: string;
54
+ currency: string;
55
+ stock: number;
56
+ };
57
+ order: {
58
+ id: string;
59
+ status: string;
60
+ total: string;
61
+ currency: string;
62
+ customer_id: string;
63
+ };
64
+ customer: {
65
+ id: string;
66
+ name: string;
67
+ email: string;
68
+ };
69
+ paginated<T>(items: T[], page?: number, perPage?: number, total?: number): {
70
+ success: boolean;
71
+ data: {
72
+ data: T[];
73
+ current_page: number;
74
+ per_page: number;
75
+ total: number;
76
+ last_page: number;
77
+ };
78
+ };
79
+ pluginManifest(overrides?: Partial<PluginManifest>): PluginManifest;
80
+ };
81
+
82
+ export { type MockFetch, type MockRoute, type RecordedRequest, type TestActionOptions, createMockFetch, fixtures, testPluginAction, testPluginHealth };
@@ -0,0 +1,106 @@
1
+ import { signLegacyWebhook } from '../chunk-ZWLFJIAM.js';
2
+ import '../chunk-LFF5LPWT.js';
3
+
4
+ // src/testing/index.ts
5
+ function createMockFetch(routes = []) {
6
+ const requests = [];
7
+ const impl = async (input, init) => {
8
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
9
+ const method = (init?.method ?? "GET").toUpperCase();
10
+ const headers = normalizeHeaders(init?.headers);
11
+ const parsedBody = parseRequestBody(init?.body);
12
+ requests.push({ method, url, headers, body: parsedBody });
13
+ const route = routes.find(
14
+ (r) => (!r.method || r.method.toUpperCase() === method) && (r.path instanceof RegExp ? r.path.test(url) : url.includes(r.path))
15
+ );
16
+ if (!route) {
17
+ return jsonResponse(404, { success: false, message: `No mock route for ${method} ${url}` });
18
+ }
19
+ const dynamic = route.respond?.({ method, url, body: parsedBody });
20
+ const status = dynamic?.status ?? route.status ?? 200;
21
+ const body = dynamic?.body ?? route.body ?? { success: true, data: null };
22
+ return jsonResponse(status, body);
23
+ };
24
+ return Object.assign(impl, { requests });
25
+ }
26
+ function normalizeHeaders(headers) {
27
+ const out = {};
28
+ if (!headers) return out;
29
+ if (headers instanceof Headers) {
30
+ headers.forEach((v, k) => out[k] = v);
31
+ } else if (Array.isArray(headers)) {
32
+ for (const [k, v] of headers) out[k] = v;
33
+ } else {
34
+ Object.assign(out, headers);
35
+ }
36
+ return out;
37
+ }
38
+ function parseRequestBody(body) {
39
+ if (typeof body !== "string") return body ?? null;
40
+ try {
41
+ return JSON.parse(body);
42
+ } catch {
43
+ return body;
44
+ }
45
+ }
46
+ function jsonResponse(status, body) {
47
+ return new Response(JSON.stringify(body), {
48
+ status,
49
+ headers: { "content-type": "application/json" }
50
+ });
51
+ }
52
+ async function testPluginAction(server, action, payload, options = {}) {
53
+ const bodyObj = { brand_id: options.brandId, verb: options.verb ?? "POST", action, payload };
54
+ const rawBody = JSON.stringify(bodyObj);
55
+ const headers = { "content-type": "application/json" };
56
+ if (options.secret) headers["x-threeu-signature"] = await signLegacyWebhook(options.secret, bodyObj);
57
+ const res = await server.handle({ method: "POST", path: `/actions/${action}`, headers, rawBody });
58
+ return { status: res.status, body: safeJson(res.body) };
59
+ }
60
+ async function testPluginHealth(server, healthPath = "/health") {
61
+ const res = await server.handle({ method: "GET", path: healthPath, headers: {}, rawBody: "" });
62
+ return { status: res.status, body: safeJson(res.body) };
63
+ }
64
+ function safeJson(text) {
65
+ try {
66
+ return JSON.parse(text);
67
+ } catch {
68
+ return text;
69
+ }
70
+ }
71
+ var fixtures = {
72
+ product: { id: "prod_123", name: "Black Abaya", price: "350.00", currency: "QAR", stock: 20 },
73
+ order: { id: "order_123", status: "pending", total: "350.00", currency: "QAR", customer_id: "cust_1" },
74
+ customer: { id: "cust_1", name: "Aisha", email: "aisha@example.com" },
75
+ paginated(items, page = 1, perPage = 20, total = items.length) {
76
+ return {
77
+ success: true,
78
+ data: { data: items, current_page: page, per_page: perPage, total, last_page: Math.max(1, Math.ceil(total / perPage)) }
79
+ };
80
+ },
81
+ pluginManifest(overrides = {}) {
82
+ return {
83
+ type: "plugin",
84
+ runtime: "remote_http",
85
+ name: "FastShip",
86
+ slug: "fastship",
87
+ version: "1.0.0",
88
+ category: "shipping",
89
+ surface_type: "plugin",
90
+ trigger_mode: "manual",
91
+ ownership: "community",
92
+ permissions: ["orders:read", "shipping:write"],
93
+ provider: {
94
+ type: "remote_http",
95
+ base_url: "https://api.fastship.com/threeu",
96
+ health_path: "/health",
97
+ auth: { type: "hmac_sha256", signature_header: "X-ThreeU-Signature", timestamp_header: "X-ThreeU-Timestamp" }
98
+ },
99
+ ...overrides
100
+ };
101
+ }
102
+ };
103
+
104
+ export { createMockFetch, fixtures, testPluginAction, testPluginHealth };
105
+ //# sourceMappingURL=index.js.map
106
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/testing/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @threeu/threeu/testing — mocks & fixtures for testing SDK integrations.\n *\n * const fetchMock = createMockFetch([\n * { method: \"POST\", path: \"/products\", body: { success: true, data: fixtures.product } },\n * ]);\n * const threeu = new Threeu(\"test\", { fetch: fetchMock, baseUrl: \"https://api.test\" });\n */\nimport { signLegacyWebhook } from \"../webhooks\";\nimport type { PluginServer } from \"../plugin/server\";\nimport type { PluginManifest } from \"../manifests/types\";\n\nexport interface MockRoute {\n method?: string;\n /** Match against the request path (substring) or a RegExp on the full URL. */\n path: string | RegExp;\n status?: number;\n body?: unknown;\n /** Dynamic responder; takes precedence over `body`. */\n respond?: (req: { method: string; url: string; body: unknown }) => { status?: number; body?: unknown };\n}\n\nexport interface RecordedRequest {\n method: string;\n url: string;\n headers: Record<string, string>;\n body: unknown;\n}\n\nexport interface MockFetch {\n (input: RequestInfo | URL, init?: RequestInit): Promise<Response>;\n /** Every request the mock received, in order. */\n readonly requests: RecordedRequest[];\n}\n\n/** Build a `fetch` implementation that answers from a route table and records calls. */\nexport function createMockFetch(routes: MockRoute[] = []): MockFetch {\n const requests: RecordedRequest[] = [];\n\n const impl = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n const url = typeof input === \"string\" ? input : input instanceof URL ? input.toString() : input.url;\n const method = (init?.method ?? \"GET\").toUpperCase();\n const headers = normalizeHeaders(init?.headers);\n const parsedBody = parseRequestBody(init?.body);\n requests.push({ method, url, headers, body: parsedBody });\n\n const route = routes.find(\n (r) =>\n (!r.method || r.method.toUpperCase() === method) &&\n (r.path instanceof RegExp ? r.path.test(url) : url.includes(r.path)),\n );\n\n if (!route) {\n return jsonResponse(404, { success: false, message: `No mock route for ${method} ${url}` });\n }\n const dynamic = route.respond?.({ method, url, body: parsedBody });\n const status = dynamic?.status ?? route.status ?? 200;\n const body = dynamic?.body ?? route.body ?? { success: true, data: null };\n return jsonResponse(status, body);\n };\n\n return Object.assign(impl, { requests }) as MockFetch;\n}\n\nfunction normalizeHeaders(headers: RequestInit[\"headers\"]): Record<string, string> {\n const out: Record<string, string> = {};\n if (!headers) return out;\n if (headers instanceof Headers) {\n headers.forEach((v, k) => (out[k] = v));\n } else if (Array.isArray(headers)) {\n for (const [k, v] of headers) out[k] = v;\n } else {\n Object.assign(out, headers);\n }\n return out;\n}\n\nfunction parseRequestBody(body: RequestInit[\"body\"]): unknown {\n if (typeof body !== \"string\") return body ?? null;\n try {\n return JSON.parse(body);\n } catch {\n return body;\n }\n}\n\nfunction jsonResponse(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { \"content-type\": \"application/json\" },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Plugin server local testing\n// ---------------------------------------------------------------------------\n\nexport interface TestActionOptions {\n brandId?: string | number;\n verb?: string;\n secret?: string;\n}\n\n/** Invoke a plugin-server action locally, signing the request when a secret is given. */\nexport async function testPluginAction(\n server: PluginServer,\n action: string,\n payload: Record<string, unknown>,\n options: TestActionOptions = {},\n): Promise<{ status: number; body: unknown }> {\n const bodyObj = { brand_id: options.brandId, verb: options.verb ?? \"POST\", action, payload };\n const rawBody = JSON.stringify(bodyObj);\n const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n if (options.secret) headers[\"x-threeu-signature\"] = await signLegacyWebhook(options.secret, bodyObj);\n\n const res = await server.handle({ method: \"POST\", path: `/actions/${action}`, headers, rawBody });\n return { status: res.status, body: safeJson(res.body) };\n}\n\n/** Invoke a plugin-server health check locally. */\nexport async function testPluginHealth(\n server: PluginServer,\n healthPath = \"/health\",\n): Promise<{ status: number; body: unknown }> {\n const res = await server.handle({ method: \"GET\", path: healthPath, headers: {}, rawBody: \"\" });\n return { status: res.status, body: safeJson(res.body) };\n}\n\nfunction safeJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Fixtures\n// ---------------------------------------------------------------------------\n\nexport const fixtures = {\n product: { id: \"prod_123\", name: \"Black Abaya\", price: \"350.00\", currency: \"QAR\", stock: 20 },\n order: { id: \"order_123\", status: \"pending\", total: \"350.00\", currency: \"QAR\", customer_id: \"cust_1\" },\n customer: { id: \"cust_1\", name: \"Aisha\", email: \"aisha@example.com\" },\n paginated<T>(items: T[], page = 1, perPage = 20, total = items.length) {\n return {\n success: true,\n data: { data: items, current_page: page, per_page: perPage, total, last_page: Math.max(1, Math.ceil(total / perPage)) },\n };\n },\n pluginManifest(overrides: Partial<PluginManifest> = {}): PluginManifest {\n return {\n type: \"plugin\",\n runtime: \"remote_http\",\n name: \"FastShip\",\n slug: \"fastship\",\n version: \"1.0.0\",\n category: \"shipping\",\n surface_type: \"plugin\",\n trigger_mode: \"manual\",\n ownership: \"community\",\n permissions: [\"orders:read\", \"shipping:write\"],\n provider: {\n type: \"remote_http\",\n base_url: \"https://api.fastship.com/threeu\",\n health_path: \"/health\",\n auth: { type: \"hmac_sha256\", signature_header: \"X-ThreeU-Signature\", timestamp_header: \"X-ThreeU-Timestamp\" },\n },\n ...overrides,\n };\n },\n};\n"]}
@@ -0,0 +1,109 @@
1
+ import { ComponentType, CSSProperties, ReactNode } from 'react';
2
+ import { u as ThemeVisibility, v as ThemeEditableField, d as ThemeManifest } from '../types-BoGD3IXz.js';
3
+ export { A as AsyncState, F as FieldKind, T as ThemeBrandInfo, a as ThemeContextValue, b as ThemeProvider, c as ThemeProviderProps, d as ThreeuThemeProvider, e as ThreeuThemeProviderProps, f as extractFields, r as registerField, g as resetRegistry, h as resolveValue, s as setActiveCustomization, u as useBrand, i as useCart, j as useCheckout, k as useCollections, l as useCurrency, m as useCustomer, n as useLocale, o as useProducts, p as useTheme, q as useThemeContext, t as useThemeField } from '../index-ksmDFDZc.js';
4
+ import { C as Collection, d as Product } from '../types-DfyYnoLn.js';
5
+ import '../storefront/index.js';
6
+
7
+ /**
8
+ * createTheme / defineTheme — register pages and produce a theme manifest.
9
+ *
10
+ * Editable fields are discovered at render time via the runtime registry, so
11
+ * `manifest()` merges any explicitly declared fields with those collected from
12
+ * the most recent render/extraction pass. A CLI build step can render each page
13
+ * once and then call `manifest()` to capture the full field set.
14
+ */
15
+
16
+ interface CreateThemeOptions {
17
+ name: string;
18
+ slug: string;
19
+ version?: string;
20
+ visibility?: ThemeVisibility;
21
+ pages: Record<string, ComponentType<Record<string, unknown>>>;
22
+ /** Explicitly declared editable fields (merged with render-extracted ones). */
23
+ fields?: ThemeEditableField[];
24
+ }
25
+ interface DefinedTheme {
26
+ readonly name: string;
27
+ readonly slug: string;
28
+ readonly visibility: ThemeVisibility;
29
+ readonly pages: Record<string, ComponentType<Record<string, unknown>>>;
30
+ getPage(name: string): ComponentType<Record<string, unknown>> | undefined;
31
+ /** Build the theme manifest, merging declared + render-extracted fields. */
32
+ manifest(): ThemeManifest;
33
+ toJSON(): ThemeManifest;
34
+ }
35
+ declare function createTheme(options: CreateThemeOptions): DefinedTheme;
36
+ /** Alias — some authors prefer `defineTheme`. */
37
+ declare const defineTheme: typeof createTheme;
38
+
39
+ /**
40
+ * Type-safe, customizable theme components. Each `T*` component registers the
41
+ * editable field it exposes (so the theme editor / build step can enumerate
42
+ * them) and resolves its current value from the active customization.
43
+ *
44
+ * Editable elements carry a `data-threeu-field` attribute the in-app editor uses
45
+ * to target them.
46
+ */
47
+
48
+ /** Resolve (and register) a color field. Usable inline: `TColor("hero.bg", "#000")`. */
49
+ declare function TColor(name: string, fallback?: string): string;
50
+ interface FieldProps {
51
+ name: string;
52
+ className?: string;
53
+ style?: CSSProperties;
54
+ }
55
+ declare function TSection(props: FieldProps & {
56
+ children?: ReactNode;
57
+ }): JSX.Element;
58
+ declare function THeading(props: FieldProps & {
59
+ children?: ReactNode;
60
+ as?: "h1" | "h2" | "h3";
61
+ }): JSX.Element;
62
+ declare function TParagraph(props: FieldProps & {
63
+ children?: ReactNode;
64
+ }): JSX.Element;
65
+ declare function TText(props: FieldProps & {
66
+ children?: ReactNode;
67
+ }): JSX.Element;
68
+ declare function TSpan(props: FieldProps & {
69
+ children?: ReactNode;
70
+ }): JSX.Element;
71
+ declare function TImage(props: FieldProps & {
72
+ src: string;
73
+ alt: string;
74
+ width?: number | string;
75
+ height?: number | string;
76
+ }): JSX.Element;
77
+ declare function TButton(props: FieldProps & {
78
+ children?: ReactNode;
79
+ onClick?: () => void;
80
+ type?: "button" | "submit";
81
+ }): JSX.Element;
82
+ declare function TLink(props: FieldProps & {
83
+ href: string;
84
+ children?: ReactNode;
85
+ }): JSX.Element;
86
+ declare function TProductCard(props: {
87
+ product: Product;
88
+ className?: string;
89
+ }): JSX.Element;
90
+ declare function TProductGrid(props: {
91
+ products: Product[];
92
+ columns?: number;
93
+ className?: string;
94
+ }): JSX.Element;
95
+ declare function TCollectionGrid(props: {
96
+ collections: Collection[];
97
+ className?: string;
98
+ }): JSX.Element;
99
+ declare function TCartSummary(props: {
100
+ cart?: Record<string, unknown>;
101
+ className?: string;
102
+ }): JSX.Element;
103
+ declare function TCheckoutForm(props: {
104
+ onSubmit?: (data: Record<string, FormDataEntryValue>) => void;
105
+ className?: string;
106
+ children?: ReactNode;
107
+ }): JSX.Element;
108
+
109
+ export { type CreateThemeOptions, type DefinedTheme, TButton, TCartSummary, TCheckoutForm, TCollectionGrid, TColor, THeading, TImage, TLink, TParagraph, TProductCard, TProductGrid, TSection, TSpan, TText, ThemeEditableField, ThemeManifest, ThemeVisibility, createTheme, defineTheme };
@@ -0,0 +1,152 @@
1
+ import { registerField, resolveValue, useThemeField, extractFields } from '../chunk-6ZCBDWWQ.js';
2
+ export { ThemeProvider, ThreeuThemeProvider, extractFields, registerField, resetRegistry, resolveValue, setActiveCustomization, useBrand, useCart, useCheckout, useCollections, useCurrency, useCustomer, useLocale, useProducts, useTheme, useThemeContext, useThemeField } from '../chunk-6ZCBDWWQ.js';
3
+ import '../chunk-6S7OFN23.js';
4
+ import { buildThemeManifest } from '../chunk-USFJIM5K.js';
5
+ import '../chunk-HYSJ6YPN.js';
6
+ import '../chunk-H3XILKGI.js';
7
+ import '../chunk-LFF5LPWT.js';
8
+ import { jsx, jsxs } from 'react/jsx-runtime';
9
+
10
+ // src/theme/define.ts
11
+ function createTheme(options) {
12
+ const pageNames = Object.keys(options.pages);
13
+ const manifest = () => {
14
+ const declared = options.fields ?? [];
15
+ const extracted = extractFields();
16
+ const byName = /* @__PURE__ */ new Map();
17
+ for (const f of [...extracted, ...declared]) byName.set(f.name, f);
18
+ return buildThemeManifest({
19
+ name: options.name,
20
+ slug: options.slug,
21
+ version: options.version,
22
+ visibility: options.visibility,
23
+ pages: pageNames,
24
+ fields: Array.from(byName.values())
25
+ });
26
+ };
27
+ return {
28
+ name: options.name,
29
+ slug: options.slug,
30
+ visibility: options.visibility ?? "public",
31
+ pages: options.pages,
32
+ getPage: (name) => options.pages[name],
33
+ manifest,
34
+ toJSON: manifest
35
+ };
36
+ }
37
+ var defineTheme = createTheme;
38
+ function TColor(name, fallback = "#000000") {
39
+ registerField(name, "color", fallback);
40
+ return resolveValue(name, fallback);
41
+ }
42
+ function TSection(props) {
43
+ return /* @__PURE__ */ jsx("section", { "data-threeu-field": props.name, "data-threeu-kind": "section", className: props.className, style: props.style, children: props.children });
44
+ }
45
+ function THeading(props) {
46
+ const text = useThemeField(props.name, "heading", asText(props.children));
47
+ const Tag = props.as ?? "h1";
48
+ return /* @__PURE__ */ jsx(Tag, { "data-threeu-field": props.name, "data-threeu-kind": "heading", className: props.className, style: props.style, children: text });
49
+ }
50
+ function TParagraph(props) {
51
+ const text = useThemeField(props.name, "paragraph", asText(props.children));
52
+ return /* @__PURE__ */ jsx("p", { "data-threeu-field": props.name, "data-threeu-kind": "paragraph", className: props.className, style: props.style, children: text });
53
+ }
54
+ function TText(props) {
55
+ const text = useThemeField(props.name, "text", asText(props.children));
56
+ return /* @__PURE__ */ jsx("span", { "data-threeu-field": props.name, "data-threeu-kind": "text", className: props.className, style: props.style, children: text });
57
+ }
58
+ function TSpan(props) {
59
+ const text = useThemeField(props.name, "span", asText(props.children));
60
+ return /* @__PURE__ */ jsx("span", { "data-threeu-field": props.name, "data-threeu-kind": "span", className: props.className, style: props.style, children: text });
61
+ }
62
+ function TImage(props) {
63
+ const src = useThemeField(props.name, "image", props.src);
64
+ return /* @__PURE__ */ jsx(
65
+ "img",
66
+ {
67
+ "data-threeu-field": props.name,
68
+ "data-threeu-kind": "image",
69
+ src,
70
+ alt: props.alt,
71
+ width: props.width,
72
+ height: props.height,
73
+ className: props.className,
74
+ style: props.style
75
+ }
76
+ );
77
+ }
78
+ function TButton(props) {
79
+ const label = useThemeField(props.name, "text", asText(props.children));
80
+ return /* @__PURE__ */ jsx(
81
+ "button",
82
+ {
83
+ "data-threeu-field": props.name,
84
+ "data-threeu-kind": "text",
85
+ type: props.type ?? "button",
86
+ onClick: props.onClick,
87
+ className: props.className,
88
+ style: props.style,
89
+ children: label
90
+ }
91
+ );
92
+ }
93
+ function TLink(props) {
94
+ const href = useThemeField(props.name, "link", props.href);
95
+ return /* @__PURE__ */ jsx("a", { "data-threeu-field": props.name, "data-threeu-kind": "link", href, className: props.className, style: props.style, children: props.children });
96
+ }
97
+ function TProductCard(props) {
98
+ const { product } = props;
99
+ return /* @__PURE__ */ jsxs("div", { "data-threeu-product": String(product.id), className: props.className, children: [
100
+ /* @__PURE__ */ jsx("div", { className: "threeu-product-name", children: String(product.name ?? "") }),
101
+ /* @__PURE__ */ jsx("div", { className: "threeu-product-price", children: formatPrice(product.price, product.currency) })
102
+ ] });
103
+ }
104
+ function TProductGrid(props) {
105
+ const columns = props.columns ?? 3;
106
+ return /* @__PURE__ */ jsx(
107
+ "div",
108
+ {
109
+ className: props.className,
110
+ style: { display: "grid", gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`, gap: "1rem" },
111
+ children: props.products.map((p) => /* @__PURE__ */ jsx(TProductCard, { product: p }, String(p.id)))
112
+ }
113
+ );
114
+ }
115
+ function TCollectionGrid(props) {
116
+ return /* @__PURE__ */ jsx("div", { className: props.className, style: { display: "grid", gap: "1rem" }, children: props.collections.map((c) => /* @__PURE__ */ jsx("div", { "data-threeu-collection": String(c.id), children: String(c.title ?? c.handle ?? c.id) }, String(c.id))) });
117
+ }
118
+ function TCartSummary(props) {
119
+ const items = Array.isArray(props.cart?.items) ? props.cart?.items : [];
120
+ return /* @__PURE__ */ jsx("div", { className: props.className, "data-threeu-cart-summary": true, children: /* @__PURE__ */ jsxs("div", { children: [
121
+ items.length,
122
+ " item(s)"
123
+ ] }) });
124
+ }
125
+ function TCheckoutForm(props) {
126
+ return /* @__PURE__ */ jsx(
127
+ "form",
128
+ {
129
+ className: props.className,
130
+ "data-threeu-checkout-form": true,
131
+ onSubmit: (e) => {
132
+ e.preventDefault();
133
+ const data = Object.fromEntries(new FormData(e.currentTarget).entries());
134
+ props.onSubmit?.(data);
135
+ },
136
+ children: props.children
137
+ }
138
+ );
139
+ }
140
+ function asText(children) {
141
+ return typeof children === "string" ? children : "";
142
+ }
143
+ function formatPrice(price, currency) {
144
+ if (price === void 0) return "";
145
+ const n = typeof price === "string" ? Number(price) : price;
146
+ if (!Number.isFinite(n)) return String(price);
147
+ return `${n.toFixed(2)} ${currency ?? ""}`.trim();
148
+ }
149
+
150
+ export { TButton, TCartSummary, TCheckoutForm, TCollectionGrid, TColor, THeading, TImage, TLink, TParagraph, TProductCard, TProductGrid, TSection, TSpan, TText, createTheme, defineTheme };
151
+ //# sourceMappingURL=index.js.map
152
+ //# sourceMappingURL=index.js.map