includio-cms 0.26.0 → 0.28.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 (128) hide show
  1. package/API.md +58 -2
  2. package/CHANGELOG.md +105 -0
  3. package/DOCS.md +1 -1
  4. package/ROADMAP.md +8 -0
  5. package/dist/admin/auth-client.d.ts +42 -42
  6. package/dist/admin/client/admin/admin-layout.svelte +12 -2
  7. package/dist/admin/client/admin/admin-layout.svelte.d.ts +2 -1
  8. package/dist/admin/client/collection/data-table.svelte +0 -39
  9. package/dist/admin/client/collection/data-table.svelte.d.ts +0 -2
  10. package/dist/admin/client/shop/coupon-schema.d.ts +1 -1
  11. package/dist/admin/client/shop/refund-dialog.svelte +37 -1
  12. package/dist/admin/client/shop/refund-dialog.svelte.d.ts +3 -0
  13. package/dist/admin/client/shop/shop-order-detail-page.svelte +192 -0
  14. package/dist/admin/components/fields/field-renderer.svelte +6 -1
  15. package/dist/admin/components/fields/icon-field.svelte +86 -0
  16. package/dist/admin/components/fields/icon-field.svelte.d.ts +8 -0
  17. package/dist/admin/components/fields/icon-picker-dialog.svelte +174 -0
  18. package/dist/admin/components/fields/icon-picker-dialog.svelte.d.ts +11 -0
  19. package/dist/admin/components/fields/object-field.svelte +27 -7
  20. package/dist/admin/components/fields/shop-field.svelte +210 -20
  21. package/dist/admin/components/layout/layout-tabs.svelte +1 -0
  22. package/dist/admin/components/variant-form/VariantAttributeRenderer.svelte +109 -0
  23. package/dist/admin/components/variant-form/VariantAttributeRenderer.svelte.d.ts +9 -0
  24. package/dist/admin/helpers/build-icon-set-map.d.ts +8 -0
  25. package/dist/admin/helpers/build-icon-set-map.js +16 -0
  26. package/dist/admin/helpers/index.d.ts +2 -0
  27. package/dist/admin/helpers/index.js +2 -0
  28. package/dist/admin/remote/shop.remote.d.ts +116 -24
  29. package/dist/admin/remote/shop.remote.js +79 -6
  30. package/dist/admin/state/icon-sets.svelte.d.ts +9 -0
  31. package/dist/admin/state/icon-sets.svelte.js +20 -0
  32. package/dist/cli/scaffold/admin.js +2 -2
  33. package/dist/components/ui/checkbox/checkbox.svelte +3 -3
  34. package/dist/core/cms.d.ts +11 -2
  35. package/dist/core/cms.js +29 -0
  36. package/dist/core/fields/fieldSchemaToTs.js +7 -0
  37. package/dist/core/server/generator/fields.d.ts +2 -0
  38. package/dist/core/server/generator/fields.js +34 -1
  39. package/dist/core/server/generator/generator.js +2 -1
  40. package/dist/db-postgres/schema/shop/index.d.ts +1 -0
  41. package/dist/db-postgres/schema/shop/index.js +1 -0
  42. package/dist/db-postgres/schema/shop/invoice.d.ts +254 -0
  43. package/dist/db-postgres/schema/shop/invoice.js +27 -0
  44. package/dist/db-postgres/schema/shop/order.d.ts +107 -1
  45. package/dist/db-postgres/schema/shop/order.js +7 -1
  46. package/dist/db-postgres/schema/shop/payment.d.ts +20 -0
  47. package/dist/db-postgres/schema/shop/payment.js +4 -1
  48. package/dist/db-postgres/schema/shop/product.d.ts +20 -0
  49. package/dist/db-postgres/schema/shop/product.js +3 -1
  50. package/dist/db-postgres/schema/shop/productVariant.d.ts +12 -2
  51. package/dist/db-postgres/schema/shop/productVariant.js +22 -0
  52. package/dist/paraglide/messages/_index.d.ts +36 -3
  53. package/dist/paraglide/messages/_index.js +71 -3
  54. package/dist/paraglide/messages/en.d.ts +5 -0
  55. package/dist/paraglide/messages/en.js +14 -0
  56. package/dist/paraglide/messages/pl.d.ts +5 -0
  57. package/dist/paraglide/messages/pl.js +14 -0
  58. package/dist/shop/adapters/fakturownia/client.d.ts +28 -0
  59. package/dist/shop/adapters/fakturownia/client.js +67 -0
  60. package/dist/shop/adapters/fakturownia/index.d.ts +27 -0
  61. package/dist/shop/adapters/fakturownia/index.js +36 -0
  62. package/dist/shop/adapters/fakturownia/payload.d.ts +35 -0
  63. package/dist/shop/adapters/fakturownia/payload.js +45 -0
  64. package/dist/shop/cart/types.d.ts +1 -0
  65. package/dist/shop/client/index.d.ts +61 -0
  66. package/dist/shop/client/index.js +5 -1
  67. package/dist/shop/expiry.d.ts +35 -0
  68. package/dist/shop/expiry.js +68 -0
  69. package/dist/shop/http/balance-handler.d.ts +20 -0
  70. package/dist/shop/http/balance-handler.js +91 -0
  71. package/dist/shop/http/cart-handler.js +19 -0
  72. package/dist/shop/http/checkout-handler.js +30 -1
  73. package/dist/shop/http/index.d.ts +2 -0
  74. package/dist/shop/http/index.js +2 -0
  75. package/dist/shop/http/upcoming-handler.d.ts +16 -0
  76. package/dist/shop/http/upcoming-handler.js +65 -0
  77. package/dist/shop/http/webhook-handler.js +46 -9
  78. package/dist/shop/index.d.ts +7 -1
  79. package/dist/shop/index.js +10 -1
  80. package/dist/shop/nip.d.ts +12 -0
  81. package/dist/shop/nip.js +23 -0
  82. package/dist/shop/server/balance-payment.d.ts +40 -0
  83. package/dist/shop/server/balance-payment.js +140 -0
  84. package/dist/shop/server/cart-hydrate.js +2 -0
  85. package/dist/shop/server/init.d.ts +14 -0
  86. package/dist/shop/server/init.js +35 -0
  87. package/dist/shop/server/invoices.d.ts +64 -0
  88. package/dist/shop/server/invoices.js +237 -0
  89. package/dist/shop/server/orders.d.ts +38 -0
  90. package/dist/shop/server/orders.js +152 -2
  91. package/dist/shop/server/payment-policy.d.ts +35 -0
  92. package/dist/shop/server/payment-policy.js +55 -0
  93. package/dist/shop/server/payments.d.ts +29 -0
  94. package/dist/shop/server/payments.js +64 -0
  95. package/dist/shop/server/populate.d.ts +1 -1
  96. package/dist/shop/server/refund.d.ts +17 -12
  97. package/dist/shop/server/refund.js +96 -13
  98. package/dist/shop/server/shop-data.d.ts +4 -1
  99. package/dist/shop/server/shop-data.js +24 -2
  100. package/dist/shop/template.d.ts +13 -0
  101. package/dist/shop/template.js +98 -0
  102. package/dist/shop/types.d.ts +208 -1
  103. package/dist/shop/variant-attributes.d.ts +28 -0
  104. package/dist/shop/variant-attributes.js +69 -0
  105. package/dist/sveltekit/server/index.d.ts +1 -0
  106. package/dist/sveltekit/server/index.js +2 -0
  107. package/dist/types/cms.d.ts +4 -3
  108. package/dist/types/cms.schema.d.ts +1 -1
  109. package/dist/types/cms.schema.js +9 -0
  110. package/dist/types/fields.d.ts +21 -2
  111. package/dist/types/index.d.ts +1 -1
  112. package/dist/types/index.js +1 -1
  113. package/dist/types/plugins.d.ts +40 -0
  114. package/dist/types/plugins.js +4 -1
  115. package/dist/updates/0.26.1/index.d.ts +2 -0
  116. package/dist/updates/0.26.1/index.js +19 -0
  117. package/dist/updates/0.27.0/index.d.ts +2 -0
  118. package/dist/updates/0.27.0/index.js +50 -0
  119. package/dist/updates/0.28.0/index.d.ts +2 -0
  120. package/dist/updates/0.28.0/index.js +38 -0
  121. package/dist/updates/index.js +7 -1
  122. package/package.json +1 -1
  123. package/dist/paraglide/messages/hello_world.d.ts +0 -5
  124. package/dist/paraglide/messages/hello_world.js +0 -33
  125. package/dist/paraglide/messages/login_hello.d.ts +0 -16
  126. package/dist/paraglide/messages/login_hello.js +0 -34
  127. package/dist/paraglide/messages/login_please_login.d.ts +0 -16
  128. package/dist/paraglide/messages/login_please_login.js +0 -34
