platform-types 1.10.833 → 1.10.835

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. */
@@ -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.
@@ -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/return.d.ts CHANGED
@@ -64,8 +64,15 @@ declare global {
64
64
  * - `unnumbered-factura` — a live factura entry carries no valid voucher
65
65
  * number (and is not `pending_cae`), or a live entry carries no
66
66
  * `invoiceType` at all and is counted as a factura, failing closed.
67
+ * - `freight-line-unmapped` — the return gives the freight back
68
+ * ({@link Return.freight}) and every factura line maps to a goods line,
69
+ * none to the order's freight line: the factura carries no freight line. A
70
+ * note without the freight would credit less than the return, so none is
71
+ * committed. A factura whose freight line differs from the order's (sku,
72
+ * name, quantity, unit or IVA) fails the whole line mapping instead, and
73
+ * answers `409 RETURN_LEDGER_BASIS_UNAVAILABLE`.
67
74
  */
68
- type ReturnCreditNoteUnallocatableCause = 'multiple-facturas' | 'missing-invoice-id' | 'unnumbered-factura';
75
+ type ReturnCreditNoteUnallocatableCause = 'multiple-facturas' | 'missing-invoice-id' | 'unnumbered-factura' | 'freight-line-unmapped';
69
76
  /**
70
77
  * `data` of `409 RETURN_CREDIT_NOTE_REQUIRED`: the request sent
71
78
  * `emitCreditNote: false` for a return whose `flow` requires the credit note
@@ -213,6 +220,18 @@ declare global {
213
220
  */
214
221
  warehouseId?: string;
215
222
  items: ReturnItem[];
223
+ /**
224
+ * The order's freight line, given back by the `purchase-withdrawal` return that exhausts the order's goods
225
+ * lines. Server-appended, never client-settable: a body naming the freight index is
226
+ * `400 RETURN_INVALID_SUBSET`. `total` is the freight line's charge (`freightLineTotal`, never discounted)
227
+ * in the order's currency, and it is part of {@link Return.total}. Not a {@link ReturnItem}: it restocks
228
+ * nothing and writes no `SALE#`, `INCOME#` or disposition. The credit note's subset is `items` plus this.
229
+ * Operator-only, like the rest of the row.
230
+ *
231
+ * ⚠️ OPTIONAL PERMANENTLY. Absent on every other return — every other flow, a withdrawal return that leaves
232
+ * goods behind, an order with no freight line or one charged at 0 — and on every row written before it existed.
233
+ */
234
+ freight?: ReturnFreight;
216
235
  /** Operator-only intake allocations. Absent on legacy returns means no recorded movement snapshot. */
217
236
  stockMovements?: ReturnStockMovement[];
218
237
  /**
@@ -254,8 +273,10 @@ declare global {
254
273
  /**
255
274
  * The commercial refund in the originating order's currency. Selected
256
275
  * units use the sale's per-unit rounding rule, capped at the remaining
257
- * recorded charge; the final units refund that charge less earlier
258
- * `ReturnSummary.total` amounts. Historical returns remain as recorded.
276
+ * recorded charge; the final units refund that charge, less its freight
277
+ * line, less earlier `ReturnSummary.total` amounts. Historical returns
278
+ * remain as recorded. The one exception keeps the freight: a return
279
+ * carrying {@link Return.freight} refunds it too.
259
280
  *
260
281
  * The ACCOUNT credit is a separate conversion into the customer ledger's
261
282
  * currency, and a credit note reverses its factura's fiscal economics in
@@ -639,6 +660,15 @@ declare global {
639
660
  * for the idempotency window and claims no second number.
640
661
  */
641
662
  type ReturnCreditNoteErrorCode = 'PARTIAL_NC_AFIP_DOWN' | 'NC_MULTI_FAC_UNSUPPORTED' | 'NC_EXCEEDS_FAC_TOTAL' | 'CBTE_ASOC_NOT_FOUND' | 'CBTE_ASOC_NOT_CREDITABLE' | 'CBTE_ASOC_AMBIGUOUS' | 'INVOICE_POINT_OF_SALE_CONFLICT' | 'INVOICE_POINT_OF_SALE_RESERVED' | 'INVOICE_MIXED_CURRENCY' | 'LINE_CURRENCY_NOT_INVOICEABLE' | 'LINE_IVA_TYPE_NOT_INVOICEABLE' | 'AFIP_LINE_FX_UNSTAMPED' | 'CUSTOMER_AFIP_SHAPE_INVALID' | 'AFIP_BNA_RATE_UNAVAILABLE' | 'PARTIAL_NC_INVALID_SUBSET' | 'PARTIAL_NC_REQUIRES_CBTE' | 'INVOICE_PERSIST_AFTER_CAE_FAILED' | 'NC_EMISSION_FAILED';
663
+ /** The freight a `purchase-withdrawal` return gives back — see {@link Return.freight}. */
664
+ interface ReturnFreight {
665
+ /** The freight line's index in `Order.items`. */
666
+ orderItemIndex: number;
667
+ /** The freight line's whole quantity. */
668
+ quantity: number;
669
+ /** The freight line's charge in the order's currency (`freightLineTotal`). */
670
+ total: number;
671
+ }
642
672
  interface ReturnItem {
643
673
  /**
644
674
  * Index of the line in the ORIGINATING `Order.items` array. Returns are
@@ -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.833",
3
+ "version": "1.10.835",
4
4
  "description": "Shared wire and entity contracts for the platform.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",