platform-types 1.10.834 → 1.10.836

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/auth.d.ts CHANGED
@@ -186,6 +186,46 @@ declare global {
186
186
  */
187
187
  storeContact?: TenantRegisterStoreContactInput;
188
188
  }
189
+ /**
190
+ * `POST /auth { mode: 'register-social' }` on the OPERATOR host — register a
191
+ * store whose founder signs in with Google or Apple and has NO password. Field
192
+ * for field what `registerSocialSchema` (`stacks/lambdas/auth/_registerSocial.ts`)
193
+ * accepts as INPUT, beside `mode`; the tie is compile-time,
194
+ * `AssertTenantSocialRegisterInputMatchesSchema`.
195
+ *
196
+ * The business fields are {@link TenantRegisterInput}'s, same bounds, minus
197
+ * `email` and `password`: the founder's email is ALWAYS the verified token's,
198
+ * and an `email` or `password` in the body is stripped, never read. The 200 is
199
+ * the body `mode: 'register'` answers, with `emailVerified: true`, and no
200
+ * verification code is mailed.
201
+ *
202
+ * ⚠️ Apple Hide My Email: a relay login is allowed, but then
203
+ * `storeContact.email` is REQUIRED and must itself not be a relay — see
204
+ * {@link TENANT_SOCIAL_REGISTER_ERROR_CODES}.
205
+ */
206
+ type TenantSocialRegisterInput = Omit<TenantRegisterInput, 'email' | 'password'> & {
207
+ /** A Firebase ID token from a Google or Apple sign-in, exactly as {@link Social.accessToken}. */
208
+ accessToken: string;
209
+ };
210
+ /**
211
+ * `POST /auth/verify-password` — re-authentication before a sensitive
212
+ * operation. EXACTLY ONE proof:
213
+ * - `password` — every account that has one;
214
+ * - `idToken` — a FRESH Firebase ID token (signed in within the last 5
215
+ * minutes) from a provider the account was registered with, whose verified
216
+ * email is the account's. Accepted ONLY on an account with no password;
217
+ * see {@link STAFF_REAUTH_ERROR_CODES}.
218
+ * `totpCode` is the second factor, when the account has one, on either proof.
219
+ */
220
+ type StaffReauthInput = {
221
+ password: string;
222
+ idToken?: never;
223
+ totpCode?: string;
224
+ } | {
225
+ idToken: string;
226
+ password?: never;
227
+ totpCode?: string;
228
+ };
189
229
  /** {@link TenantRegisterInput.storeContact} — at least one key. */