@@ -1,3 +1,36 @@
1
- export * from "./hello_world.js";
2
- export * from "./login_hello.js";
3
- export * from "./login_please_login.js";
1
+ export function hello_world(inputs: {
2
+ name: NonNullable<unknown>;
3
+ }, options?: {
4
+ locale?: "en" | "pl";
5
+ }): string;
6
+ /**
7
+ * This function has been compiled by [Paraglide JS](https://inlang.com/m/gerre34r).
8
+ *
9
+ * - Changing this function will be over-written by the next build.
10
+ *
11
+ * - If you want to change the translations, you can either edit the source files e.g. `en.json`, or
12
+ * use another inlang app like [Fink](https://inlang.com/m/tdozzpar) or the [VSCode extension Sherlock](https://inlang.com/m/r7kp499g).
13
+ *
14
+ * @param {{}} inputs
15
+ * @param {{ locale?: "en" | "pl" }} options
16
+ * @returns {string}
17
+ */
18
+ declare function login_hello(inputs?: {}, options?: {
19
+ locale?: "en" | "pl";
20
+ }): string;
21
+ /**
22
+ * This function has been compiled by [Paraglide JS](https://inlang.com/m/gerre34r).
23
+ *
24
+ * - Changing this function will be over-written by the next build.
25
+ *
26
+ * - If you want to change the translations, you can either edit the source files e.g. `en.json`, or
27
+ * use another inlang app like [Fink](https://inlang.com/m/tdozzpar) or the [VSCode extension Sherlock](https://inlang.com/m/r7kp499g).
28
+ *
29
+ * @param {{}} inputs
30
+ * @param {{ locale?: "en" | "pl" }} options
31
+ * @returns {string}
32
+ */
33
+ declare function login_please_login(inputs?: {}, options?: {
34
+ locale?: "en" | "pl";
35
+ }): string;
36
+ export { login_hello as login.hello, login_please_login as login.please_login };
@@ -1,4 +1,72 @@
1
1
  /* eslint-disable */
