@umec/admin 0.1.0-alpha.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 (60) hide show
  1. package/LICENSE +21 -0
  2. package/dist/access.d.ts +7 -0
  3. package/dist/access.js +9 -0
  4. package/dist/access.js.map +1 -0
  5. package/dist/carriers.d.ts +9 -0
  6. package/dist/carriers.js +13 -0
  7. package/dist/carriers.js.map +1 -0
  8. package/dist/catalog/index.d.ts +27 -0
  9. package/dist/catalog/index.js +21 -0
  10. package/dist/catalog/index.js.map +1 -0
  11. package/dist/chunk-4DR4K72B.js +24 -0
  12. package/dist/chunk-4DR4K72B.js.map +1 -0
  13. package/dist/chunk-E7ATMICY.js +51 -0
  14. package/dist/chunk-E7ATMICY.js.map +1 -0
  15. package/dist/chunk-G3O62WUE.js +57 -0
  16. package/dist/chunk-G3O62WUE.js.map +1 -0
  17. package/dist/chunk-ODK4R57S.js +131 -0
  18. package/dist/chunk-ODK4R57S.js.map +1 -0
  19. package/dist/chunk-RK7FIONL.js +27 -0
  20. package/dist/chunk-RK7FIONL.js.map +1 -0
  21. package/dist/chunk-STQU4LQU.js +402 -0
  22. package/dist/chunk-STQU4LQU.js.map +1 -0
  23. package/dist/chunk-TIK7KC4O.js +210 -0
  24. package/dist/chunk-TIK7KC4O.js.map +1 -0
  25. package/dist/chunk-TKJ6KSXM.js +23 -0
  26. package/dist/chunk-TKJ6KSXM.js.map +1 -0
  27. package/dist/chunk-VUT3UCCR.js +200 -0
  28. package/dist/chunk-VUT3UCCR.js.map +1 -0
  29. package/dist/chunk-W2O6EQWT.js +57 -0
  30. package/dist/chunk-W2O6EQWT.js.map +1 -0
  31. package/dist/chunk-ZEA6G75Z.js +129 -0
  32. package/dist/chunk-ZEA6G75Z.js.map +1 -0
  33. package/dist/csv.d.ts +4 -0
  34. package/dist/csv.js +9 -0
  35. package/dist/csv.js.map +1 -0
  36. package/dist/http.d.ts +17 -0
  37. package/dist/http.js +17 -0
  38. package/dist/http.js.map +1 -0
  39. package/dist/index.d.ts +16 -0
  40. package/dist/index.js +141 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/inventory/index.d.ts +27 -0
  43. package/dist/inventory/index.js +21 -0
  44. package/dist/inventory/index.js.map +1 -0
  45. package/dist/offers.d.ts +10 -0
  46. package/dist/offers.js +9 -0
  47. package/dist/offers.js.map +1 -0
  48. package/dist/orders/index.d.ts +49 -0
  49. package/dist/orders/index.js +34 -0
  50. package/dist/orders/index.js.map +1 -0
  51. package/dist/product-fields.d.ts +12 -0
  52. package/dist/product-fields.js +14 -0
  53. package/dist/product-fields.js.map +1 -0
  54. package/dist/products.d.ts +48 -0
  55. package/dist/products.js +27 -0
  56. package/dist/products.js.map +1 -0
  57. package/dist/unshipped-alert.d.ts +19 -0
  58. package/dist/unshipped-alert.js +12 -0
  59. package/dist/unshipped-alert.js.map +1 -0
  60. package/package.json +86 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daisuke Murayama
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ type AdminIdentity = {
2
+ email: string;
3
+ };
4
+ declare function authenticateAdmin(request: Request): Promise<AdminIdentity | Response>;
5
+ declare function requireAdminAuth(request: Request): Promise<Response | null>;
6
+
7
+ export { type AdminIdentity, authenticateAdmin, requireAdminAuth };
package/dist/access.js ADDED
@@ -0,0 +1,9 @@
1
+ import {
2
+ authenticateAdmin,
3
+ requireAdminAuth
4
+ } from "./chunk-W2O6EQWT.js";
5
+ export {
6
+ authenticateAdmin,
7
+ requireAdminAuth
8
+ };
9
+ //# sourceMappingURL=access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,9 @@
1
+ declare const CARRIERS: readonly ["yamato", "sagawa", "japanpost", "other"];
2
+ type Carrier = (typeof CARRIERS)[number];
3
+ declare const CARRIER_LABELS: Record<Carrier, string>;
4
+ declare function isCarrier(value: unknown): value is Carrier;
5
+ declare function parseOptionalCarrier(value: unknown): Carrier | null | {
6
+ error: string;
7
+ };
8
+
9
+ export { CARRIERS, CARRIER_LABELS, type Carrier, isCarrier, parseOptionalCarrier };
@@ -0,0 +1,13 @@
1
+ import {
2
+ CARRIERS,
3
+ CARRIER_LABELS,
4
+ isCarrier,
5
+ parseOptionalCarrier
6
+ } from "./chunk-4DR4K72B.js";
7
+ export {
8
+ CARRIERS,
9
+ CARRIER_LABELS,
10
+ isCarrier,
11
+ parseOptionalCarrier
12
+ };
13
+ //# sourceMappingURL=carriers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,27 @@
1
+ import { AdminApiHandler } from '../http.js';
2
+ import { InventoryRow } from '@umec/core/db';
3
+ import { ProductRow } from '../products.js';
4
+ import '@umec/core/checkout';
5
+ import '@umec/core/config';
6
+
7
+ declare function createCreateProductHandler(): AdminApiHandler;
8
+
9
+ declare function createDiscontinueProductHandler(): AdminApiHandler;
10
+
11
+ type ProductListRow = ProductRow & {
12
+ stock: InventoryRow["stock"] | null;
13
+ };
14
+ type CatalogAdminPageData = {
15
+ products: ProductListRow[];
16
+ dbNotice: string;
17
+ };
18
+ type CatalogAdminPageLoad = {
19
+ response: Response;
20
+ } | CatalogAdminPageData;
21
+ declare function formatYen(value: number): string;
22
+ declare function imagesText(row: ProductRow): string;
23
+ declare function loadCatalogAdminPage(request: Request): Promise<CatalogAdminPageLoad>;
24
+
25
+ declare function createUpdateProductHandler(): AdminApiHandler;
26
+
27
+ export { type CatalogAdminPageData, type CatalogAdminPageLoad, type ProductListRow, createCreateProductHandler, createDiscontinueProductHandler, createUpdateProductHandler, formatYen, imagesText, loadCatalogAdminPage };
@@ -0,0 +1,21 @@
1
+ import {
2
+ createCreateProductHandler,
3
+ createDiscontinueProductHandler,
4
+ createUpdateProductHandler,
5
+ formatYen,
6
+ imagesText,
7
+ loadCatalogAdminPage
8
+ } from "../chunk-TIK7KC4O.js";
9
+ import "../chunk-W2O6EQWT.js";
10
+ import "../chunk-E7ATMICY.js";
11
+ import "../chunk-ODK4R57S.js";
12
+ import "../chunk-ZEA6G75Z.js";
13
+ export {
14
+ createCreateProductHandler,
15
+ createDiscontinueProductHandler,
16
+ createUpdateProductHandler,
17
+ formatYen,
18
+ imagesText,
19
+ loadCatalogAdminPage
20
+ };
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,24 @@
1
+ // src/carriers.ts
2
+ var CARRIERS = ["yamato", "sagawa", "japanpost", "other"];
3
+ var CARRIER_LABELS = {
4
+ yamato: "\u30E4\u30DE\u30C8\u904B\u8F38",
5
+ sagawa: "\u4F50\u5DDD\u6025\u4FBF",
6
+ japanpost: "\u65E5\u672C\u90F5\u4FBF",
7
+ other: "\u305D\u306E\u4ED6"
8
+ };
9
+ function isCarrier(value) {
10
+ return typeof value === "string" && CARRIERS.includes(value);
11
+ }
12
+ function parseOptionalCarrier(value) {
13
+ if (value === void 0 || value === null || value === "") return null;
14
+ if (!isCarrier(value)) return { error: "Invalid carrier" };
15
+ return value;
16
+ }
17
+
18
+ export {
19
+ CARRIERS,
20
+ CARRIER_LABELS,
21
+ isCarrier,
22
+ parseOptionalCarrier
23
+ };
24
+ //# sourceMappingURL=chunk-4DR4K72B.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/carriers.ts"],"sourcesContent":["export const CARRIERS = [\"yamato\", \"sagawa\", \"japanpost\", \"other\"] as const;\n\nexport type Carrier = (typeof CARRIERS)[number];\n\nexport const CARRIER_LABELS: Record<Carrier, string> = {\n yamato: \"ヤマト運輸\",\n sagawa: \"佐川急便\",\n japanpost: \"日本郵便\",\n other: \"その他\",\n};\n\nexport function isCarrier(value: unknown): value is Carrier {\n return typeof value === \"string\" && (CARRIERS as readonly string[]).includes(value);\n}\n\nexport function parseOptionalCarrier(value: unknown): Carrier | null | { error: string } {\n if (value === undefined || value === null || value === \"\") return null;\n if (!isCarrier(value)) return { error: \"Invalid carrier\" };\n return value;\n}\n"],"mappings":";AAAO,IAAM,WAAW,CAAC,UAAU,UAAU,aAAa,OAAO;AAI1D,IAAM,iBAA0C;AAAA,EACrD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,OAAO;AACT;AAEO,SAAS,UAAU,OAAkC;AAC1D,SAAO,OAAO,UAAU,YAAa,SAA+B,SAAS,KAAK;AACpF;AAEO,SAAS,qBAAqB,OAAoD;AACvF,MAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,GAAI,QAAO;AAClE,MAAI,CAAC,UAAU,KAAK,EAAG,QAAO,EAAE,OAAO,kBAAkB;AACzD,SAAO;AACT;","names":[]}
@@ -0,0 +1,51 @@
1
+ // src/http.ts
2
+ var NO_STORE_JSON = {
3
+ "Content-Type": "application/json",
4
+ "Cache-Control": "no-store"
5
+ };
6
+ var NO_STORE_CSV = {
7
+ "Content-Type": "text/csv; charset=utf-8",
8
+ "Cache-Control": "no-store"
9
+ };
10
+ function jsonError(error, status) {
11
+ return new Response(JSON.stringify({ error }), {
12
+ status,
13
+ headers: NO_STORE_JSON
14
+ });
15
+ }
16
+ function jsonOk(body = { ok: true }, status = 200) {
17
+ return new Response(JSON.stringify(body), {
18
+ status,
19
+ headers: NO_STORE_JSON
20
+ });
21
+ }
22
+ async function readJsonObject(request) {
23
+ let payload;
24
+ try {
25
+ payload = await request.json();
26
+ } catch {
27
+ return jsonError("Invalid request body", 400);
28
+ }
29
+ if (typeof payload !== "object" || payload === null) {
30
+ return jsonError("Invalid request body", 400);
31
+ }
32
+ return payload;
33
+ }
34
+ function csvAttachment(filename, body) {
35
+ return new Response(body, {
36
+ headers: {
37
+ ...NO_STORE_CSV,
38
+ "Content-Disposition": `attachment; filename="${filename}"`
39
+ }
40
+ });
41
+ }
42
+
43
+ export {
44
+ NO_STORE_JSON,
45
+ NO_STORE_CSV,
46
+ jsonError,
47
+ jsonOk,
48
+ readJsonObject,
49
+ csvAttachment
50
+ };
51
+ //# sourceMappingURL=chunk-E7ATMICY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/http.ts"],"sourcesContent":["export type AdminApiHandler = (context: { request: Request }) => Promise<Response>;\n\nexport const NO_STORE_JSON = {\n \"Content-Type\": \"application/json\",\n \"Cache-Control\": \"no-store\",\n} as const;\n\nexport const NO_STORE_CSV = {\n \"Content-Type\": \"text/csv; charset=utf-8\",\n \"Cache-Control\": \"no-store\",\n} as const;\n\nexport function jsonError(error: string, status: number) {\n return new Response(JSON.stringify({ error }), {\n status,\n headers: NO_STORE_JSON,\n });\n}\n\nexport function jsonOk(body: unknown = { ok: true }, status = 200) {\n return new Response(JSON.stringify(body), {\n status,\n headers: NO_STORE_JSON,\n });\n}\n\nexport async function readJsonObject(\n request: Request,\n): Promise<Record<string, unknown> | Response> {\n let payload: unknown;\n try {\n payload = await request.json();\n } catch {\n return jsonError(\"Invalid request body\", 400);\n }\n\n if (typeof payload !== \"object\" || payload === null) {\n return jsonError(\"Invalid request body\", 400);\n }\n\n return payload as Record<string, unknown>;\n}\n\nexport function csvAttachment(filename: string, body: string) {\n return new Response(body, {\n headers: {\n ...NO_STORE_CSV,\n \"Content-Disposition\": `attachment; filename=\"${filename}\"`,\n },\n });\n}\n"],"mappings":";AAEO,IAAM,gBAAgB;AAAA,EAC3B,gBAAgB;AAAA,EAChB,iBAAiB;AACnB;AAEO,IAAM,eAAe;AAAA,EAC1B,gBAAgB;AAAA,EAChB,iBAAiB;AACnB;AAEO,SAAS,UAAU,OAAe,QAAgB;AACvD,SAAO,IAAI,SAAS,KAAK,UAAU,EAAE,MAAM,CAAC,GAAG;AAAA,IAC7C;AAAA,IACA,SAAS;AAAA,EACX,CAAC;AACH;AAEO,SAAS,OAAO,OAAgB,EAAE,IAAI,KAAK,GAAG,SAAS,KAAK;AACjE,SAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;AAAA,IACxC;AAAA,IACA,SAAS;AAAA,EACX,CAAC;AACH;AAEA,eAAsB,eACpB,SAC6C;AAC7C,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,QAAQ,KAAK;AAAA,EAC/B,QAAQ;AACN,WAAO,UAAU,wBAAwB,GAAG;AAAA,EAC9C;AAEA,MAAI,OAAO,YAAY,YAAY,YAAY,MAAM;AACnD,WAAO,UAAU,wBAAwB,GAAG;AAAA,EAC9C;AAEA,SAAO;AACT;AAEO,SAAS,cAAc,UAAkB,MAAc;AAC5D,SAAO,IAAI,SAAS,MAAM;AAAA,IACxB,SAAS;AAAA,MACP,GAAG;AAAA,MACH,uBAAuB,yBAAyB,QAAQ;AAAA,IAC1D;AAAA,EACF,CAAC;AACH;","names":[]}
@@ -0,0 +1,57 @@
1
+ import {
2
+ loadProductNamesFromDb
3
+ } from "./chunk-ZEA6G75Z.js";
4
+
5
+ // src/unshipped-alert.ts
6
+ import { formatEmailFrom } from "@umec/core/config";
7
+ import { listUnshipped } from "@umec/core/db";
8
+ import { notifyUnshipped } from "@umec/core/notify";
9
+ var DEFAULT_ALERT_DAYS_THRESHOLD = 3;
10
+ var DAY_MS = 24 * 60 * 60 * 1e3;
11
+ function parseAlertDaysThreshold(value) {
12
+ if (!value) return DEFAULT_ALERT_DAYS_THRESHOLD;
13
+ const parsed = Number(value);
14
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ALERT_DAYS_THRESHOLD;
15
+ }
16
+ function createUnshippedAlerter(config) {
17
+ function resolveUnshippedAlertTo(env) {
18
+ if (typeof env.UNSHIPPED_ALERT_TO === "string" && env.UNSHIPPED_ALERT_TO.trim()) {
19
+ return env.UNSHIPPED_ALERT_TO.trim();
20
+ }
21
+ if (config.email.bcc) return config.email.bcc;
22
+ return null;
23
+ }
24
+ async function alertUnshippedOrders(env) {
25
+ if (!env.DB) {
26
+ console.error("[scheduled/unshipped] DB binding is not available");
27
+ return;
28
+ }
29
+ const alertTo = resolveUnshippedAlertTo(env);
30
+ if (!alertTo) {
31
+ console.error("[scheduled/unshipped] UNSHIPPED_ALERT_TO (or email.bcc) is not configured");
32
+ return;
33
+ }
34
+ const thresholdDays = parseAlertDaysThreshold(env.ALERT_DAYS_THRESHOLD);
35
+ const cutoff = Date.now() - thresholdDays * DAY_MS;
36
+ const orders = await listUnshipped(env.DB, cutoff);
37
+ if (orders.length === 0) return;
38
+ const names = await loadProductNamesFromDb(env.DB);
39
+ const products = Object.fromEntries(
40
+ Object.entries(names).map(([sku, name]) => [sku, { name }])
41
+ );
42
+ await notifyUnshipped(orders, env, {
43
+ alertFrom: formatEmailFrom({ from: config.email.from, fromName: `${config.brand.name} alerts` }),
44
+ alertTo,
45
+ alertSubject: `\u3010${config.brand.name}\u3011\u672A\u767A\u9001\u30A2\u30E9\u30FC\u30C8`,
46
+ products
47
+ });
48
+ }
49
+ return { resolveUnshippedAlertTo, alertUnshippedOrders };
50
+ }
51
+
52
+ export {
53
+ DEFAULT_ALERT_DAYS_THRESHOLD,
54
+ parseAlertDaysThreshold,
55
+ createUnshippedAlerter
56
+ };
57
+ //# sourceMappingURL=chunk-G3O62WUE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/unshipped-alert.ts"],"sourcesContent":["import { formatEmailFrom, type UmecConfig } from \"@umec/core/config\";\nimport { listUnshipped } from \"@umec/core/db\";\nimport { notifyUnshipped, type NotifyEnv } from \"@umec/core/notify\";\nimport type { ServerEnv } from \"@umec/core/env\";\nimport { loadProductNamesFromDb } from \"./products.js\";\n\nexport const DEFAULT_ALERT_DAYS_THRESHOLD = 3;\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\nexport type UnshippedAlertConfig = {\n brand: Pick<UmecConfig[\"brand\"], \"name\">;\n email: Pick<UmecConfig[\"email\"], \"from\" | \"fromName\" | \"bcc\">;\n};\n\nexport type AlertEnv = ServerEnv &\n NotifyEnv & {\n UNSHIPPED_ALERT_TO?: string;\n };\n\nexport function parseAlertDaysThreshold(value: string | undefined) {\n if (!value) return DEFAULT_ALERT_DAYS_THRESHOLD;\n const parsed = Number(value);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_ALERT_DAYS_THRESHOLD;\n}\n\nexport function createUnshippedAlerter(config: UnshippedAlertConfig) {\n function resolveUnshippedAlertTo(env: AlertEnv): string | null {\n if (typeof env.UNSHIPPED_ALERT_TO === \"string\" && env.UNSHIPPED_ALERT_TO.trim()) {\n return env.UNSHIPPED_ALERT_TO.trim();\n }\n if (config.email.bcc) return config.email.bcc;\n return null;\n }\n\n async function alertUnshippedOrders(env: AlertEnv): Promise<void> {\n if (!env.DB) {\n console.error(\"[scheduled/unshipped] DB binding is not available\");\n return;\n }\n\n const alertTo = resolveUnshippedAlertTo(env);\n if (!alertTo) {\n console.error(\"[scheduled/unshipped] UNSHIPPED_ALERT_TO (or email.bcc) is not configured\");\n return;\n }\n\n const thresholdDays = parseAlertDaysThreshold(env.ALERT_DAYS_THRESHOLD);\n const cutoff = Date.now() - thresholdDays * DAY_MS;\n const orders = await listUnshipped(env.DB, cutoff);\n if (orders.length === 0) return;\n\n const names = await loadProductNamesFromDb(env.DB);\n const products = Object.fromEntries(\n Object.entries(names).map(([sku, name]) => [sku, { name }]),\n );\n\n await notifyUnshipped(orders, env, {\n alertFrom: formatEmailFrom({ from: config.email.from, fromName: `${config.brand.name} alerts` }),\n alertTo,\n alertSubject: `【${config.brand.name}】未発送アラート`,\n products,\n });\n }\n\n return { resolveUnshippedAlertTo, alertUnshippedOrders };\n}\n"],"mappings":";;;;;AAAA,SAAS,uBAAwC;AACjD,SAAS,qBAAqB;AAC9B,SAAS,uBAAuC;AAIzC,IAAM,+BAA+B;AAC5C,IAAM,SAAS,KAAK,KAAK,KAAK;AAYvB,SAAS,wBAAwB,OAA2B;AACjE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,OAAO,KAAK;AAC3B,SAAO,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS;AAC1D;AAEO,SAAS,uBAAuB,QAA8B;AACnE,WAAS,wBAAwB,KAA8B;AAC7D,QAAI,OAAO,IAAI,uBAAuB,YAAY,IAAI,mBAAmB,KAAK,GAAG;AAC/E,aAAO,IAAI,mBAAmB,KAAK;AAAA,IACrC;AACA,QAAI,OAAO,MAAM,IAAK,QAAO,OAAO,MAAM;AAC1C,WAAO;AAAA,EACT;AAEA,iBAAe,qBAAqB,KAA8B;AAChE,QAAI,CAAC,IAAI,IAAI;AACX,cAAQ,MAAM,mDAAmD;AACjE;AAAA,IACF;AAEA,UAAM,UAAU,wBAAwB,GAAG;AAC3C,QAAI,CAAC,SAAS;AACZ,cAAQ,MAAM,2EAA2E;AACzF;AAAA,IACF;AAEA,UAAM,gBAAgB,wBAAwB,IAAI,oBAAoB;AACtE,UAAM,SAAS,KAAK,IAAI,IAAI,gBAAgB;AAC5C,UAAM,SAAS,MAAM,cAAc,IAAI,IAAI,MAAM;AACjD,QAAI,OAAO,WAAW,EAAG;AAEzB,UAAM,QAAQ,MAAM,uBAAuB,IAAI,EAAE;AACjD,UAAM,WAAW,OAAO;AAAA,MACtB,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAAA,IAC5D;AAEA,UAAM,gBAAgB,QAAQ,KAAK;AAAA,MACjC,WAAW,gBAAgB,EAAE,MAAM,OAAO,MAAM,MAAM,UAAU,GAAG,OAAO,MAAM,IAAI,UAAU,CAAC;AAAA,MAC/F;AAAA,MACA,cAAc,SAAI,OAAO,MAAM,IAAI;AAAA,MACnC;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,yBAAyB,qBAAqB;AACzD;","names":[]}
@@ -0,0 +1,131 @@
1
+ import {
2
+ isValidGtin,
3
+ isValidSku,
4
+ parseProductImages
5
+ } from "./chunk-ZEA6G75Z.js";
6
+
7
+ // src/product-fields.ts
8
+ function asOptionalString(value) {
9
+ if (value === void 0 || value === null || value === "") return null;
10
+ if (typeof value !== "string") return { error: "Invalid string field" };
11
+ const trimmed = value.trim();
12
+ return trimmed === "" ? null : trimmed;
13
+ }
14
+ function parseSku(value) {
15
+ if (!isValidSku(value)) return { error: "Invalid sku" };
16
+ return value;
17
+ }
18
+ function parseCreateProductFields(payload) {
19
+ if (typeof payload.name !== "string" || payload.name.trim() === "") {
20
+ return { error: "Invalid name" };
21
+ }
22
+ if (typeof payload.price !== "number" || !Number.isInteger(payload.price) || payload.price <= 0) {
23
+ return { error: "Invalid price" };
24
+ }
25
+ const shippingUnits = payload.shipping_units === void 0 ? 1 : payload.shipping_units;
26
+ if (typeof shippingUnits !== "number" || !Number.isInteger(shippingUnits) || shippingUnits <= 0) {
27
+ return { error: "Invalid shipping_units" };
28
+ }
29
+ const images = parseProductImages(payload.images);
30
+ if (!Array.isArray(images)) return images;
31
+ const description = asOptionalString(payload.description);
32
+ if (isFieldError(description)) return description;
33
+ const gtin = asOptionalString(payload.gtin);
34
+ if (isFieldError(gtin)) return gtin;
35
+ if (gtin && !isValidGtin(gtin)) return { error: "Invalid gtin" };
36
+ const category = asOptionalString(payload.category);
37
+ if (isFieldError(category)) return category;
38
+ let active = 1;
39
+ if (payload.active !== void 0) {
40
+ if (payload.active === true || payload.active === 1) active = 1;
41
+ else if (payload.active === false || payload.active === 0) active = 0;
42
+ else return { error: "Invalid active" };
43
+ }
44
+ let lowStockThreshold = null;
45
+ if (payload.low_stock_threshold !== void 0 && payload.low_stock_threshold !== null && payload.low_stock_threshold !== "") {
46
+ if (typeof payload.low_stock_threshold !== "number" || !Number.isInteger(payload.low_stock_threshold) || payload.low_stock_threshold < 0) {
47
+ return { error: "Invalid low_stock_threshold" };
48
+ }
49
+ lowStockThreshold = payload.low_stock_threshold;
50
+ }
51
+ return {
52
+ name: payload.name.trim(),
53
+ description,
54
+ price: payload.price,
55
+ shippingUnits,
56
+ images,
57
+ gtin,
58
+ category,
59
+ active,
60
+ lowStockThreshold
61
+ };
62
+ }
63
+ function parseUpdateProductFields(payload) {
64
+ const next = {};
65
+ if (payload.name !== void 0) {
66
+ if (typeof payload.name !== "string" || payload.name.trim() === "") {
67
+ return { error: "Invalid name" };
68
+ }
69
+ next.name = payload.name.trim();
70
+ }
71
+ if (payload.description !== void 0) {
72
+ const description = asOptionalString(payload.description);
73
+ if (isFieldError(description)) return description;
74
+ next.description = description;
75
+ }
76
+ if (payload.price !== void 0) {
77
+ if (typeof payload.price !== "number" || !Number.isInteger(payload.price) || payload.price <= 0) {
78
+ return { error: "Invalid price" };
79
+ }
80
+ next.price = payload.price;
81
+ }
82
+ if (payload.shipping_units !== void 0) {
83
+ if (typeof payload.shipping_units !== "number" || !Number.isInteger(payload.shipping_units) || payload.shipping_units <= 0) {
84
+ return { error: "Invalid shipping_units" };
85
+ }
86
+ next.shippingUnits = payload.shipping_units;
87
+ }
88
+ if (payload.images !== void 0) {
89
+ const images = parseProductImages(payload.images);
90
+ if (!Array.isArray(images)) return images;
91
+ next.images = images;
92
+ }
93
+ if (payload.gtin !== void 0) {
94
+ const gtin = asOptionalString(payload.gtin);
95
+ if (isFieldError(gtin)) return gtin;
96
+ if (gtin && !isValidGtin(gtin)) return { error: "Invalid gtin" };
97
+ next.gtin = gtin;
98
+ }
99
+ if (payload.category !== void 0) {
100
+ const category = asOptionalString(payload.category);
101
+ if (isFieldError(category)) return category;
102
+ next.category = category;
103
+ }
104
+ if (payload.active !== void 0) {
105
+ if (payload.active === true || payload.active === 1) next.active = 1;
106
+ else if (payload.active === false || payload.active === 0) next.active = 0;
107
+ else return { error: "Invalid active" };
108
+ }
109
+ if (payload.low_stock_threshold !== void 0) {
110
+ if (payload.low_stock_threshold === null || payload.low_stock_threshold === "") {
111
+ next.lowStockThreshold = null;
112
+ } else if (typeof payload.low_stock_threshold !== "number" || !Number.isInteger(payload.low_stock_threshold) || payload.low_stock_threshold < 0) {
113
+ return { error: "Invalid low_stock_threshold" };
114
+ } else {
115
+ next.lowStockThreshold = payload.low_stock_threshold;
116
+ }
117
+ }
118
+ if (Object.keys(next).length === 0) return { error: "No fields to update" };
119
+ return next;
120
+ }
121
+ function isFieldError(value) {
122
+ return typeof value === "object" && value !== null && "error" in value && Object.keys(value).length === 1;
123
+ }
124
+
125
+ export {
126
+ parseSku,
127
+ parseCreateProductFields,
128
+ parseUpdateProductFields,
129
+ isFieldError
130
+ };
131
+ //# sourceMappingURL=chunk-ODK4R57S.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/product-fields.ts"],"sourcesContent":["import type { ProductWriteFields } from \"@umec/core/db\";\nimport { isValidGtin, isValidSku, parseProductImages } from \"./products.js\";\n\nexport type { ProductWriteFields };\n\nexport type FieldError = { error: string };\n\nfunction asOptionalString(value: unknown): string | null | FieldError {\n if (value === undefined || value === null || value === \"\") return null;\n if (typeof value !== \"string\") return { error: \"Invalid string field\" };\n const trimmed = value.trim();\n return trimmed === \"\" ? null : trimmed;\n}\n\nexport function parseSku(value: unknown): string | FieldError {\n if (!isValidSku(value)) return { error: \"Invalid sku\" };\n return value;\n}\n\nexport function parseCreateProductFields(payload: Record<string, unknown>): ProductWriteFields | FieldError {\n if (typeof payload.name !== \"string\" || payload.name.trim() === \"\") {\n return { error: \"Invalid name\" };\n }\n if (typeof payload.price !== \"number\" || !Number.isInteger(payload.price) || payload.price <= 0) {\n return { error: \"Invalid price\" };\n }\n\n const shippingUnits = payload.shipping_units === undefined ? 1 : payload.shipping_units;\n if (typeof shippingUnits !== \"number\" || !Number.isInteger(shippingUnits) || shippingUnits <= 0) {\n return { error: \"Invalid shipping_units\" };\n }\n\n const images = parseProductImages(payload.images);\n if (!Array.isArray(images)) return images;\n\n const description = asOptionalString(payload.description);\n if (isFieldError(description)) return description;\n\n const gtin = asOptionalString(payload.gtin);\n if (isFieldError(gtin)) return gtin;\n if (gtin && !isValidGtin(gtin)) return { error: \"Invalid gtin\" };\n\n const category = asOptionalString(payload.category);\n if (isFieldError(category)) return category;\n\n let active: 0 | 1 = 1;\n if (payload.active !== undefined) {\n if (payload.active === true || payload.active === 1) active = 1;\n else if (payload.active === false || payload.active === 0) active = 0;\n else return { error: \"Invalid active\" };\n }\n\n let lowStockThreshold: number | null = null;\n if (\n payload.low_stock_threshold !== undefined &&\n payload.low_stock_threshold !== null &&\n payload.low_stock_threshold !== \"\"\n ) {\n if (\n typeof payload.low_stock_threshold !== \"number\" ||\n !Number.isInteger(payload.low_stock_threshold) ||\n payload.low_stock_threshold < 0\n ) {\n return { error: \"Invalid low_stock_threshold\" };\n }\n lowStockThreshold = payload.low_stock_threshold;\n }\n\n return {\n name: payload.name.trim(),\n description,\n price: payload.price,\n shippingUnits,\n images,\n gtin,\n category,\n active,\n lowStockThreshold,\n };\n}\n\nexport function parseUpdateProductFields(\n payload: Record<string, unknown>,\n): Partial<ProductWriteFields> | FieldError {\n const next: Partial<ProductWriteFields> = {};\n\n if (payload.name !== undefined) {\n if (typeof payload.name !== \"string\" || payload.name.trim() === \"\") {\n return { error: \"Invalid name\" };\n }\n next.name = payload.name.trim();\n }\n\n if (payload.description !== undefined) {\n const description = asOptionalString(payload.description);\n if (isFieldError(description)) return description;\n next.description = description;\n }\n\n if (payload.price !== undefined) {\n if (typeof payload.price !== \"number\" || !Number.isInteger(payload.price) || payload.price <= 0) {\n return { error: \"Invalid price\" };\n }\n next.price = payload.price;\n }\n\n if (payload.shipping_units !== undefined) {\n if (\n typeof payload.shipping_units !== \"number\" ||\n !Number.isInteger(payload.shipping_units) ||\n payload.shipping_units <= 0\n ) {\n return { error: \"Invalid shipping_units\" };\n }\n next.shippingUnits = payload.shipping_units;\n }\n\n if (payload.images !== undefined) {\n const images = parseProductImages(payload.images);\n if (!Array.isArray(images)) return images;\n next.images = images;\n }\n\n if (payload.gtin !== undefined) {\n const gtin = asOptionalString(payload.gtin);\n if (isFieldError(gtin)) return gtin;\n if (gtin && !isValidGtin(gtin)) return { error: \"Invalid gtin\" };\n next.gtin = gtin;\n }\n\n if (payload.category !== undefined) {\n const category = asOptionalString(payload.category);\n if (isFieldError(category)) return category;\n next.category = category;\n }\n\n if (payload.active !== undefined) {\n if (payload.active === true || payload.active === 1) next.active = 1;\n else if (payload.active === false || payload.active === 0) next.active = 0;\n else return { error: \"Invalid active\" };\n }\n\n if (payload.low_stock_threshold !== undefined) {\n if (payload.low_stock_threshold === null || payload.low_stock_threshold === \"\") {\n next.lowStockThreshold = null;\n } else if (\n typeof payload.low_stock_threshold !== \"number\" ||\n !Number.isInteger(payload.low_stock_threshold) ||\n payload.low_stock_threshold < 0\n ) {\n return { error: \"Invalid low_stock_threshold\" };\n } else {\n next.lowStockThreshold = payload.low_stock_threshold;\n }\n }\n\n if (Object.keys(next).length === 0) return { error: \"No fields to update\" };\n return next;\n}\n\nexport function isFieldError(value: unknown): value is FieldError {\n return typeof value === \"object\" && value !== null && \"error\" in value && Object.keys(value).length === 1;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,27 @@
1
+ // src/offers.ts
2
+ import { getServerEnv } from "@umec/core/env";
3
+ var SAMPLE_FREE_SHIPPING_OFFER_SLUG = "sample-free-shipping";
4
+ var DEFAULT_OFFERS = {
5
+ [SAMPLE_FREE_SHIPPING_OFFER_SLUG]: {
6
+ priceOverrides: {},
7
+ freeShipping: true
8
+ }
9
+ };
10
+ function defaultIsVerificationRun() {
11
+ const env = getServerEnv();
12
+ return typeof env.UMEC_VERIFY_RUN_ID === "string" && env.UMEC_VERIFY_RUN_ID !== "";
13
+ }
14
+ function createResolveCheckoutOffer(options = {}) {
15
+ const offers = options.offers ?? DEFAULT_OFFERS;
16
+ const isVerificationRun = options.isVerificationRun ?? defaultIsVerificationRun;
17
+ return async (offerSlug) => {
18
+ if (!isVerificationRun()) return null;
19
+ return offers[offerSlug] ?? null;
20
+ };
21
+ }
22
+
23
+ export {
24
+ SAMPLE_FREE_SHIPPING_OFFER_SLUG,
25
+ createResolveCheckoutOffer
26
+ };
27
+ //# sourceMappingURL=chunk-RK7FIONL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/offers.ts"],"sourcesContent":["import type { CheckOffer, CheckoutOffer } from \"@umec/core/checkout\";\nimport { getServerEnv } from \"@umec/core/env\";\n\nexport const SAMPLE_FREE_SHIPPING_OFFER_SLUG = \"sample-free-shipping\";\n\nconst DEFAULT_OFFERS: Record<string, CheckoutOffer> = {\n [SAMPLE_FREE_SHIPPING_OFFER_SLUG]: {\n priceOverrides: {},\n freeShipping: true,\n },\n};\n\nexport type ResolveCheckoutOfferOptions = {\n offers?: Record<string, CheckoutOffer>;\n isVerificationRun?: () => boolean;\n};\n\nfunction defaultIsVerificationRun(): boolean {\n const env = getServerEnv() as ReturnType<typeof getServerEnv> & {\n UMEC_VERIFY_RUN_ID?: string;\n };\n return typeof env.UMEC_VERIFY_RUN_ID === \"string\" && env.UMEC_VERIFY_RUN_ID !== \"\";\n}\n\nexport function createResolveCheckoutOffer(\n options: ResolveCheckoutOfferOptions = {},\n): CheckOffer {\n const offers = options.offers ?? DEFAULT_OFFERS;\n const isVerificationRun = options.isVerificationRun ?? defaultIsVerificationRun;\n\n return async (offerSlug) => {\n if (!isVerificationRun()) return null;\n return offers[offerSlug] ?? null;\n };\n}\n"],"mappings":";AACA,SAAS,oBAAoB;AAEtB,IAAM,kCAAkC;AAE/C,IAAM,iBAAgD;AAAA,EACpD,CAAC,+BAA+B,GAAG;AAAA,IACjC,gBAAgB,CAAC;AAAA,IACjB,cAAc;AAAA,EAChB;AACF;AAOA,SAAS,2BAAoC;AAC3C,QAAM,MAAM,aAAa;AAGzB,SAAO,OAAO,IAAI,uBAAuB,YAAY,IAAI,uBAAuB;AAClF;AAEO,SAAS,2BACd,UAAuC,CAAC,GAC5B;AACZ,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,oBAAoB,QAAQ,qBAAqB;AAEvD,SAAO,OAAO,cAAc;AAC1B,QAAI,CAAC,kBAAkB,EAAG,QAAO;AACjC,WAAO,OAAO,SAAS,KAAK;AAAA,EAC9B;AACF;","names":[]}