190
230
  interface TenantRegisterStoreContactInput {
191
231
  /** EXACTLY 10 characters. */
@@ -1615,6 +1655,41 @@ export type SocialBridgeErrorCode = (typeof SOCIAL_BRIDGE_ERROR_CODES)[number];
1615
1655
  */
1616
1656
  export declare const CUSTOMER_SOCIAL_REGISTER_ERROR_CODES: readonly ["EMAIL_TAKEN_BY_ANOTHER_ACCOUNT", "CUSTOMER_ID_ALLOCATION_EXHAUSTED", "SIGN_IN_IDENTITY_UNVERIFIED", "INVALID_ID_TOKEN", "SIGN_IN_PROVIDER_UNSUPPORTED"];
1617
1657
  export type CustomerSocialRegisterErrorCode = (typeof CUSTOMER_SOCIAL_REGISTER_ERROR_CODES)[number];
1658
+ /**
1659
+ * `body.error` codes `POST /auth { mode: 'register-social' }` on the OPERATOR
1660
+ * host can refuse with, beside the ordinary `400` validation refusal:
1661
+ *
1662
+ * - `INVALID_ID_TOKEN` · 401 — the token is expired or malformed; get a fresh one.
1663
+ * - `SIGN_IN_IDENTITY_UNVERIFIED` · 401 — the provider has not verified the
1664
+ * email, or the token carries none.
1665
+ * - `SIGN_IN_PROVIDER_UNSUPPORTED` · 400 — a provider other than Google or Apple.
1666
+ * - `STORE_CONTACT_EMAIL_REQUIRED` · 400 — the login is an Apple relay address
1667
+ * and the body carries no `storeContact.email`, or a relay one.
1668
+ * - `EMAIL_IN_USE` · 400 and `CUIT_SHARED` · 409 — exactly as `mode: 'register'`.
1669
+ */
1670
+ export declare const TENANT_SOCIAL_REGISTER_ERROR_CODES: readonly ["INVALID_ID_TOKEN", "SIGN_IN_IDENTITY_UNVERIFIED", "SIGN_IN_PROVIDER_UNSUPPORTED", "STORE_CONTACT_EMAIL_REQUIRED", "EMAIL_IN_USE", "CUIT_SHARED"];
1671
+ export type TenantSocialRegisterErrorCode = (typeof TENANT_SOCIAL_REGISTER_ERROR_CODES)[number];
1672
+ /**
1673
+ * `body.error` codes `POST /auth/verify-password` answers for the PROOF itself
1674
+ * (the `password` / `idToken` choice), beside the identity, lockout and
1675
+ * second-factor codes it shared before:
1676
+ *
1677
+ * - `WRONG_CREDENTIALS` · 401 — the password did not match.
1678
+ * - `PASSWORD_NOT_SET` · 409 — a `password` was sent for an account that has
1679
+ * none (a founder registered with Google or Apple). The body carries
1680
+ * `providers`, the providers to re-authenticate with through `idToken`; or set
1681
+ * a password through recovery. Nothing is counted against the account.
1682
+ * - `PASSWORD_REQUIRED` · 400 — an `idToken` was sent for an account that HAS a
1683
+ * password; it must re-authenticate with the password.
1684
+ * - `INVALID_ID_TOKEN` · 401 / `SIGN_IN_IDENTITY_UNVERIFIED` · 401 — as on
1685
+ * `register-social`.
1686
+ * - `REAUTH_IDENTITY_MISMATCH` · 401 — the token's email is not the account's,
1687
+ * or its provider is not one the account was registered with.
1688
+ * - `REAUTH_NOT_RECENT` · 401 — the provider sign-in behind the token is older
1689
+ * than 5 minutes; sign in with the provider again.
1690
+ */
1691
+ export declare const STAFF_REAUTH_ERROR_CODES: readonly ["WRONG_CREDENTIALS", "PASSWORD_NOT_SET", "PASSWORD_REQUIRED", "INVALID_ID_TOKEN", "SIGN_IN_IDENTITY_UNVERIFIED", "REAUTH_IDENTITY_MISMATCH", "REAUTH_NOT_RECENT"];
1692
+ export type StaffReauthErrorCode = (typeof STAFF_REAUTH_ERROR_CODES)[number];
1618
1693
  /**
1619
1694
  * `body.error` codes the operator refresh/session leg can refuse with —
1620
1695
  * `POST /auth { mode: 'refresh' }` and the body-transport ingress it shares with
package/dist/auth.js CHANGED
@@ -163,6 +163,54 @@ export const CUSTOMER_SOCIAL_REGISTER_ERROR_CODES = [
163
163
  'INVALID_ID_TOKEN',
164
164
  'SIGN_IN_PROVIDER_UNSUPPORTED',
165
165
  ];
166
+ /**
167
+ * `body.error` codes `POST /auth { mode: 'register-social' }` on the OPERATOR
168
+ * host can refuse with, beside the ordinary `400` validation refusal:
169
+ *
170
+ * - `INVALID_ID_TOKEN` · 401 — the token is expired or malformed; get a fresh one.
171
+ * - `SIGN_IN_IDENTITY_UNVERIFIED` · 401 — the provider has not verified the
172
+ * email, or the token carries none.
173
+ * - `SIGN_IN_PROVIDER_UNSUPPORTED` · 400 — a provider other than Google or Apple.
174
+ * - `STORE_CONTACT_EMAIL_REQUIRED` · 400 — the login is an Apple relay address
175
+ * and the body carries no `storeContact.email`, or a relay one.
176
+ * - `EMAIL_IN_USE` · 400 and `CUIT_SHARED` · 409 — exactly as `mode: 'register'`.
177
+ */
178
+ export const TENANT_SOCIAL_REGISTER_ERROR_CODES = [
179
+ 'INVALID_ID_TOKEN',
180
+ 'SIGN_IN_IDENTITY_UNVERIFIED',
181
+ 'SIGN_IN_PROVIDER_UNSUPPORTED',
182
+ 'STORE_CONTACT_EMAIL_REQUIRED',
183
+ 'EMAIL_IN_USE',
184
+ 'CUIT_SHARED',
185
+ ];
186
+ /**
187
+ * `body.error` codes `POST /auth/verify-password` answers for the PROOF itself
188
+ * (the `password` / `idToken` choice), beside the identity, lockout and
189
+ * second-factor codes it shared before:
190
+ *
191
+ * - `WRONG_CREDENTIALS` · 401 — the password did not match.
192
+ * - `PASSWORD_NOT_SET` · 409 — a `password` was sent for an account that has
193
+ * none (a founder registered with Google or Apple). The body carries
194
+ * `providers`, the providers to re-authenticate with through `idToken`; or set
195
+ * a password through recovery. Nothing is counted against the account.
196
+ * - `PASSWORD_REQUIRED` · 400 — an `idToken` was sent for an account that HAS a
197
+ * password; it must re-authenticate with the password.
198
+ * - `INVALID_ID_TOKEN` · 401 / `SIGN_IN_IDENTITY_UNVERIFIED` · 401 — as on
199
+ * `register-social`.
200
+ * - `REAUTH_IDENTITY_MISMATCH` · 401 — the token's email is not the account's,
201
+ * or its provider is not one the account was registered with.
202
+ * - `REAUTH_NOT_RECENT` · 401 — the provider sign-in behind the token is older
203
+ * than 5 minutes; sign in with the provider again.
204
+ */
205
+ export const STAFF_REAUTH_ERROR_CODES = [
206
+ 'WRONG_CREDENTIALS',
207
+ 'PASSWORD_NOT_SET',
208
+ 'PASSWORD_REQUIRED',
209
+ 'INVALID_ID_TOKEN',
210
+ 'SIGN_IN_IDENTITY_UNVERIFIED',
211
+ 'REAUTH_IDENTITY_MISMATCH',
212
+ 'REAUTH_NOT_RECENT',
213
+ ];
166
214
  /**
167
215
  * `body.error` codes the operator refresh/session leg can refuse with —
168
216
  * `POST /auth { mode: 'refresh' }` and the body-transport ingress it shares with
package/dist/cjs/auth.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CUSTOMER_OTP_LIMITS = exports.CUSTOMER_OTP_ERROR_CODES = exports.STAFF_ACCOUNT_DELETION_ERROR_CODES = exports.SESSION_ACTION_ERROR_CODES = exports.PASSKEY_ERROR_CODES = exports.TWO_FACTOR_MANAGEMENT_ERROR_CODES = exports.LOGIN_2FA_ERROR_CODES = exports.LOGIN_ERROR_CODES = exports.STEP_UP_ERROR_CODES = exports.REFRESH_ERROR_CODES = exports.CUSTOMER_SOCIAL_REGISTER_ERROR_CODES = exports.SOCIAL_BRIDGE_ERROR_CODES = exports.SIGN_IN_METHOD_ERROR_CODES = void 0;
3
+ exports.CUSTOMER_OTP_LIMITS = exports.CUSTOMER_OTP_ERROR_CODES = exports.STAFF_ACCOUNT_DELETION_ERROR_CODES = exports.SESSION_ACTION_ERROR_CODES = exports.PASSKEY_ERROR_CODES = exports.TWO_FACTOR_MANAGEMENT_ERROR_CODES = exports.LOGIN_2FA_ERROR_CODES = exports.LOGIN_ERROR_CODES = exports.STEP_UP_ERROR_CODES = exports.REFRESH_ERROR_CODES = exports.STAFF_REAUTH_ERROR_CODES = exports.TENANT_SOCIAL_REGISTER_ERROR_CODES = exports.CUSTOMER_SOCIAL_REGISTER_ERROR_CODES = exports.SOCIAL_BRIDGE_ERROR_CODES = exports.SIGN_IN_METHOD_ERROR_CODES = void 0;
4
4
  /**
5
5
  * `body.error` codes the sign-in-method paths can refuse with.
6
6
  *
@@ -166,6 +166,54 @@ exports.CUSTOMER_SOCIAL_REGISTER_ERROR_CODES = [
166
166
  'INVALID_ID_TOKEN',
167
167
  'SIGN_IN_PROVIDER_UNSUPPORTED',
168
168
  ];
169
+ /**
170
+ * `body.error` codes `POST /auth { mode: 'register-social' }` on the OPERATOR
171
+ * host can refuse with, beside the ordinary `400` validation refusal:
172
+ *
173
+ * - `INVALID_ID_TOKEN` · 401 — the token is expired or malformed; get a fresh one.
174
+ * - `SIGN_IN_IDENTITY_UNVERIFIED` · 401 — the provider has not verified the
175
+ * email, or the token carries none.
176
+ * - `SIGN_IN_PROVIDER_UNSUPPORTED` · 400 — a provider other than Google or Apple.
177
+ * - `STORE_CONTACT_EMAIL_REQUIRED` · 400 — the login is an Apple relay address
178
+ * and the body carries no `storeContact.email`, or a relay one.
179
+ * - `EMAIL_IN_USE` · 400 and `CUIT_SHARED` · 409 — exactly as `mode: 'register'`.
180
+ */
181
+ exports.TENANT_SOCIAL_REGISTER_ERROR_CODES = [
182
+ 'INVALID_ID_TOKEN',
183
+ 'SIGN_IN_IDENTITY_UNVERIFIED',
184
+ 'SIGN_IN_PROVIDER_UNSUPPORTED',
185
+ 'STORE_CONTACT_EMAIL_REQUIRED',
186
+ 'EMAIL_IN_USE',
187
+ 'CUIT_SHARED',
188
+ ];
189
+ /**
190
+ * `body.error` codes `POST /auth/verify-password` answers for the PROOF itself
191
+ * (the `password` / `idToken` choice), beside the identity, lockout and
192
+ * second-factor codes it shared before:
193
+ *
194
+ * - `WRONG_CREDENTIALS` · 401 — the password did not match.
195
+ * - `PASSWORD_NOT_SET` · 409 — a `password` was sent for an account that has
196
+ * none (a founder registered with Google or Apple). The body carries
197
+ * `providers`, the providers to re-authenticate with through `idToken`; or set
198
+ * a password through recovery. Nothing is counted against the account.
199
+ * - `PASSWORD_REQUIRED` · 400 — an `idToken` was sent for an account that HAS a
200
+ * password; it must re-authenticate with the password.
201
+ * - `INVALID_ID_TOKEN` · 401 / `SIGN_IN_IDENTITY_UNVERIFIED` · 401 — as on
202
+ * `register-social`.
203
+ * - `REAUTH_IDENTITY_MISMATCH` · 401 — the token's email is not the account's,
204
+ * or its provider is not one the account was registered with.
205
+ * - `REAUTH_NOT_RECENT` · 401 — the provider sign-in behind the token is older
206
+ * than 5 minutes; sign in with the provider again.
207
+ */
208
+ exports.STAFF_REAUTH_ERROR_CODES = [
209
+ 'WRONG_CREDENTIALS',
210
+ 'PASSWORD_NOT_SET',
211
+ 'PASSWORD_REQUIRED',
212
+ 'INVALID_ID_TOKEN',
213
+ 'SIGN_IN_IDENTITY_UNVERIFIED',
214
+ 'REAUTH_IDENTITY_MISMATCH',
215
+ 'REAUTH_NOT_RECENT',
216
+ ];
169
217
  /**
170
218
  * `body.error` codes the operator refresh/session leg can refuse with —
171
219
  * `POST /auth { mode: 'refresh' }` and the body-transport ingress it shares with
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MARKETING_COPY_VARIANTS_MAX = exports.MARKETING_COPY_VARIANTS_MIN = exports.MARKETING_COPY_FACT_MAX_CHARS = exports.MARKETING_COPY_FACTS_MAX = exports.MARKETING_COPY_FACTS_MIN = exports.MARKETING_COPY_GOAL_MAX_CHARS = exports.ABANDONED_CART_SNAPSHOT_NAME_MAX = exports.ABANDONED_CART_SNAPSHOT_MAX_LINES = exports.CART_APPLICABLE_PROMOTION_TYPES = exports.PROMOTION_TYPES = exports.AUTOMATION_RULE_STATUSES = exports.AUTOMATION_SEND_STATUSES = exports.ATTRIBUTION_WINDOW_HOURS = exports.RFM_SEGMENT_LABELS = exports.AUTOMATION_TRIGGERS = void 0;
3
+ exports.MARKETING_COPY_VARIANTS_MAX = exports.MARKETING_COPY_VARIANTS_MIN = exports.MARKETING_COPY_FACT_MAX_CHARS = exports.MARKETING_COPY_FACTS_MAX = exports.MARKETING_COPY_FACTS_MIN = exports.MARKETING_COPY_GOAL_MAX_CHARS = exports.ABANDONED_CART_SNAPSHOT_NAME_MAX = exports.ABANDONED_CART_SNAPSHOT_MAX_LINES = exports.CART_APPLICABLE_PROMOTION_TYPES = exports.PROMOTION_TYPES = exports.AUTOMATION_RULE_STATUSES = exports.AUTOMATION_SEND_STATUSES = exports.ATTRIBUTION_WINDOW_HOURS = exports.RFM_SEGMENT_LABELS = exports.CAMPAIGN_ERROR_CODES = exports.CAMPAIGN_CLIENT_STATUSES = exports.AUTOMATION_TRIGGERS = void 0;
4
4
  /**
5
5
  * Runtime source for {@link AutomationTrigger}, the api’s Zod enum and the
6
6
  * configuration picker. Derive them from this tuple; a separate union plus
@@ -11,6 +11,32 @@ exports.MARKETING_COPY_VARIANTS_MAX = exports.MARKETING_COPY_VARIANTS_MIN = expo
11
11
  * consumers such as Record<AutomationTrigger, …>; handle it in the same change.
12
12
  */
13
13
  exports.AUTOMATION_TRIGGERS = ['ABANDONED_CART', 'REORDER_DUE'];
14
+ /**
15
+ * The {@link CampaignStatus} values a client may write on `POST /campaigns`.
16
+ * Any other status is a 409 `CAMPAIGN_ILLEGAL_TRANSITION`; a client `sentAt` is
17
+ * a 400 `VALIDATION_FAILED`.
18
+ */
19
+ exports.CAMPAIGN_CLIENT_STATUSES = [
20
+ 'draft',
21
+ 'scheduled',
22
+ 'cancelled',
23
+ ];
24
+ /**
25
+ * Refusals of the campaign status machine on `POST /campaigns`.
26
+ *
27
+ * - `CAMPAIGN_NOT_EDITABLE` · 409 — the campaign is `'sending'`, `'sent'` or
28
+ * `'cancelled'`, and the request changes no status.
29
+ * - `CAMPAIGN_ILLEGAL_TRANSITION` · 409 — the request (create or update) asks
30
+ * for a server-owned status (`'sending'` / `'sent'`), or asks a `'sending'`,
31
+ * `'sent'` or `'cancelled'` campaign for a different status.
32
+ * - `CAMPAIGN_STATUS_CONFLICT` · 409 — the status changed between the read and
33
+ * the write (usually the scheduler claiming it); re-read and retry.
34
+ */
35
+ exports.CAMPAIGN_ERROR_CODES = [
36
+ 'CAMPAIGN_NOT_EDITABLE',
37
+ 'CAMPAIGN_ILLEGAL_TRANSITION',
38
+ 'CAMPAIGN_STATUS_CONFLICT',
39
+ ];
14
40
  /**
15
41
  * {@link RfmSegmentLabel} as a runtime value, so `isSegmentRuleValid`
16
42
  * (`stacks/services/segments.ts`) and the nightly scoring pass
@@ -100,9 +100,10 @@ exports.PURCHASE_WITHDRAWAL_ACTION_MODES = ['accept', 'decline', 'relay'];
100
100
  * on) on an order that already has an open shopper return request
101
101
  * (`requested`, `authorized` or `received`); `data.rmaId` names it. One open
102
102
  * shopper request per order.
103
- * - `too-many-lines` — a shopper return request (either flow) naming, or
104
- * resolving to, more lines than one return request may hold; `data.max` is
105
- * the bound.
103
+ * - `too-many-lines` — a withdrawal naming more lines than
104
+ * {@link PURCHASE_WITHDRAWAL_LINES_MAX}, or a shopper return request (either
105
+ * flow) naming, or resolving to, more lines than one return request may hold;
106
+ * `data.max` is the bound.
106
107
  */
107
108
  exports.PURCHASE_WITHDRAWAL_INELIGIBLE_REASONS = [
108
109
  'order-cancelled',
@@ -131,8 +132,14 @@ exports.PURCHASE_WITHDRAWAL_INELIGIBLE_REASONS = [
131
132
  exports.PURCHASE_WITHDRAWAL_FISCAL_IN_FLIGHT_CAUSES = ['in-progress', 'uncertain', 'stranded'];
132
133
  /** The longest decline or relay `note`, in characters after trimming. */
133
134
  exports.PURCHASE_WITHDRAWAL_NOTE_MAX = 500;
134
- /** The most lines one request may name. */
135
- exports.PURCHASE_WITHDRAWAL_LINES_MAX = 100;
135
+ /**
136
+ * The most lines one request may name. It is held to the worst case a single
137
+ * bound `Return` can carry: a withdrawal's hand-off RMA approves every line,
138
+ * and its `Return` must name exactly those lines, while the Return's
139
+ * transaction item budget fits 48 lines when every line restocks into a
140
+ * depósito as well as the store total. A larger withdrawal could never settle.
141
+ */
142
+ exports.PURCHASE_WITHDRAWAL_LINES_MAX = 48;
136
143
  /** Calendar days of the withdrawal window after the later of delivery and purchase. */
137
144
  exports.PURCHASE_WITHDRAWAL_WINDOW_DAYS = 10;
138
145
  /** The matrix row {@link PurchaseWithdrawal.windowClosesAt} is computed from. */
package/dist/cjs/store.js CHANGED
@@ -112,7 +112,7 @@ exports.STORE_MODULE_ERROR_CODES = [
112
112
  * Role refusals, `403` — a plain USER writing a privileged section. Each is
113
113
  * judged before any other check on that section:
114
114
  * - `STORE_CONFIG_FORBIDDEN` — any privileged `config` leaf (`modules`,
115
- * `stock`, `priceDecimals`, `changePrice`, `mandatory2fa`, …).
115
+ * `stock`, `priceDecimals`, `changePrice`, `mandatory2fa`, `autoPrint`, …).
116
116
  * - `STORE_ECOMMERCE_FORBIDDEN` — any `ecommerce` block.
117
117
  * - `STORE_FISCAL_FIELD_FORBIDDEN` — a fiscal field of the store.
118
118
  * - `STORE_JURISDICTION_FORBIDDEN` — `jurisdiction`.
@@ -90,6 +90,10 @@ exports.TERMINAL_ENROLMENT_ERROR_CODES = [
90
90
  * - `INVALID_PIN_FORMAT` · 400 — `POST /terminal/pin`: not 4–6 digits.
91
91
  * - `WRONG_CREDENTIALS` · 400 — `POST /terminal/pin`: the re-verified password
92
92
  * did not match.
93
+ * - `PASSWORD_NOT_SET` · 409 — `POST /terminal/activation` and `POST
94
+ * /terminal/pin`: the employee (a founder registered with Google or Apple) has
95
+ * no password. The body carries `providers`, the recorded sign-in providers, as
96
+ * `POST /auth/verify-password` does. Nothing is counted against the account.
93
97
  * - `IDEMPOTENCY_KEY_REUSE` · 422 / `IDEMPOTENCY_IN_PROGRESS` · 409 /
94
98
  * `INVALID_IDEMPOTENCY_KEY` · 400 — the `Idempotency-Key` header's own
95
99
  * refusals on `POST /terminal/operators`. A MISSING header is
@@ -126,6 +130,7 @@ exports.TERMINAL_SESSION_ERROR_CODES = [
126
130
  'ROLE_NOT_ALLOWED',
127
131
  'INVALID_PIN_FORMAT',
128
132
  'WRONG_CREDENTIALS',
133
+ 'PASSWORD_NOT_SET',
129
134
  'IDEMPOTENCY_KEY_REUSE',
130
135
  'IDEMPOTENCY_IN_PROGRESS',
131
136
  'INVALID_IDEMPOTENCY_KEY',
@@ -56,8 +56,13 @@ declare global {
56
56
  interface ChannelConsentStamp {
57
57
  /** ms epoch of the grant or withdrawal this stamp describes. */
58
58
  ts: number;
59
- source: 'ui' | 'import' | 'api' | 'storefront';
60
- /** Absent when the write had no request-level IP (a CSV import, a job). */
59
+ /**
60
+ * Who set the value. `'complaint'` is the SES events worker withdrawing
61
+ * `email` after the recipient marked a message as spam; it is only ever a
62
+ * withdrawal.
63
+ */
64
+ source: 'ui' | 'import' | 'api' | 'storefront' | 'complaint';
65
+ /** Absent when the write had no request-level IP (a CSV import, a job, a complaint). */
61
66
  ip?: string;
62
67
  }
63
68
  interface CustomerMarketing {
@@ -9,6 +9,21 @@ declare global {
9
9
  * cheap, and having shipped a promise is not.
10
10
  */
11
11
  type CampaignChannel = 'email';
12
+ /**
13
+ * Where a campaign is in its life. The api enforces the transitions:
14
+ *
15
+ * - `'draft'` → `'scheduled'` | `'cancelled'`
16
+ * - `'scheduled'` → `'draft'` | `'cancelled'`, or `'sending'` by the scheduler's claim
17
+ * - `'sending'` → `'sent'` once every recipient settled, or back to `'scheduled'`
18
+ * only on the scheduler's own fan-out retry
19
+ * - `'sent'` and `'cancelled'` are terminal.
20
+ *
21
+ * ⚠️ A client may write only {@link CAMPAIGN_CLIENT_STATUSES}. `'sending'`,
22
+ * `'sent'` and `sentAt` are server-owned, and a campaign that is `'sending'`,
23
+ * `'sent'` or `'cancelled'` cannot be edited at all — the refusals are
24
+ * {@link CAMPAIGN_ERROR_CODES}. A sent campaign is never sent again; to send
25
+ * the same content twice, create a new campaign.
26
+ */
12
27
  type CampaignStatus = 'draft' | 'scheduled' | 'sending' | 'sent' | 'cancelled';
13
28
  interface Campaign {
14
29
  storeId: string;
@@ -18,8 +33,14 @@ declare global {
18
33
  status: CampaignStatus;
19
34
  templateId?: string;
20
35
  segmentId?: string;
36
+ /**
37
+ * ⚠️ UNUSED. Stored as the client sent it and returned on reads, but no
38
+ * code reads it: the send does not apply, mention or link a promotion.
39
+ * Do not build on it as if a campaign carried a discount.
40
+ */
21
41
  promotionId?: string;
22
42
  scheduledAt?: number;
43
+ /** Unix MILLISECONDS. Server-owned: written only when the campaign reaches `'sent'`. */
23
44
  sentAt?: number;
24
45
  /**
25
46
  * Aggregate send counters, maintained by an atomic DynamoDB `ADD` as the
@@ -65,6 +86,16 @@ declare global {
65
86
  * bound the scheduler stops reverting and asks for an operator.
66
87
  */
67
88
  sendAttempts?: number;
89
+ /**
90
+ * Server-owned. Present while recipients HELD by the email kill switch
91
+ * (`'failed'` / `EMAIL_SENDING_DISABLED`) await a re-enqueue: the send
92
+ * worker `ADD`s 1 on every hold, and the scheduler removes it once it has
93
+ * re-enqueued every held recipient after the switch is back on.
94
+ *
95
+ * ⚠️ A MARKER, not a recipient count: a recipient held twice counts twice.
96
+ * Read it as "held recipients are still owed a delivery", nothing more.
97
+ */
98
+ sendHolds?: number;
68
99
  createdAt: number;
69
100
  updatedAt?: number;
70
101
  }
@@ -73,12 +104,14 @@ declare global {
73
104
  *
74
105
  * `'queued'` — accepted for delivery, not yet handed to the transport.
75
106
  * `'sent'` — the transport ACCEPTED it; acceptance is never proof of inbox
76
- * placement. `'failed'` — the transport refused or errored. `'skipped'` — the
77
- * segment matched the recipient but the send was withheld at send time: the
78
- * recipient was not eligible (no marketing consent for the channel, no
79
- * address, a suppressed address, a customer that no longer exists), the
80
- * campaign was cancelled, or the store had its Marketing module switched off
81
- * (`reason: 'MODULE_OFF'`). `CampaignRecipient.reason` carries which.
107
+ * placement. `'failed'` — the transport refused or errored, or the platform
108
+ * had email sending switched off (`reason: 'EMAIL_SENDING_DISABLED'`, held for
109
+ * a re-enqueue once sending is back on, and counted nowhere until it settles). `'skipped'` — the segment
110
+ * matched the recipient but the send was withheld at send time: the recipient
111
+ * was not eligible (no marketing consent for the channel, no address, a
112
+ * suppressed address, a customer that no longer exists or that the store
113
+ * disabled), the campaign was cancelled, or the store had its Marketing module
114
+ * switched off (`reason: 'MODULE_OFF'`). `CampaignRecipient.reason` carries which.
82
115
  *
83
116
  * ⚠️ `'skipped'` is a REFUSAL that was honoured, not a delivery problem:
84
117
  * never retry it, and never roll it into a failure rate. A `'MODULE_OFF'`
@@ -477,12 +510,15 @@ declare global {
477
510
  * a per-customer frequency cap. The configuration record for automated
478
511
  * marketing — abandoned-cart recovery first.
479
512
  *
480
- * ⚠️ **Nothing consumes these rows yet.** This is the entity, its CRUD and
481
- * its published contract, shipped INERT on purpose so the configuration
482
- * screen has something to write to. The consumer that receives the signal,
483
- * evaluates the rule and enqueues a send is a separate slice, and so is the
484
- * storage the cap below is counted against. A rule created today is a stored
485
- * intention, not an armed sender.
513
+ * ⚠️ **An `'active'` rule is an ARMED SENDER.** `cartSweep` (`ABANDONED_CART`)
514
+ * and `reorderDueCron` (`REORDER_DUE`) arm an {@link AutomationSend} for
515
+ * every active rule matching the trigger; the `automation-due` cron re-reads
516
+ * the rule, its template, the cap and consent when the delay ends, and the
517
+ * `automation-send` worker mails it. An archived template sends nothing.
518
+ * Automations are a `marketing` (Avanzado) feature: `/automation-rules` and
519
+ * `/templates` answer 403 `PLAN_UPGRADE_REQUIRED` without it, and both
520
+ * unattended lanes settle a store's due sends `'skipped'`
521
+ * `AUTOMATION_NOT_ENTITLED`.
486
522
  *
487
523
  * ⚠️ **No `channel`.** The bound `Template` already carries one, and a
488
524
  * second copy is a second answer — the same argument
@@ -554,8 +590,12 @@ declare global {
554
590
  * `'queued'` — accepted, not yet handed to a transport. `'sent'` — handed
555
591
  * over. `'failed'` — the transport refused or errored. `'skipped'` — the rule
556
592
  * matched but the send was withheld, by the frequency cap, by a live
557
- * consent re-check, or because the store had its Marketing module switched
558
- * off (`reason: 'MODULE_OFF'`, a store choice rather than a customer's).
593
+ * consent re-check, because the store had its Marketing module switched
594
+ * off (`reason: 'MODULE_OFF'`, a store choice rather than a customer's),
595
+ * because its plan does not carry `marketing`
596
+ * (`reason: 'AUTOMATION_NOT_ENTITLED'`), or because the rule's template was
597
+ * archived (`reason: 'AUTOMATION_TEMPLATE_ARCHIVED'`). A `'skipped'` row
598
+ * does not count against the frequency cap.
559
599
  *
560
600
  * Deliberately the same four values as {@link CampaignRecipientStatus}, and
561
601
  * deliberately a SEPARATE type rather than an alias: the two vocabularies
@@ -870,8 +910,18 @@ declare global {
870
910
  due: number;
871
911
  /** Sends handed to the transport queue. */
872
912
  enqueued: number;
873
- /** Due rows withheld, each with its own recorded reason. */
913
+ /**
914
+ * Due rows withheld, each with its own recorded reason — including a
915
+ * store whose plan lacks `marketing` (`AUTOMATION_NOT_ENTITLED`).
916
+ */
874
917
  skipped: number;
918
+ /**
919
+ * Due rows whose evaluation or settle threw. Left queued, so the next run
920
+ * picks them up again; one deduped ERROR row per rule names them.
921
+ */
922
+ failed: number;
923
+ /** Tenants whose drain threw. Isolated: the rest of the tick still ran. */
924
+ storesFailed: number;
875
925
  /**
876
926
  * The pass reached the end of its work rather than stopping on its own
877
927
  * clock reserve. ⚠️ `false` is NORMAL under load and is not an error — it
@@ -1067,6 +1117,25 @@ declare global {
1067
1117
  * consumers such as Record<AutomationTrigger, …>; handle it in the same change.
1068
1118
  */
1069
1119
  export declare const AUTOMATION_TRIGGERS: readonly ["ABANDONED_CART", "REORDER_DUE"];
1120
+ /**
1121
+ * The {@link CampaignStatus} values a client may write on `POST /campaigns`.
1122
+ * Any other status is a 409 `CAMPAIGN_ILLEGAL_TRANSITION`; a client `sentAt` is
1123
+ * a 400 `VALIDATION_FAILED`.
1124
+ */
1125
+ export declare const CAMPAIGN_CLIENT_STATUSES: readonly ["draft", "scheduled", "cancelled"];
1126
+ /**
1127
+ * Refusals of the campaign status machine on `POST /campaigns`.
1128
+ *
1129
+ * - `CAMPAIGN_NOT_EDITABLE` · 409 — the campaign is `'sending'`, `'sent'` or
1130
+ * `'cancelled'`, and the request changes no status.
1131
+ * - `CAMPAIGN_ILLEGAL_TRANSITION` · 409 — the request (create or update) asks
1132
+ * for a server-owned status (`'sending'` / `'sent'`), or asks a `'sending'`,
1133
+ * `'sent'` or `'cancelled'` campaign for a different status.
1134
+ * - `CAMPAIGN_STATUS_CONFLICT` · 409 — the status changed between the read and
1135
+ * the write (usually the scheduler claiming it); re-read and retry.
1136
+ */
1137
+ export declare const CAMPAIGN_ERROR_CODES: readonly ["CAMPAIGN_NOT_EDITABLE", "CAMPAIGN_ILLEGAL_TRANSITION", "CAMPAIGN_STATUS_CONFLICT"];
1138
+ export type CampaignErrorCode = (typeof CAMPAIGN_ERROR_CODES)[number];
1070
1139
  /**
1071
1140
  * {@link RfmSegmentLabel} as a runtime value, so `isSegmentRuleValid`
1072
1141
  * (`stacks/services/segments.ts`) and the nightly scoring pass
package/dist/marketing.js CHANGED
@@ -8,6 +8,32 @@
8
8
  * consumers such as Record<AutomationTrigger, …>; handle it in the same change.
9
9
  */
10
10
  export const AUTOMATION_TRIGGERS = ['ABANDONED_CART', 'REORDER_DUE'];
11
+ /**
12
+ * The {@link CampaignStatus} values a client may write on `POST /campaigns`.
13
+ * Any other status is a 409 `CAMPAIGN_ILLEGAL_TRANSITION`; a client `sentAt` is
14
+ * a 400 `VALIDATION_FAILED`.
15
+ */
16
+ export const CAMPAIGN_CLIENT_STATUSES = [
17
+ 'draft',
18
+ 'scheduled',
19
+ 'cancelled',
20
+ ];
21
+ /**
22
+ * Refusals of the campaign status machine on `POST /campaigns`.
23
+ *
24
+ * - `CAMPAIGN_NOT_EDITABLE` · 409 — the campaign is `'sending'`, `'sent'` or
25
+ * `'cancelled'`, and the request changes no status.
26
+ * - `CAMPAIGN_ILLEGAL_TRANSITION` · 409 — the request (create or update) asks
27
+ * for a server-owned status (`'sending'` / `'sent'`), or asks a `'sending'`,
28
+ * `'sent'` or `'cancelled'` campaign for a different status.
29
+ * - `CAMPAIGN_STATUS_CONFLICT` · 409 — the status changed between the read and
30
+ * the write (usually the scheduler claiming it); re-read and retry.
31
+ */
32
+ export const CAMPAIGN_ERROR_CODES = [
33
+ 'CAMPAIGN_NOT_EDITABLE',
34
+ 'CAMPAIGN_ILLEGAL_TRANSITION',
35
+ 'CAMPAIGN_STATUS_CONFLICT',
36
+ ];
11
37
  /**
12
38
  * {@link RfmSegmentLabel} as a runtime value, so `isSegmentRuleValid`
13
39
  * (`stacks/services/segments.ts`) and the nightly scoring pass
package/dist/order.d.ts CHANGED
@@ -1236,6 +1236,18 @@ declare global {
1236
1236
  printedAt?: number;
1237
1237
  /** BE-minted pointer to the most recent print dispatch. Last-write-wins on reprint. */
1238
1238
  printJobId?: string;
1239
+ /**
1240
+ * A snapshot of each factura issued on this order, located by `invoiceId`.
1241
+ * Two print fields mirror the invoice row and mean the same thing there:
1242
+ * - `invoicePrinted`: stamped `true` when `POST /invoices { mode: 'print' }`
1243
+ * dispatches a job — optimistic, not evidence the job printed;
1244
+ * - `printedAt`: server ms epoch, stamped when the agent's `ACK_PRINTED`
1245
+ * correlates to the invoice row's CURRENT `printJobId`. Removed by every
1246
+ * dispatch, so it is absent until that job is confirmed.
1247
+ * Both are written right after the invoice row's own stamp and announced on an
1248
+ * operator-only `orders` frame carrying `{ orderId, invoices }`. An entry for a
1249
+ * factura printed before the order copy was kept carries neither.
1250
+ */
1239
1251
  invoices?: Partial<Invoice>[];
1240
1252
  /**
1241
1253
  * Bounded, embedded projections of this order's returns, capped at 50.
package/dist/print.d.ts CHANGED
@@ -560,8 +560,26 @@ declare global {
560
560
  * sorted and joined by `+`; an agent `ACK_FAILED` can classify to `PRINTER_PAUSED` /
561
561
  * `NO_PRINTER_ASSIGNED` / `PRINTER_NOT_FOUND`. Unrecognised agent
562
562
  * text stays in `detail` only, so absence of a code does NOT mean success.
563
+ *
564
+ * An automatic job (`Store.config.autoPrint`) adds three BE-side codes on
565
+ * `error`: `PRINT_QUEUE_EXPIRED` — it was held for want of a connected
566
+ * agent and its `heldUntil` passed — `AUTO_PRINT_SOURCE_GONE` — the
567
+ * order or invoice it was printing no longer exists when it was replayed —
568
+ * and `AUTO_PRINT_NO_LONGER_ELIGIBLE` — at replay it no longer passed the
569
+ * checks it was minted under (the store's switch for its event is off, the
570
+ * order was cancelled or its ready / delivered mark cleared, the factura is
571
+ * a credit note), so it was never sent.
572
+ * A HELD job is `queued` with `NO_PRINTER_CONNECTED` and `heldUntil`.
563
573
  */
564
574
  errorCode?: string;
575
+ /**
576
+ * ms epoch until which an automatic job with no connected agent is HELD —
577
+ * present only while the summary is `queued` waiting for an agent. The
578
+ * job is replayed when an agent of the store registers its printers, and
579
+ * ends `error` / `PRINT_QUEUE_EXPIRED` once this passes. Every later
580
+ * transition removes it. A manual print is never held.
581
+ */
582
+ heldUntil?: number;
565
583
  }
566
584
  /**
567
585
  * `data` payload of the server → agent `printers_active` WSS frame,
@@ -82,9 +82,10 @@ export declare const PURCHASE_WITHDRAWAL_ACTION_MODES: readonly ["accept", "decl
82
82
  * on) on an order that already has an open shopper return request
83
83
  * (`requested`, `authorized` or `received`); `data.rmaId` names it. One open
84
84
  * shopper request per order.
85
- * - `too-many-lines` — a shopper return request (either flow) naming, or
86
- * resolving to, more lines than one return request may hold; `data.max` is
87
- * the bound.
85
+ * - `too-many-lines` — a withdrawal naming more lines than
86
+ * {@link PURCHASE_WITHDRAWAL_LINES_MAX}, or a shopper return request (either
87
+ * flow) naming, or resolving to, more lines than one return request may hold;
88
+ * `data.max` is the bound.
88
89
  */
89
90
  export declare const PURCHASE_WITHDRAWAL_INELIGIBLE_REASONS: readonly ["order-cancelled", "no-customer", "partial-before-fulfilment", "line-not-on-order", "line-unavailable", "nothing-to-withdraw", "not-delivered", "return-request-open", "too-many-lines"];
90
91
  /**
@@ -103,8 +104,14 @@ export declare const PURCHASE_WITHDRAWAL_INELIGIBLE_REASONS: readonly ["order-ca
103
104
  export declare const PURCHASE_WITHDRAWAL_FISCAL_IN_FLIGHT_CAUSES: readonly ["in-progress", "uncertain", "stranded"];
104
105
  /** The longest decline or relay `note`, in characters after trimming. */
105
106
  export declare const PURCHASE_WITHDRAWAL_NOTE_MAX = 500;
106
- /** The most lines one request may name. */
107
- export declare const PURCHASE_WITHDRAWAL_LINES_MAX = 100;
107
+ /**
108
+ * The most lines one request may name. It is held to the worst case a single
109
+ * bound `Return` can carry: a withdrawal's hand-off RMA approves every line,
110
+ * and its `Return` must name exactly those lines, while the Return's
111
+ * transaction item budget fits 48 lines when every line restocks into a
112
+ * depósito as well as the store total. A larger withdrawal could never settle.
113
+ */
114
+ export declare const PURCHASE_WITHDRAWAL_LINES_MAX = 48;
108
115
  /** Calendar days of the withdrawal window after the later of delivery and purchase. */
109
116
  export declare const PURCHASE_WITHDRAWAL_WINDOW_DAYS = 10;
110
117
  /** The matrix row {@link PurchaseWithdrawal.windowClosesAt} is computed from. */
@@ -450,7 +457,7 @@ declare global {
450
457
  orderItemIndex?: number;
451
458
  /** The open shopper return request, for `return-request-open`. */
452
459
  rmaId?: string;
453
- /** The most lines one return request may hold, for `too-many-lines`. */
460
+ /** The bound a `too-many-lines` request exceeded: a withdrawal's, or a return request's. */
454
461
  max?: number;
455
462
  }
456
463
  /** One of {@link PURCHASE_WITHDRAWAL_LIST_FILTERS}. */
@@ -97,9 +97,10 @@ export const PURCHASE_WITHDRAWAL_ACTION_MODES = ['accept', 'decline', 'relay'];
97
97
  * on) on an order that already has an open shopper return request
98
98
  * (`requested`, `authorized` or `received`); `data.rmaId` names it. One open
99
99
  * shopper request per order.
100
- * - `too-many-lines` — a shopper return request (either flow) naming, or
101
- * resolving to, more lines than one return request may hold; `data.max` is
102
- * the bound.
100
+ * - `too-many-lines` — a withdrawal naming more lines than
101
+ * {@link PURCHASE_WITHDRAWAL_LINES_MAX}, or a shopper return request (either
102
+ * flow) naming, or resolving to, more lines than one return request may hold;
103
+ * `data.max` is the bound.
103
104
  */
104
105
  export const PURCHASE_WITHDRAWAL_INELIGIBLE_REASONS = [
105
106
  'order-cancelled',
@@ -128,8 +129,14 @@ export const PURCHASE_WITHDRAWAL_INELIGIBLE_REASONS = [
128
129
  export const PURCHASE_WITHDRAWAL_FISCAL_IN_FLIGHT_CAUSES = ['in-progress', 'uncertain', 'stranded'];
129
130
  /** The longest decline or relay `note`, in characters after trimming. */
130
131
  export const PURCHASE_WITHDRAWAL_NOTE_MAX = 500;
131
- /** The most lines one request may name. */
132
- export const PURCHASE_WITHDRAWAL_LINES_MAX = 100;
132
+ /**
133
+ * The most lines one request may name. It is held to the worst case a single
134
+ * bound `Return` can carry: a withdrawal's hand-off RMA approves every line,
135
+ * and its `Return` must name exactly those lines, while the Return's
136
+ * transaction item budget fits 48 lines when every line restocks into a
137
+ * depósito as well as the store total. A larger withdrawal could never settle.
138
+ */
139
+ export const PURCHASE_WITHDRAWAL_LINES_MAX = 48;
133
140
  /** Calendar days of the withdrawal window after the later of delivery and purchase. */
134
141
  export const PURCHASE_WITHDRAWAL_WINDOW_DAYS = 10;
135
142
  /** The matrix row {@link PurchaseWithdrawal.windowClosesAt} is computed from. */
package/dist/store.d.ts CHANGED
@@ -87,6 +87,28 @@ declare global {
87
87
  /** MercadoLibre orders. */
88
88
  mercadolibre?: string;
89
89
  }
90
+ /**
91
+ * The automatic-print switches — see `Store.config.autoPrint`. Every switch is
92
+ * OFF unless it is `true`: an absent leaf, an absent key and `false` all mean
93
+ * "do not print this automatically".
94
+ */
95
+ interface StoreAutoPrintConfig {
96
+ /** A factura authorised by ARCA (CAE, CAEA stamp, or the pending drain settling it). Credit notes never. */
97
+ invoiceAuthorized?: boolean;
98
+ /** A new order, per sales channel. A `service` channel order has no switch. */
99
+ orderCreated?: {
100
+ /** `POST /orders` on the App, including a presupuesto conversion. */
101
+ counter?: boolean;
102
+ /** The public storefront checkout. */
103
+ storefront?: boolean;
104
+ /** An order imported from MercadoLibre. */
105
+ meli?: boolean;
106
+ };
107
+ /** An order marked ready (its `readyAt` moving from unset to set). */
108
+ orderReady?: boolean;
109
+ /** The shipping tag, when an order with a delivery address is marked delivered. */
110
+ deliveryTag?: boolean;
111
+ }
90
112
  /**
91
113
  * One store-wide module a store can switch off for every user — the
92
114
  * catalogue is the shared `domain/STORE_MODULES.md`. Enumerable at runtime as
@@ -1135,6 +1157,24 @@ declare global {
1135
1157
  * whatever this holds.
1136
1158
  */
1137
1159
  channelWarehouses?: StoreChannelWarehouses;
1160
+ /**
1161
+ * Automatic printing — which events print a document with no operator
1162
+ * pressing «Imprimir». Written through `PATCH /store` `config` at the
1163
+ * PRIVILEGED tier (ADMIN/SUPERVISOR/MANAGER; a plain USER is refused
1164
+ * `STORE_CONFIG_FORBIDDEN`). No plan gate.
1165
+ *
1166
+ * ⚠️ The merge REPLACES the whole leaf, like every other `config` leaf: a
1167
+ * body carrying `autoPrint: { orderReady: true }` switches every other
1168
+ * event off. Send the full leaf.
1169
+ *
1170
+ * An automatic job fans out exactly as «Imprimir» does (the store's print
1171
+ * rules decide). When no print agent is connected it is HELD as `queued`
1172
+ * with `heldUntil` on its `PrintJobSummary`, replayed when an agent of the
1173
+ * store registers, and ended `error` / `PRINT_QUEUE_EXPIRED` at the end
1174
+ * of the store's Buenos Aires day or 12 h after the event, whichever is
1175
+ * first. Absent means nothing prints automatically.
1176
+ */
1177
+ autoPrint?: StoreAutoPrintConfig;
1138
1178
  /**
1139
1179
  * Store-wide module switches — see `StoreModules`. Written through
1140
1180
  * `PATCH /store` `config` at the PRIVILEGED tier (ADMIN/SUPERVISOR/MANAGER;
@@ -2592,7 +2632,7 @@ export type StoreModuleRefusalBody = {
2592
2632
  * Role refusals, `403` — a plain USER writing a privileged section. Each is
2593
2633
  * judged before any other check on that section:
2594
2634
  * - `STORE_CONFIG_FORBIDDEN` — any privileged `config` leaf (`modules`,
2595
- * `stock`, `priceDecimals`, `changePrice`, `mandatory2fa`, …).
2635
+ * `stock`, `priceDecimals`, `changePrice`, `mandatory2fa`, `autoPrint`, …).
2596
2636
  * - `STORE_ECOMMERCE_FORBIDDEN` — any `ecommerce` block.
2597
2637
  * - `STORE_FISCAL_FIELD_FORBIDDEN` — a fiscal field of the store.
2598
2638
  * - `STORE_JURISDICTION_FORBIDDEN` — `jurisdiction`.
package/dist/store.js CHANGED
@@ -107,7 +107,7 @@ export const STORE_MODULE_ERROR_CODES = [
107
107
  * Role refusals, `403` — a plain USER writing a privileged section. Each is
108
108
  * judged before any other check on that section:
109
109
  * - `STORE_CONFIG_FORBIDDEN` — any privileged `config` leaf (`modules`,
110
- * `stock`, `priceDecimals`, `changePrice`, `mandatory2fa`, …).
110
+ * `stock`, `priceDecimals`, `changePrice`, `mandatory2fa`, `autoPrint`, …).
111
111
  * - `STORE_ECOMMERCE_FORBIDDEN` — any `ecommerce` block.
112
112
  * - `STORE_FISCAL_FIELD_FORBIDDEN` — a fiscal field of the store.
113
113
  * - `STORE_JURISDICTION_FORBIDDEN` — `jurisdiction`.
@@ -587,6 +587,10 @@ export type TerminalEnrolmentErrorCode = (typeof TERMINAL_ENROLMENT_ERROR_CODES)
587
587
  * - `INVALID_PIN_FORMAT` · 400 — `POST /terminal/pin`: not 4–6 digits.
588
588
  * - `WRONG_CREDENTIALS` · 400 — `POST /terminal/pin`: the re-verified password
589
589
  * did not match.
590
+ * - `PASSWORD_NOT_SET` · 409 — `POST /terminal/activation` and `POST
591
+ * /terminal/pin`: the employee (a founder registered with Google or Apple) has
592
+ * no password. The body carries `providers`, the recorded sign-in providers, as
593
+ * `POST /auth/verify-password` does. Nothing is counted against the account.
590
594
  * - `IDEMPOTENCY_KEY_REUSE` · 422 / `IDEMPOTENCY_IN_PROGRESS` · 409 /
591
595
  * `INVALID_IDEMPOTENCY_KEY` · 400 — the `Idempotency-Key` header's own
592
596
  * refusals on `POST /terminal/operators`. A MISSING header is
@@ -610,7 +614,7 @@ export type TerminalEnrolmentErrorCode = (typeof TERMINAL_ENROLMENT_ERROR_CODES)
610
614
  * wrapper refused (`IDEMPOTENCY_KEY_REUSE` and the rest) exactly like a wrong
611
615
  * PIN: a key reused with another PIN is not a free guess.
612
616
  */
613
- export declare const TERMINAL_SESSION_ERROR_CODES: readonly ["TERMINAL_NOT_PAIRED", "SHIFT_NOT_OPEN", "TERMINAL_ACTIVATION_SESSION_INVALID", "TERMINAL_ACTIVATION_SESSION_MISMATCH", "TERMINAL_ACTIVATION_CLEANUP_FAILED", "TERMINAL_NOT_ACTIVATED", "INVALID_PIN", "ACCOUNT_LOCKED", "PIN_LOCKED", "ROLE_NOT_ALLOWED", "INVALID_PIN_FORMAT", "WRONG_CREDENTIALS", "IDEMPOTENCY_KEY_REUSE", "IDEMPOTENCY_IN_PROGRESS", "INVALID_IDEMPOTENCY_KEY", "IMPERSONATION_TERMINAL_FORBIDDEN", "VALIDATION_FAILED", "IDEMPOTENCY_LOCK_CONTENTION", "OPERATOR_TOKEN_FORBIDDEN", "INVALID_CURSOR", "OPERATOR_SESSION_STALE", "TERMINAL_LOCK_FORBIDDEN", "TERMINAL_LOCK_UNAVAILABLE"];
617
+ export declare const TERMINAL_SESSION_ERROR_CODES: readonly ["TERMINAL_NOT_PAIRED", "SHIFT_NOT_OPEN", "TERMINAL_ACTIVATION_SESSION_INVALID", "TERMINAL_ACTIVATION_SESSION_MISMATCH", "TERMINAL_ACTIVATION_CLEANUP_FAILED", "TERMINAL_NOT_ACTIVATED", "INVALID_PIN", "ACCOUNT_LOCKED", "PIN_LOCKED", "ROLE_NOT_ALLOWED", "INVALID_PIN_FORMAT", "WRONG_CREDENTIALS", "PASSWORD_NOT_SET", "IDEMPOTENCY_KEY_REUSE", "IDEMPOTENCY_IN_PROGRESS", "INVALID_IDEMPOTENCY_KEY", "IMPERSONATION_TERMINAL_FORBIDDEN", "VALIDATION_FAILED", "IDEMPOTENCY_LOCK_CONTENTION", "OPERATOR_TOKEN_FORBIDDEN", "INVALID_CURSOR", "OPERATOR_SESSION_STALE", "TERMINAL_LOCK_FORBIDDEN", "TERMINAL_LOCK_UNAVAILABLE"];
614
618
  export type TerminalSessionErrorCode = (typeof TERMINAL_SESSION_ERROR_CODES)[number];
615
619
  /** The device kinds a pairing code can pair (`TerminalPairingCodeRequest.kind`). */
616
620
  export declare const TERMINAL_PAIRING_KINDS: readonly ["terminal", "print-agent"];
package/dist/terminal.js CHANGED
@@ -87,6 +87,10 @@ export const TERMINAL_ENROLMENT_ERROR_CODES = [
87
87
  * - `INVALID_PIN_FORMAT` · 400 — `POST /terminal/pin`: not 4–6 digits.
88
88
  * - `WRONG_CREDENTIALS` · 400 — `POST /terminal/pin`: the re-verified password
89
89
  * did not match.
90
+ * - `PASSWORD_NOT_SET` · 409 — `POST /terminal/activation` and `POST
91
+ * /terminal/pin`: the employee (a founder registered with Google or Apple) has
92
+ * no password. The body carries `providers`, the recorded sign-in providers, as
93
+ * `POST /auth/verify-password` does. Nothing is counted against the account.
90
94
  * - `IDEMPOTENCY_KEY_REUSE` · 422 / `IDEMPOTENCY_IN_PROGRESS` · 409 /
91
95
  * `INVALID_IDEMPOTENCY_KEY` · 400 — the `Idempotency-Key` header's own
92
96
  * refusals on `POST /terminal/operators`. A MISSING header is
@@ -123,6 +127,7 @@ export const TERMINAL_SESSION_ERROR_CODES = [
123
127
  'ROLE_NOT_ALLOWED',
124
128
  'INVALID_PIN_FORMAT',
125
129
  'WRONG_CREDENTIALS',
130
+ 'PASSWORD_NOT_SET',
126
131
  'IDEMPOTENCY_KEY_REUSE',
127
132
  'IDEMPOTENCY_IN_PROGRESS',
128
133
  'INVALID_IDEMPOTENCY_KEY',
@@ -104,7 +104,8 @@ declare global {
104
104
  channel: 'adds' | 'email' | 'phone' | 'sms' | 'whatsapp';
105
105
  from: boolean | null;
106
106
  to: boolean;
107
- source: 'ui' | 'import' | 'api' | 'storefront';
107
+ /** `'complaint'`: a spam complaint withdrew `email` (`to: false`, no `ip`). */
108
+ source: 'ui' | 'import' | 'api' | 'storefront' | 'complaint';
108
109
  /**
109
110
  * ⚠️ PII — absent when the write had no request-level IP. Stored only: every
110
111
  * reader withholds it (both timeline reads, socket frames, the audit export
@@ -125,7 +126,7 @@ declare global {
125
126
  }
126
127
  /** Rows written for a customer's own action. Kept separate from {@link UserActivityEvent}. */
127
128
  type CustomerActivityEvent = CustomerConsentUpdatedEvent;
128
- /** A staff CSV import set consent on a customer row, on the customer's word. */
129
+ /** A staff CSV import CLEARED consent on a customer row (an import never grants; a `true` cell is dropped). */
129
130
  interface CustomerConsentImportedEvent extends UserActivityEventBase {
130
131
  event: 'Customer Consent Imported';
131
132
  customer_id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "platform-types",
3
- "version": "1.10.834",
3
+ "version": "1.10.836",
4
4
  "description": "Shared wire and entity contracts for the platform.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",