2
- export * from './hello_world.js'
3
- export * from './login_hello.js'
4
- export * from './login_please_login.js'
2
+ import { getLocale, trackMessageCall, experimentalMiddlewareLocaleSplitting, isServer } from "../runtime.js"
3
+ import * as en from "./en.js"
4
+ import * as pl from "./pl.js"
5
+ /**
6
+ * This function has been compiled by [Paraglide JS](https://inlang.com/m/gerre34r).
7
+ *
8
+ * - Changing this function will be over-written by the next build.
9
+ *
10
+ * - If you want to change the translations, you can either edit the source files e.g. `en.json`, or
11
+ * use another inlang app like [Fink](https://inlang.com/m/tdozzpar) or the [VSCode extension Sherlock](https://inlang.com/m/r7kp499g).
12
+ *
13
+ * @param {{ name: NonNullable<unknown> }} inputs
14
+ * @param {{ locale?: "en" | "pl" }} options
15
+ * @returns {string}
16
+ */
17
+ /* @__NO_SIDE_EFFECTS__ */
18
+ export const hello_world = (inputs, options = {}) => {
19
+ if (experimentalMiddlewareLocaleSplitting && isServer === false) {
20
+ return /** @type {any} */ (globalThis).__paraglide_ssr.hello_world(inputs)
21
+ }
22
+ const locale = options.locale ?? getLocale()
23
+ trackMessageCall("hello_world", locale)
24
+ if (locale === "en") return en.hello_world(inputs)
25
+ return pl.hello_world(inputs)
26
+ };
27
+ /**
28
+ * This function has been compiled by [Paraglide JS](https://inlang.com/m/gerre34r).
29
+ *
30
+ * - Changing this function will be over-written by the next build.
31
+ *
32
+ * - If you want to change the translations, you can either edit the source files e.g. `en.json`, or
33
+ * use another inlang app like [Fink](https://inlang.com/m/tdozzpar) or the [VSCode extension Sherlock](https://inlang.com/m/r7kp499g).
34
+ *
35
+ * @param {{}} inputs
36
+ * @param {{ locale?: "en" | "pl" }} options
37
+ * @returns {string}
38
+ */
39
+ /* @__NO_SIDE_EFFECTS__ */
40
+ const login_hello = (inputs = {}, options = {}) => {
41
+ if (experimentalMiddlewareLocaleSplitting && isServer === false) {
42
+ return /** @type {any} */ (globalThis).__paraglide_ssr.login_hello(inputs)
43
+ }
44
+ const locale = options.locale ?? getLocale()
45
+ trackMessageCall("login_hello", locale)
46
+ if (locale === "en") return en.login_hello(inputs)
47
+ return pl.login_hello(inputs)
48
+ };
49
+ export { login_hello as "login.hello" }
50
+ /**
51
+ * This function has been compiled by [Paraglide JS](https://inlang.com/m/gerre34r).
52
+ *
53
+ * - Changing this function will be over-written by the next build.
54
+ *
55
+ * - If you want to change the translations, you can either edit the source files e.g. `en.json`, or
56
+ * use another inlang app like [Fink](https://inlang.com/m/tdozzpar) or the [VSCode extension Sherlock](https://inlang.com/m/r7kp499g).
57
+ *
58
+ * @param {{}} inputs
59
+ * @param {{ locale?: "en" | "pl" }} options
60
+ * @returns {string}
61
+ */
62
+ /* @__NO_SIDE_EFFECTS__ */
63
+ const login_please_login = (inputs = {}, options = {}) => {
64
+ if (experimentalMiddlewareLocaleSplitting && isServer === false) {
65
+ return /** @type {any} */ (globalThis).__paraglide_ssr.login_please_login(inputs)
66
+ }
67
+ const locale = options.locale ?? getLocale()
68
+ trackMessageCall("login_please_login", locale)
69
+ if (locale === "en") return en.login_please_login(inputs)
70
+ return pl.login_please_login(inputs)
71
+ };
72
+ export { login_please_login as "login.please_login" }
@@ -0,0 +1,5 @@
1
+ export const hello_world: (inputs: {
2
+ name: NonNullable<unknown>;
3
+ }) => string;
4
+ export const login_hello: (inputs: {}) => string;
5
+ export const login_please_login: (inputs: {}) => string;
@@ -0,0 +1,14 @@
1
+ /* eslint-disable */
2
+
3
+
4
+ export const hello_world = /** @type {(inputs: { name: NonNullable<unknown> }) => string} */ (i) => {
5
+ return `Hello, ${i.name} from en!`
6
+ };
7
+
8
+ export const login_hello = /** @type {(inputs: {}) => string} */ () => {
9
+ return `Welcome back`
10
+ };
11
+
12
+ export const login_please_login = /** @type {(inputs: {}) => string} */ () => {
13
+ return `Login to your account`
14
+ };
@@ -0,0 +1,5 @@
1
+ export const hello_world: (inputs: {
2
+ name: NonNullable<unknown>;
3
+ }) => string;
4
+ export const login_hello: (inputs: {}) => string;
5
+ export const login_please_login: (inputs: {}) => string;
@@ -0,0 +1,14 @@
1
+ /* eslint-disable */
2
+
3
+
4
+ export const hello_world = /** @type {(inputs: { name: NonNullable<unknown> }) => string} */ (i) => {
5
+ return `Hello, ${i.name} from pl!`
6
+ };
7
+
8
+ export const login_hello = /** @type {(inputs: {}) => string} */ () => {
9
+ return `Witaj ponownie`
10
+ };
11
+
12
+ export const login_please_login = /** @type {(inputs: {}) => string} */ () => {
13
+ return `Zaloguj się na swoje konto`
14
+ };
@@ -0,0 +1,28 @@
1
+ import type { FakturowniaInvoiceBody } from './payload.js';
2
+ export interface FakturowniaClientOptions {
3
+ /** Account subdomain (`acme`) or full host/URL (`acme.fakturownia.pl`) — normalised either way. */
4
+ domain: string;
5
+ apiToken: string;
6
+ fetch?: typeof fetch;
7
+ }
8
+ export interface FakturowniaInvoice {
9
+ id: number | string;
10
+ number?: string;
11
+ view_url?: string;
12
+ [key: string]: unknown;
13
+ }
14
+ export declare class FakturowniaApiError extends Error {
15
+ readonly status: number;
16
+ readonly body?: unknown | undefined;
17
+ constructor(message: string, status: number, body?: unknown | undefined);
18
+ }
19
+ export declare class FakturowniaClient {
20
+ private readonly base;
21
+ private readonly apiToken;
22
+ private readonly fetchFn;
23
+ constructor(opts: FakturowniaClientOptions);
24
+ createInvoice(invoice: FakturowniaInvoiceBody): Promise<FakturowniaInvoice>;
25
+ sendByEmail(id: number | string): Promise<void>;
26
+ private postRaw;
27
+ private post;
28
+ }
@@ -0,0 +1,67 @@
1
+ export class FakturowniaApiError extends Error {
2
+ status;
3
+ body;
4
+ constructor(message, status, body) {
5
+ super(message);
6
+ this.status = status;
7
+ this.body = body;
8
+ this.name = 'FakturowniaApiError';
9
+ }
10
+ }
11
+ export class FakturowniaClient {
12
+ base;
13
+ apiToken;
14
+ fetchFn;
15
+ constructor(opts) {
16
+ if (!opts.domain)
17
+ throw new Error('FakturowniaClient: domain required.');
18
+ if (!opts.apiToken)
19
+ throw new Error('FakturowniaClient: apiToken required.');
20
+ // Accept a bare subdomain (`acme`) as well as a full host or URL
21
+ // (`acme.fakturownia.pl`, `https://acme.fakturownia.pl/`) — strip protocol,
22
+ // the `.fakturownia.pl` suffix and trailing slashes so we never double it.
23
+ const subdomain = opts.domain
24
+ .trim()
25
+ .replace(/^https?:\/\//i, '')
26
+ .replace(/\/+$/, '')
27
+ .replace(/\.fakturownia\.pl$/i, '');
28
+ this.base = `https://${subdomain}.fakturownia.pl`;
29
+ this.apiToken = opts.apiToken;
30
+ this.fetchFn = opts.fetch ?? globalThis.fetch.bind(globalThis);
31
+ }
32
+ async createInvoice(invoice) {
33
+ return this.post('/invoices.json', {
34
+ api_token: this.apiToken,
35
+ invoice
36
+ });
37
+ }
38
+ async sendByEmail(id) {
39
+ await this.postRaw(`/invoices/${encodeURIComponent(String(id))}/send_by_email.json`, {
40
+ api_token: this.apiToken
41
+ });
42
+ }
43
+ async postRaw(path, payload) {
44
+ const res = await this.fetchFn(`${this.base}${path}`, {
45
+ method: 'POST',
46
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
47
+ body: JSON.stringify(payload)
48
+ });
49
+ if (!res.ok) {
50
+ let body;
51
+ try {
52
+ body = await res.json();
53
+ }
54
+ catch {
55
+ body = await res.text().catch(() => undefined);
56
+ }
57
+ const raw = body == null ? '' : typeof body === 'string' ? body : JSON.stringify(body);
58
+ const detail = raw ? `: ${raw.slice(0, 400)}` : '';
59
+ throw new FakturowniaApiError(`Fakturownia ${path} → ${res.status}${detail}`, res.status, body);
60
+ }
61
+ return res;
62
+ }
63
+ async post(path, payload) {
64
+ const res = await this.postRaw(path, payload);
65
+ return (await res.json());
66
+ }
67
+ }
@@ -0,0 +1,27 @@
1
+ import type { InvoiceIssuePolicy, InvoicingAdapter } from '../../types.js';
2
+ export interface FakturowniaAdapterOptions {
3
+ /** Account subdomain (`acme`) or full host (`acme.fakturownia.pl`). Required. */
4
+ domain: string;
5
+ /** Fakturownia API token. Required. Pass from `$env/dynamic/private`. */
6
+ apiToken: string;
7
+ /** Adapter id. Default `fakturownia`. */
8
+ id?: string;
9
+ /** Invoice kind sent to Fakturownia. Default `vat`. */
10
+ kind?: string;
11
+ /** Unit of measure per line — KSeF list: szt, godz, dni, mc, m2, kg. Default `szt`. */
12
+ unit?: string;
13
+ /** When to issue automatically. Default (server-side) `b2bAndOnRequest`. */
14
+ issueWhen?: InvoiceIssuePolicy;
15
+ /** E-mail the invoice to the buyer via Fakturownia. Default `true`. */
16
+ sendEmail?: boolean;
17
+ /** Override fetch — primarily for testing. */
18
+ fetch?: typeof fetch;
19
+ }
20
+ /**
21
+ * Invoicing adapter backed by Fakturownia (fakturownia.pl). Issues a paid VAT
22
+ * invoice from a fully-paid order and (by default) e-mails the PDF to the buyer
23
+ * provider-side. Seller data and numbering are managed in the Fakturownia
24
+ * account; the NIP is validated upstream at checkout.
25
+ * @public
26
+ */
27
+ export declare function fakturowniaAdapter(opts: FakturowniaAdapterOptions): InvoicingAdapter;
@@ -0,0 +1,36 @@
1
+ import { FakturowniaClient } from './client.js';
2
+ import { buildFakturowniaInvoice } from './payload.js';
3
+ /**
4
+ * Invoicing adapter backed by Fakturownia (fakturownia.pl). Issues a paid VAT
5
+ * invoice from a fully-paid order and (by default) e-mails the PDF to the buyer
6
+ * provider-side. Seller data and numbering are managed in the Fakturownia
7
+ * account; the NIP is validated upstream at checkout.
8
+ * @public
9
+ */
10
+ export function fakturowniaAdapter(opts) {
11
+ const client = new FakturowniaClient({
12
+ domain: opts.domain,
13
+ apiToken: opts.apiToken,
14
+ fetch: opts.fetch
15
+ });
16
+ const adapter = {
17
+ id: opts.id ?? 'fakturownia',
18
+ issueWhen: opts.issueWhen,
19
+ async createInvoice(payload) {
20
+ const body = buildFakturowniaInvoice(payload, { kind: opts.kind, unit: opts.unit });
21
+ const inv = await client.createInvoice(body);
22
+ return {
23
+ externalId: String(inv.id),
24
+ number: inv.number,
25
+ pdfUrl: inv.view_url,
26
+ raw: inv
27
+ };
28
+ }
29
+ };
30
+ if (opts.sendEmail !== false) {
31
+ adapter.send = async (externalId, _ctx) => {
32
+ await client.sendByEmail(externalId);
33
+ };
34
+ }
35
+ return adapter;
36
+ }
@@ -0,0 +1,35 @@
1
+ import type { InvoicePayload } from '../../types.js';
2
+ export interface FakturowniaPosition {
3
+ name: string;
4
+ quantity: number;
5
+ /** Total gross price of the line (unit × quantity) in the major unit (PLN). */
6
+ total_price_gross: number;
7
+ /** VAT rate as a plain percentage (e.g. 23). */
8
+ tax: number;
9
+ /** Unit of measure — KSeF requires one of: szt, godz, dni, mc, m2, kg. */
10
+ quantity_unit: string;
11
+ }
12
+ export interface FakturowniaInvoiceBody {
13
+ kind: string;
14
+ status: 'paid';
15
+ issue_date: string;
16
+ sell_date: string;
17
+ paid_date: string;
18
+ currency: string;
19
+ buyer_name: string;
20
+ buyer_email: string;
21
+ buyer_tax_no?: string;
22
+ buyer_street?: string;
23
+ buyer_city?: string;
24
+ buyer_post_code?: string;
25
+ buyer_country?: string;
26
+ positions: FakturowniaPosition[];
27
+ }
28
+ /**
29
+ * Map an {@link InvoicePayload} onto the Fakturownia `invoice` object. Pure —
30
+ * no network. The NIP is assumed already validated upstream (checkout).
31
+ */
32
+ export declare function buildFakturowniaInvoice(payload: InvoicePayload, opts?: {
33
+ kind?: string;
34
+ unit?: string;
35
+ }): FakturowniaInvoiceBody;
@@ -0,0 +1,45 @@
1
+ /** Minor units (grosze) → major units (PLN) with 2-decimal precision. */
2
+ function toMajor(minor) {
3
+ return Math.round(minor) / 100;
4
+ }
5
+ function pick(addr, ...keys) {
6
+ if (!addr)
7
+ return undefined;
8
+ for (const k of keys) {
9
+ if (addr[k])
10
+ return addr[k];
11
+ }
12
+ return undefined;
13
+ }
14
+ /**
15
+ * Map an {@link InvoicePayload} onto the Fakturownia `invoice` object. Pure —
16
+ * no network. The NIP is assumed already validated upstream (checkout).
17
+ */
18
+ export function buildFakturowniaInvoice(payload, opts = {}) {
19
+ const date = payload.paidAt.slice(0, 10);
20
+ const { buyer } = payload;
21
+ return {
22
+ kind: opts.kind ?? 'vat',
23
+ status: 'paid',
24
+ issue_date: date,
25
+ sell_date: date,
26
+ paid_date: date,
27
+ currency: payload.currency,
28
+ buyer_name: buyer.companyName || buyer.name,
29
+ buyer_email: buyer.email,
30
+ ...(buyer.nip ? { buyer_tax_no: buyer.nip } : {}),
31
+ ...(pick(buyer.address, 'street') ? { buyer_street: pick(buyer.address, 'street') } : {}),
32
+ ...(pick(buyer.address, 'city') ? { buyer_city: pick(buyer.address, 'city') } : {}),
33
+ ...(pick(buyer.address, 'postCode', 'zip', 'postalCode')
34
+ ? { buyer_post_code: pick(buyer.address, 'postCode', 'zip', 'postalCode') }
35
+ : {}),
36
+ ...(pick(buyer.address, 'country') ? { buyer_country: pick(buyer.address, 'country') } : {}),
37
+ positions: payload.items.map((item) => ({
38
+ name: item.name,
39
+ quantity: item.quantity,
40
+ total_price_gross: toMajor(item.unitPriceGross * item.quantity),
41
+ tax: item.vatRate,
42
+ quantity_unit: opts.unit ?? 'szt'
43
+ }))
44
+ };
45
+ }
@@ -5,6 +5,7 @@ export interface CartItemRef {
5
5
  }
6
6
  export interface CartLine extends CartItemRef {
7
7
  entryId: string;
8
+ productId: string;
8
9
  variantName: Record<string, string> | null;
9
10
  variantSku: string | null;
10
11
  productTitle: string | null;
@@ -39,7 +39,14 @@ export interface CheckoutInput {
39
39
  customerEmail: string;
40
40
  customerName?: string;
41
41
  customerPhone?: string;
42
+ /** Optional tax id (NIP). Validated server-side; invalid → checkout 400. */
43
+ customerNip?: string;
44
+ customerCompanyName?: string;
42
45
  shippingAddress?: Record<string, string>;
46
+ /** Separate billing address for the invoice; falls back to shipping. */
47
+ billingAddress?: Record<string, string>;
48
+ /** B2C opt-in: request an invoice even without a NIP. */
49
+ invoiceRequested?: boolean;
43
50
  shippingMethodId: string;
44
51
  carrierRef?: string;
45
52
  paymentMethod: string;
@@ -114,6 +121,34 @@ export interface RetryPaymentResult {
114
121
  requiresPaymentRedirect: boolean;
115
122
  redirectUrl: string | null;
116
123
  }
124
+ /**
125
+ * Result of `orders.payBalance` — initiates a payment session for the
126
+ * outstanding balance on a deposit order.
127
+ * @public
128
+ */
129
+ export interface PayBalanceResult {
130
+ status: 'redirect' | 'manual' | 'error';
131
+ requiresPaymentRedirect: boolean;
132
+ redirectUrl: string | null;
133
+ }
134
+ /**
135
+ * Public-facing variant row returned by `client.products.listUpcoming`.
136
+ * Mirrors the `shop_product_variants` schema with `priceDelta` decoded as
137
+ * the raw numeric(20,6) string and `attributes` as the typed map declared
138
+ * in `defineShop({ variantAttributes })`.
139
+ * @public
140
+ */
141
+ export interface VariantPublic {
142
+ id: string;
143
+ productId: string;
144
+ sku: string | null;
145
+ name: Record<string, string> | null;
146
+ priceDelta: string;
147
+ stock: number | null;
148
+ attributes: Record<string, unknown> | null;
149
+ sortOrder: number | null;
150
+ createdAt: string;
151
+ }
117
152
  /**
118
153
  * Headless shop SDK surface returned by `createShopClient()`.
119
154
  *
@@ -124,6 +159,21 @@ export interface RetryPaymentResult {
124
159
  * @public
125
160
  */
126
161
  export interface ShopClient {
162
+ /** Product / variant discovery. */
163
+ products: {
164
+ /**
165
+ * List upcoming (non-expired) variants for a product. When
166
+ * `defineShop({ variantExpiry })` is not configured this returns
167
+ * every variant; otherwise variants whose datetime attribute has
168
+ * already passed (per `offsetDays`) are filtered out and the
169
+ * remainder are sorted ascending by that attribute.
170
+ *
171
+ * @param productId - The shop product id (UUID).
172
+ */
173
+ listUpcoming(productId: string): Promise<{
174
+ items: VariantPublic[];
175
+ }>;
176
+ };
127
177
  /** Cart operations — read state and mutate items / coupon. */
128
178
  cart: {
129
179
  /**
@@ -201,6 +251,17 @@ export interface ShopClient {
201
251
  * re-fill the checkout form.
202
252
  */
203
253
  retryPayment(orderNumber: string, token?: string): Promise<RetryPaymentResult>;
254
+ /**
255
+ * Pay the outstanding balance on a deposit-policy order. `token` is
256
+ * the signed balance token from the admin-generated balance link;
257
+ * the server returns a redirect URL when the payment provider needs
258
+ * to take over, or `paymentStatus: 'manual'` for manual methods.
259
+ *
260
+ * @param orderNumber - The order number from the balance link.
261
+ * @param token - Signed balance token (query parameter on the link).
262
+ * @experimental
263
+ */
264
+ payBalance(orderNumber: string, token: string): Promise<PayBalanceResult>;
204
265
  };
205
266
  }
206
267
  /**
@@ -59,6 +59,9 @@ export function createShopClient(options = {}) {
59
59
  return (await res.json());
60
60
  }
61
61
  return {
62
+ products: {
63
+ listUpcoming: (productId) => call('GET', `/api/shop/products/${encodeURIComponent(productId)}/variants/upcoming`)
64
+ },
62
65
  cart: {
63
66
  get: () => call('GET', '/api/shop/cart'),
64
67
  add: (variantId, qty = 1) => call('POST', '/api/shop/cart', { variantId, qty }),
@@ -77,7 +80,8 @@ export function createShopClient(options = {}) {
77
80
  orders: {
78
81
  get: (orderNumber, token) => call('GET', `/api/shop/orders/${encodeURIComponent(orderNumber)}${tokenQuery(token)}`),
79
82
  refreshPayment: (orderNumber, token) => call('POST', `/api/shop/orders/${encodeURIComponent(orderNumber)}/refresh-payment${tokenQuery(token)}`),
80
- retryPayment: (orderNumber, token) => call('POST', `/api/shop/orders/${encodeURIComponent(orderNumber)}/retry-payment${tokenQuery(token)}`)
83
+ retryPayment: (orderNumber, token) => call('POST', `/api/shop/orders/${encodeURIComponent(orderNumber)}/retry-payment${tokenQuery(token)}`),
84
+ payBalance: (orderNumber, token) => call('POST', `/api/shop/orders/${encodeURIComponent(orderNumber)}/balance?token=${encodeURIComponent(token)}`, {})
81
85
  }
82
86
  };
83
87
  }
@@ -0,0 +1,35 @@
1
+ import type { VariantExpiryConfig } from './types.js';
2
+ /**
3
+ * Thrown by `createOrderFromCart` / cart-add HTTP guard when a referenced
4
+ * variant has already expired under the configured `variantExpiry`. The
5
+ * `code` is `VARIANT_EXPIRED` and `variantId` identifies the offending row.
6
+ * @public
7
+ */
8
+ export declare class VariantExpiredError extends Error {
9
+ readonly code = "VARIANT_EXPIRED";
10
+ readonly variantId: string;
11
+ constructor(variantId: string);
12
+ }
13
+ interface VariantLike {
14
+ attributes: Record<string, unknown> | null;
15
+ }
16
+ /**
17
+ * Check whether a variant has expired under the given config. Fail-open by
18
+ * design: `null` config, missing source attribute, non-string value, or
19
+ * malformed datetime all return `false` — only a parseable past datetime
20
+ * counts as expired. Malformed datetimes emit `console.warn` so authoring
21
+ * issues surface in dev without blocking checkout.
22
+ *
23
+ * @param variant - Object with an `attributes` map (e.g. `shop_product_variants.attributes`).
24
+ * @param config - Result of `defineShop({ variantExpiry })`, or `null` when not configured.
25
+ * @param now - Override the current time (test seam). Defaults to `Date.now()`.
26
+ * @public
27
+ */
28
+ export declare function isVariantExpired(variant: VariantLike, config: VariantExpiryConfig | null, now?: Date): boolean;
29
+ /**
30
+ * Return the subset of `variants` that have not yet expired. Order is
31
+ * preserved. `null` config = passthrough.
32
+ * @public
33
+ */
34
+ export declare function filterUpcoming<T extends VariantLike>(variants: T[], config: VariantExpiryConfig | null, now?: Date): T[];
35
+ export {};
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Thrown by `createOrderFromCart` / cart-add HTTP guard when a referenced
3
+ * variant has already expired under the configured `variantExpiry`. The
4
+ * `code` is `VARIANT_EXPIRED` and `variantId` identifies the offending row.
5
+ * @public
6
+ */
7
+ export class VariantExpiredError extends Error {
8
+ code = 'VARIANT_EXPIRED';
9
+ variantId;
10
+ constructor(variantId) {
11
+ super(`Variant ${variantId} has expired`);
12
+ this.name = 'VariantExpiredError';
13
+ this.variantId = variantId;
14
+ }
15
+ }
16
+ /**
17
+ * Add `offsetDays` to an ISO-8601 datetime and return the resulting `Date`.
18
+ * Returns `null` on malformed input (caller decides fail-open vs throw).
19
+ * @internal
20
+ */
21
+ function addDaysIso(iso, offsetDays) {
22
+ const ts = Date.parse(iso);
23
+ if (Number.isNaN(ts))
24
+ return null;
25
+ return new Date(ts + offsetDays * 86_400_000);
26
+ }
27
+ /**
28
+ * Check whether a variant has expired under the given config. Fail-open by
29
+ * design: `null` config, missing source attribute, non-string value, or
30
+ * malformed datetime all return `false` — only a parseable past datetime
31
+ * counts as expired. Malformed datetimes emit `console.warn` so authoring
32
+ * issues surface in dev without blocking checkout.
33
+ *
34
+ * @param variant - Object with an `attributes` map (e.g. `shop_product_variants.attributes`).
35
+ * @param config - Result of `defineShop({ variantExpiry })`, or `null` when not configured.
36
+ * @param now - Override the current time (test seam). Defaults to `Date.now()`.
37
+ * @public
38
+ */
39
+ export function isVariantExpired(variant, config, now = new Date(Date.now())) {
40
+ if (!config)
41
+ return false;
42
+ const attrs = variant.attributes;
43
+ if (!attrs)
44
+ return false;
45
+ const raw = attrs[config.source];
46
+ if (raw === undefined || raw === null)
47
+ return false;
48
+ if (typeof raw !== 'string') {
49
+ console.warn(`[variantExpiry] attribute "${config.source}" is not a string (got ${typeof raw}); treating variant as upcoming`);
50
+ return false;
51
+ }
52
+ const cutoff = addDaysIso(raw, config.offsetDays);
53
+ if (!cutoff) {
54
+ console.warn(`[variantExpiry] attribute "${config.source}" is not a parseable datetime ("${raw}"); treating variant as upcoming`);
55
+ return false;
56
+ }
57
+ return cutoff.getTime() < now.getTime();
58
+ }
59
+ /**
60
+ * Return the subset of `variants` that have not yet expired. Order is
61
+ * preserved. `null` config = passthrough.
62
+ * @public
63
+ */
64
+ export function filterUpcoming(variants, config, now) {
65
+ if (!config)
66
+ return variants;
67
+ return variants.filter((v) => !isVariantExpired(v, config, now));
68
+ }
@@ -0,0 +1,20 @@
1
+ import { type RequestHandler } from '@sveltejs/kit';
2
+ /**
3
+ * @experimental
4
+ * HTTP handlers for the balance-payment flow. Mount at
5
+ * `/api/shop/orders/[number]/balance` (or any URL containing `number` +
6
+ * `?token=...`).
7
+ *
8
+ * - `GET` returns the minimal public order view (amount due, currency)
9
+ * when the token + balanceOwed check passes.
10
+ * - `POST` initiates a payment session for the outstanding balance using
11
+ * the order's original payment adapter and returns `{ redirectUrl,
12
+ * status }`.
13
+ *
14
+ * Both verbs return 403 when the token is invalid OR `balanceOwed` is
15
+ * false (no oracle distinction — we don't tell the caller which gate failed).
16
+ */
17
+ export declare function createBalanceHandler(): {
18
+ GET: RequestHandler;
19
+ POST: RequestHandler;
20
+ };