@vivoa/plugin-sdk 0.6.5 → 0.6.6

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.
@@ -0,0 +1,1416 @@
1
+ declare enum FieldType {
2
+ String = "string",
3
+ Password = "password",
4
+ Url = "url",
5
+ Number = "number",
6
+ Boolean = "boolean",
7
+ Select = "select"
8
+ }
9
+ declare enum ConfigScope {
10
+ Global = "global",
11
+ Country = "country"
12
+ }
13
+ interface SelectOption {
14
+ value: string;
15
+ label: string;
16
+ }
17
+ interface PluginConfigFieldDependency {
18
+ field: string;
19
+ value: string[];
20
+ }
21
+ interface PluginConfigField {
22
+ key: string;
23
+ label: string;
24
+ type: FieldType;
25
+ required: boolean;
26
+ scopedTo?: ConfigScope;
27
+ dependsOn?: PluginConfigFieldDependency;
28
+ placeholder?: string;
29
+ description?: string;
30
+ options?: SelectOption[];
31
+ internal?: boolean;
32
+ /** Visual group header rendered above the first field of each new group. */
33
+ group?: string;
34
+ }
35
+
36
+ declare enum FieldVariant {
37
+ Text = "text",
38
+ Secret = "secret",
39
+ Url = "url",
40
+ Number = "number",
41
+ Select = "select",
42
+ Textarea = "textarea",
43
+ /**
44
+ * Renders a small option set (a `select`-typed schema field) as a segmented
45
+ * pill control instead of a dropdown. Purely presentational — the underlying
46
+ * value/options come from the referenced configSchema field.
47
+ */
48
+ Segmented = "segmented"
49
+ }
50
+ declare enum ActionVariant {
51
+ Primary = "primary",
52
+ Secondary = "secondary",
53
+ Danger = "danger"
54
+ }
55
+ declare enum RepeaterSource {
56
+ SupportedCountries = "supportedCountries"
57
+ }
58
+ /**
59
+ * Presentation mode for the whole layout. Declared by the plugin wins; when
60
+ * absent the HOST infers it (wizard when the layout iterates per-country,
61
+ * stacked otherwise). Inference is a host concern and is NOT part of this
62
+ * contract — the SDK only carries the optional declared value.
63
+ */
64
+ declare enum LayoutMode {
65
+ Wizard = "wizard",
66
+ Stacked = "stacked",
67
+ Tabs = "tabs",
68
+ Sections = "sections"
69
+ }
70
+ declare enum BlockType {
71
+ Field = "field",
72
+ Group = "group",
73
+ Toggle = "toggle",
74
+ Action = "action",
75
+ Callout = "callout",
76
+ Markdown = "markdown",
77
+ Divider = "divider",
78
+ Conditional = "conditional",
79
+ Repeater = "repeater",
80
+ Card = "card",
81
+ Banner = "banner",
82
+ CopyField = "copyField",
83
+ InlineRow = "inlineRow",
84
+ Tabs = "tabs",
85
+ Accordion = "accordion",
86
+ KeyValue = "keyValue"
87
+ }
88
+ declare enum StepStatusFrom {
89
+ Fields = "fields",
90
+ Action = "action"
91
+ }
92
+ /**
93
+ * Shared base every block extends. Carries the optional 12-col grid span
94
+ * (1–12). Out-of-range values are clamped by the HOST at render time; the
95
+ * contract accepts any number so a newer plugin is never bricked by an older
96
+ * host.
97
+ */
98
+ interface BlockBase {
99
+ span?: number;
100
+ }
101
+ interface FieldBlock extends BlockBase {
102
+ type: BlockType.Field;
103
+ /** References a key in the plugin's configSchema. */
104
+ key: string;
105
+ variant?: FieldVariant;
106
+ /** Override the configSchema label inside this layout. */
107
+ labelOverride?: string;
108
+ /**
109
+ * Name of a `resolveOptions` source. When set, the host resolves the option
110
+ * list at runtime via `plugin.resolveOptions(optionsFrom, ctx)` instead of a
111
+ * static option set. NOT a configSchema key — it is a runtime source id.
112
+ */
113
+ optionsFrom?: string;
114
+ }
115
+ interface GroupBlock extends BlockBase {
116
+ type: BlockType.Group;
117
+ title: string;
118
+ description?: string;
119
+ /** configSchema keys included in this group. */
120
+ fields: string[];
121
+ collapsible?: boolean;
122
+ defaultOpen?: boolean;
123
+ }
124
+ interface ToggleBlock extends BlockBase {
125
+ type: BlockType.Toggle;
126
+ /** Logical id, not necessarily a config key. */
127
+ key: string;
128
+ label: string;
129
+ description?: string;
130
+ }
131
+ interface ActionBlock extends BlockBase {
132
+ type: BlockType.Action;
133
+ /** Matches a manifest.actions[id]. */
134
+ id: string;
135
+ label: string;
136
+ variant?: ActionVariant;
137
+ /** When set, the action result feeds the named callout slot. */
138
+ resultSlot?: string;
139
+ }
140
+ interface CalloutBlock extends BlockBase {
141
+ type: BlockType.Callout;
142
+ variant: MessageVariant;
143
+ text: string;
144
+ /** Slot id for `action.resultSlot` to write into. */
145
+ slot?: string;
146
+ }
147
+ interface MarkdownBlock extends BlockBase {
148
+ type: BlockType.Markdown;
149
+ content: string;
150
+ }
151
+ interface DividerBlock extends BlockBase {
152
+ type: BlockType.Divider;
153
+ }
154
+ type ConditionalEquals = string | number | boolean | null;
155
+ interface ConditionalWhen {
156
+ key: string;
157
+ equals: ConditionalEquals;
158
+ }
159
+ interface ConditionalBlock extends BlockBase {
160
+ type: BlockType.Conditional;
161
+ when: ConditionalWhen;
162
+ blocks: Block[];
163
+ }
164
+ interface RepeaterBlock extends BlockBase {
165
+ type: BlockType.Repeater;
166
+ source: RepeaterSource;
167
+ /** Token interpolation: {countryCode}, {countryName}. */
168
+ blocks: Block[];
169
+ }
170
+ /** Titled container. Recurses into `blocks`. */
171
+ interface CardBlock extends BlockBase {
172
+ type: BlockType.Card;
173
+ title?: string;
174
+ description?: string;
175
+ blocks: Block[];
176
+ }
177
+ /** Prominent variant-styled message. Leaf. */
178
+ interface BannerBlock extends BlockBase {
179
+ type: BlockType.Banner;
180
+ variant: MessageVariant;
181
+ title?: string;
182
+ text: string;
183
+ }
184
+ /**
185
+ * Read-only value with a copy affordance. EXACTLY ONE source of the value:
186
+ * a literal, a host `computedValues` key, or a read-only echo of a config key.
187
+ */
188
+ interface CopyFieldBlock extends BlockBase {
189
+ type: BlockType.CopyField;
190
+ label: string;
191
+ /** Literal value. */
192
+ value?: string;
193
+ /** Key into the host-provided `computedValues` map. */
194
+ computedKey?: string;
195
+ /** Read-only echo of a configSchema field value. */
196
+ fieldKey?: string;
197
+ }
198
+ /** Horizontal row on wide viewports, stacks on narrow. Recurses. */
199
+ interface InlineRowBlock extends BlockBase {
200
+ type: BlockType.InlineRow;
201
+ blocks: Block[];
202
+ }
203
+ interface TabItem {
204
+ id: string;
205
+ label: string;
206
+ blocks: Block[];
207
+ }
208
+ /** One panel visible at a time. Recurses per tab. */
209
+ interface TabsBlock extends BlockBase {
210
+ type: BlockType.Tabs;
211
+ tabs: TabItem[];
212
+ }
213
+ interface AccordionSection {
214
+ id: string;
215
+ title: string;
216
+ defaultOpen?: boolean;
217
+ blocks: Block[];
218
+ }
219
+ /** Collapsible sections. Local toggle, not persisted. Recurses per section. */
220
+ interface AccordionBlock extends BlockBase {
221
+ type: BlockType.Accordion;
222
+ sections: AccordionSection[];
223
+ }
224
+ interface KeyValueRow {
225
+ label: string;
226
+ value?: string;
227
+ computedKey?: string;
228
+ }
229
+ /** Read-only label/value list. Leaf. */
230
+ interface KeyValueBlock extends BlockBase {
231
+ type: BlockType.KeyValue;
232
+ rows: KeyValueRow[];
233
+ }
234
+ type Block = FieldBlock | GroupBlock | ToggleBlock | ActionBlock | CalloutBlock | MarkdownBlock | DividerBlock | ConditionalBlock | RepeaterBlock | CardBlock | BannerBlock | CopyFieldBlock | InlineRowBlock | TabsBlock | AccordionBlock | KeyValueBlock;
235
+ interface StepStatusFromFields {
236
+ from: StepStatusFrom.Fields;
237
+ }
238
+ interface StepStatusFromAction {
239
+ from: StepStatusFrom.Action;
240
+ actionId: string;
241
+ }
242
+ type StepStatusSource = StepStatusFromFields | StepStatusFromAction;
243
+ interface SetupStep {
244
+ id: string;
245
+ title: string;
246
+ intro?: string;
247
+ blocks: Block[];
248
+ /** When set, the step renders once per item of the source. */
249
+ repeatFor?: RepeaterSource;
250
+ status?: StepStatusSource;
251
+ }
252
+ interface SetupAction {
253
+ id: string;
254
+ /** Optional human label (admin can render in audit logs). */
255
+ label?: string;
256
+ /** Whether the action runs per-country. Default: false. */
257
+ perCountry?: boolean;
258
+ }
259
+ interface SetupLayout {
260
+ /** Reserved for Phase 2 — points to a plugin-shipped React bundle. */
261
+ customScreen?: string;
262
+ /**
263
+ * Presentation mode. Declared wins; when absent the host infers it. Additive
264
+ * in 0.4.0 — an older host ignores it and keeps its inferred layout.
265
+ */
266
+ mode?: LayoutMode;
267
+ intro?: string;
268
+ steps: SetupStep[];
269
+ actions?: SetupAction[];
270
+ }
271
+
272
+ interface SetupLayoutValidation {
273
+ success: boolean;
274
+ data?: SetupLayout;
275
+ errors?: string[];
276
+ }
277
+ /** Validates the full layout including recursive blocks. */
278
+ declare function validateSetupLayout(input: unknown): SetupLayoutValidation;
279
+ declare function crossValidateSetupLayout(layout: SetupLayout, configSchema: PluginConfigField[]): string[];
280
+
281
+ /**
282
+ * Verified authorship. `handle` is a stable slug the registry owns — NOT a
283
+ * display label. Two plugins from the same author share one handle; the host
284
+ * ties it to the publish token so a third party cannot push versions of a
285
+ * plugin they do not own. `displayName`/`url` are presentation only.
286
+ */
287
+ interface PluginAuthor {
288
+ /** Stable registry identity. Lowercase slug, bound to the publish token. */
289
+ handle: string;
290
+ /** Human-facing name shown in the admin app store. */
291
+ displayName: string;
292
+ /** Optional author/developer homepage. */
293
+ url?: string;
294
+ }
295
+ /** A handle is a URL-safe slug: starts alphanumeric, then alphanumerics/dashes. */
296
+ declare const AUTHOR_HANDLE_RE: RegExp;
297
+ interface AuthorValidation {
298
+ ok: boolean;
299
+ errors: string[];
300
+ }
301
+ /**
302
+ * Pure structural check the REGISTRY runs at publish time (the SDK ships the
303
+ * rule so host and tooling agree on one spelling). Fail-closed: a missing or
304
+ * malformed author is rejected, never defaulted.
305
+ */
306
+ declare function validatePluginAuthor(value: unknown): AuthorValidation;
307
+
308
+ interface PluginManifest {
309
+ name: string;
310
+ displayName?: string;
311
+ version: string;
312
+ entry: string;
313
+ type?: PluginType;
314
+ icon?: string;
315
+ description?: string;
316
+ dependencies?: string[];
317
+ /** Minimum @vivoa/store host version this plugin runs on (semver). */
318
+ minHostVersion?: string;
319
+ /**
320
+ * SDK contract version the plugin was compiled against (see SDK_VERSION).
321
+ * Hosts refuse plugins with an incompatible contract via isSdkCompatible.
322
+ */
323
+ sdkVersion?: string;
324
+ /**
325
+ * Verified authorship. The registry binds `author.handle` to the publish
326
+ * token on first publish and rejects later versions from a non-owner.
327
+ * Optional in the TYPE (older manifests predate it); the registry enforces
328
+ * presence at publish via validatePluginAuthor. Supersedes the free-text
329
+ * `developer` string first-party manifests used to carry.
330
+ */
331
+ author?: PluginAuthor;
332
+ /**
333
+ * Declarative admin setup screen. When omitted, host renders the legacy
334
+ * auto-form derived from the plugin's configSchema.
335
+ */
336
+ setup?: SetupLayout;
337
+ }
338
+
339
+ declare enum PluginType {
340
+ Payment = "payment",
341
+ Shipping = "shipping",
342
+ Analytics = "analytics",
343
+ Invoicing = "invoicing",
344
+ Generic = "generic"
345
+ }
346
+ /** Visual tone for messages, callouts and action results. */
347
+ declare enum MessageVariant {
348
+ Info = "info",
349
+ Warning = "warning",
350
+ Success = "success",
351
+ Danger = "danger"
352
+ }
353
+ interface PluginActionResult {
354
+ ok: boolean;
355
+ message?: string;
356
+ variant?: MessageVariant;
357
+ data?: unknown;
358
+ }
359
+ interface PluginActionContext {
360
+ /** When the action was triggered from a country-scoped step. */
361
+ country?: string;
362
+ /** Free-form payload sent from the admin UI. */
363
+ payload?: unknown;
364
+ /**
365
+ * Resolved GLOBAL-scope config for the current tenant. The host populates
366
+ * this so actions (e.g. testConnection) can build a client without any
367
+ * prior `initialize()` — same stateless discipline as the dispatch methods.
368
+ */
369
+ config?: Record<string, unknown>;
370
+ /** Resolved config for `country`, when the action is country-scoped. */
371
+ countryConfig?: Record<string, unknown>;
372
+ }
373
+ /** Option resolved at runtime for a field whose `optionsFrom` is set. */
374
+ interface FieldOption {
375
+ value: string;
376
+ label: string;
377
+ }
378
+ interface IPlugin {
379
+ readonly manifest: PluginManifest;
380
+ onActivate?(): Promise<void>;
381
+ onDeactivate?(): Promise<void>;
382
+ invokeAction?(actionId: string, ctx: PluginActionContext): Promise<PluginActionResult>;
383
+ /**
384
+ * Resolve the option list for a field whose `optionsFrom` matches `source`.
385
+ * PURE (query semantics) — the host never persists anything as a side effect
386
+ * of calling it. Optional: a plugin without it degrades the field to a plain
387
+ * input. Draft-aware via `ctx` (same resolved-config discipline as
388
+ * `invokeAction`).
389
+ */
390
+ resolveOptions?(source: string, ctx: PluginActionContext): Promise<FieldOption[]>;
391
+ }
392
+ interface ConfigurablePlugin {
393
+ loadConfig(config: Record<string, unknown>): void;
394
+ }
395
+ declare function isConfigurablePlugin(plugin: unknown): plugin is ConfigurablePlugin;
396
+
397
+ /**
398
+ * Everything a plugin needs to serve ONE operation for ONE tenant, resolved by
399
+ * the host up front. Nothing here is cached on the plugin instance — the same
400
+ * plugin object serves every tenant, and each call carries its own context.
401
+ * This is what removes the "init + dispatch in the same synchronous block"
402
+ * hazard the stateful contract used to require.
403
+ */
404
+ interface DispatchContext {
405
+ /** Resolved, merged GLOBAL-scope config for the current tenant. */
406
+ readonly config: Record<string, unknown>;
407
+ /**
408
+ * Resolved config for the country in scope, when the operation is
409
+ * country-bound (e.g. a shipping quote for SV). Merged defaults + tenant DB.
410
+ */
411
+ readonly countryConfig?: Record<string, unknown>;
412
+ /**
413
+ * All resolved country configs keyed by ISO code, for operations that span
414
+ * countries. Prefer {@link countryConfig} for single-country dispatch.
415
+ */
416
+ readonly countryConfigs?: Record<string, Record<string, unknown>>;
417
+ /** The ISO country code in scope, when applicable. */
418
+ readonly countryCode?: string;
419
+ }
420
+
421
+ /**
422
+ * Free-form gateway code defined by each plugin in its manifest/config.
423
+ * The host stores this string verbatim; no enum of allowed providers.
424
+ */
425
+ type PaymentGatewayCode = string;
426
+ declare enum TransactionStatus {
427
+ Pending = "Pending",
428
+ PendingRedirect = "Pending_Redirect",
429
+ Approved = "Approved",
430
+ Declined = "Declined",
431
+ Failed = "Failed",
432
+ Error = "Error"
433
+ }
434
+ declare enum GatewayEnvironment {
435
+ Sandbox = "sandbox",
436
+ Production = "production"
437
+ }
438
+ interface PaymentCard {
439
+ number: string;
440
+ cvv: string;
441
+ expirationMonth: string;
442
+ expirationYear: string;
443
+ holderFirstName: string;
444
+ holderLastName: string;
445
+ }
446
+ interface PaymentCustomer {
447
+ email: string;
448
+ phone: string;
449
+ address?: string;
450
+ city?: string;
451
+ state?: string;
452
+ country?: string;
453
+ zip?: string;
454
+ }
455
+ interface PaymentPayload {
456
+ amount: number;
457
+ currency: string;
458
+ orderId: string;
459
+ card: PaymentCard;
460
+ customer: PaymentCustomer;
461
+ }
462
+ /**
463
+ * Generic verify payload. Provider-specific data travels in `providerData`,
464
+ * each plugin casts it to its own type. The host passes it through opaquely.
465
+ */
466
+ interface PaymentVerifyPayload {
467
+ transactionId: string;
468
+ providerData?: unknown;
469
+ }
470
+ interface PaymentResult {
471
+ status: TransactionStatus;
472
+ gatewayTransactionId: string;
473
+ message: string;
474
+ authorizationCode?: string;
475
+ redirectUrl?: string;
476
+ rawResponse?: unknown;
477
+ }
478
+ /**
479
+ * How the host should render the payment step for this gateway.
480
+ * - `card-in` host-owned card form, charged via `processPayment` (default).
481
+ * - `offsite-redirect` host sends the shopper to `redirectUrl`.
482
+ * - `embed` gateway-owned iframe initialised via `createPaymentSession`.
483
+ */
484
+ type PaymentRenderMode = 'card-in' | 'offsite-redirect' | 'embed';
485
+ interface GatewayStaticConfig {
486
+ code: PaymentGatewayCode;
487
+ displayName: string;
488
+ description: string;
489
+ environment: GatewayEnvironment;
490
+ supportedMethods: string[];
491
+ requiredEnvVars: string[];
492
+ /**
493
+ * OPTIONAL render mode. Absent ⇒ host treats the gateway as `'card-in'`.
494
+ * Back-compat: existing card-in plugins (Wompi/Serfinsa) omit this field.
495
+ */
496
+ renderMode?: PaymentRenderMode;
497
+ }
498
+ /**
499
+ * Cardless payload for creating a gateway-hosted payment session (INV-1).
500
+ * Used by `embed` / `offsite-redirect` gateways where the host never touches
501
+ * card data — the shopper enters it inside the gateway's own surface.
502
+ */
503
+ interface PaymentSessionPayload {
504
+ /**
505
+ * Charge amount in DECIMAL major units, 2 decimal places (e.g. `12.34`).
506
+ * Matches Boxful `@boxful/contracts` `getTwoDecimals` / `toFixed(2)`.
507
+ */
508
+ amount: number;
509
+ currency: string;
510
+ orderId: string;
511
+ customer: PaymentCustomer;
512
+ /** Optional display/reconciliation hints shown on the gateway surface. */
513
+ orderSummary?: {
514
+ description?: string;
515
+ merchant?: string;
516
+ reference?: string;
517
+ };
518
+ }
519
+ /**
520
+ * Everything the host needs to mount a gateway-owned payment iframe.
521
+ * `sessionToken` is OPAQUE to the host; the amount is sealed server-side by the
522
+ * gateway, so the host cannot tamper with it.
523
+ */
524
+ interface EmbedInit {
525
+ sessionToken: string;
526
+ iframeUrl: string;
527
+ allowedOrigins: string[];
528
+ /** Expiry as epoch SECONDS. Owner TTL guidance: 15 minutes. */
529
+ expiresAt?: number;
530
+ }
531
+ /**
532
+ * Result of `createPaymentSession`. `embedInit` is present iff
533
+ * `renderMode === 'embed'`; `redirectUrl` iff `renderMode === 'offsite-redirect'`.
534
+ */
535
+ interface PaymentSessionResult {
536
+ renderMode: PaymentRenderMode;
537
+ embedInit?: EmbedInit;
538
+ redirectUrl?: string;
539
+ }
540
+ interface PaymentPluginConfig {
541
+ configSchema: PluginConfigField[];
542
+ }
543
+ interface WebhookSignatureResult {
544
+ isValid: boolean;
545
+ error?: string;
546
+ }
547
+ interface IPaymentPlugin extends IPlugin {
548
+ readonly gatewayCode: PaymentGatewayCode;
549
+ readonly staticConfig: GatewayStaticConfig;
550
+ readonly paymentConfig?: PaymentPluginConfig;
551
+ processPayment(payload: PaymentPayload, ctx: DispatchContext): Promise<PaymentResult>;
552
+ verifyTransaction?(payload: PaymentVerifyPayload, ctx: DispatchContext): Promise<PaymentResult>;
553
+ validateWebhookSignature?(rawBody: string | Buffer, headers: Record<string, string>, ctx: DispatchContext): WebhookSignatureResult;
554
+ processWebhook?(payload: unknown, ctx: DispatchContext): Promise<void>;
555
+ /**
556
+ * Query the gateway for the current status of a previously attempted charge.
557
+ * Called by the host when `processPayment` returns `status: Error` with a
558
+ * non-empty `gatewayTransactionId` (e.g. Serfinsa `audit_no` on timeout).
559
+ * MUST NOT accept or re-use card data — ID-only reconciliation.
560
+ */
561
+ queryTransaction?(ref: {
562
+ transactionId: string;
563
+ }, ctx: DispatchContext): Promise<PaymentResult>;
564
+ /**
565
+ * Create a gateway-hosted payment session for `embed` / `offsite-redirect`
566
+ * render modes. OPTIONAL — `card-in` plugins never implement it and charge
567
+ * via `processPayment` instead. STATELESS: gateway credentials arrive per
568
+ * call via `ctx.config` (INV-4); no card data is ever passed here (INV-1).
569
+ */
570
+ createPaymentSession?(payload: PaymentSessionPayload, ctx: DispatchContext): Promise<PaymentSessionResult>;
571
+ }
572
+
573
+ declare enum ShippingFeature {
574
+ Tracking = "tracking",
575
+ Cod = "cod",
576
+ Quotes = "quotes",
577
+ Labels = "labels",
578
+ /**
579
+ * The carrier pushes shipment status changes to a registered webhook URL,
580
+ * and the plugin can normalize + authenticate that payload via
581
+ * {@link IShippingPlugin.parseWebhook}. Enables unattended order transitions
582
+ * (e.g. → delivered) instead of manual admin updates.
583
+ */
584
+ TrackingSync = "tracking-sync"
585
+ }
586
+ declare enum CodFeeType {
587
+ Percentage = "percentage",
588
+ Fixed = "fixed"
589
+ }
590
+ interface CodFeeConfig {
591
+ type: CodFeeType;
592
+ value: number;
593
+ description: string;
594
+ }
595
+ interface ShippingDefaultConfig {
596
+ global?: Record<string, unknown>;
597
+ countries?: Record<string, Record<string, unknown>>;
598
+ }
599
+ interface ShippingPluginConfig {
600
+ supportedCountries: string[];
601
+ supportedFeatures: ShippingFeature[];
602
+ configSchema: PluginConfigField[];
603
+ codFeeConfig?: CodFeeConfig;
604
+ defaultConfig?: ShippingDefaultConfig;
605
+ }
606
+ interface ShippingStaticConfig {
607
+ code: string;
608
+ displayName: string;
609
+ description: string;
610
+ iconUrl?: string;
611
+ supportedCountries: string[];
612
+ supportedFeatures: ShippingFeature[];
613
+ configSchema: PluginConfigField[];
614
+ codFeeConfig?: CodFeeConfig;
615
+ }
616
+ interface ShipmentAddress {
617
+ fullName: string;
618
+ phone: string;
619
+ phoneAreaCode: string;
620
+ email: string | null;
621
+ address: string;
622
+ referencePoint: string;
623
+ instructions?: string;
624
+ city: string;
625
+ cityId?: string;
626
+ state: string;
627
+ stateId?: string;
628
+ zip?: string;
629
+ country: string;
630
+ latitude?: number;
631
+ longitude?: number;
632
+ }
633
+ interface ShipmentParcel {
634
+ content: string;
635
+ length: number;
636
+ width: number;
637
+ height: number;
638
+ weight: number;
639
+ price?: number;
640
+ isFragile?: boolean;
641
+ }
642
+ interface ShippingQuoteParams {
643
+ origin: ShipmentAddress;
644
+ destination: ShipmentAddress;
645
+ countryCode: string;
646
+ parcels?: ShipmentParcel[];
647
+ }
648
+ interface ShippingQuote {
649
+ carrierId: string;
650
+ carrierName: string;
651
+ price: number;
652
+ codCommission: number;
653
+ codFee?: number;
654
+ supportsCod: boolean;
655
+ deliveryType: string;
656
+ logo: string | null;
657
+ isPricePromotion: boolean;
658
+ isCommissionPromotion: boolean;
659
+ provider: string;
660
+ }
661
+ interface CodConfig {
662
+ enabled: boolean;
663
+ amount: number;
664
+ }
665
+ interface CreateShipmentParams {
666
+ orderId: string;
667
+ pickup: ShipmentAddress;
668
+ delivery: ShipmentAddress;
669
+ parcels: ShipmentParcel[];
670
+ recolectionDate: Date;
671
+ cod?: CodConfig;
672
+ courierId?: string;
673
+ serviceLevelToken?: string;
674
+ }
675
+ interface ShipmentResult {
676
+ externalId: string;
677
+ trackingNumber: string;
678
+ trackingUrl: string;
679
+ labelUrl: string | null;
680
+ provider: string;
681
+ courierName: string;
682
+ status: string;
683
+ rawResponse?: Record<string, unknown>;
684
+ }
685
+ /**
686
+ * Normalized, carrier-agnostic shipment status event produced by
687
+ * {@link IShippingPlugin.parseWebhook}. The host matches `trackingNumber`
688
+ * against its stored shipment to resolve the order, so the plugin never needs
689
+ * host identifiers. `status: 'unknown'` is a first-class value for carrier
690
+ * states the plugin does not map — the host treats it as a no-op instead of
691
+ * guessing a transition.
692
+ */
693
+ interface TrackingEvent {
694
+ trackingNumber: string;
695
+ status: 'in_transit' | 'delivered' | 'cancelled' | 'unknown';
696
+ /** ISO-8601 instant the carrier reported the change, when available. */
697
+ occurredAt?: string;
698
+ /** Human-readable carrier status, for audit/logging. */
699
+ statusDescription?: string;
700
+ /** Untouched carrier payload, for diagnostics. Never trusted for control flow. */
701
+ raw?: unknown;
702
+ }
703
+ interface IShippingPlugin extends IPlugin {
704
+ readonly name: string;
705
+ readonly shippingConfig: ShippingPluginConfig;
706
+ createShipment(params: CreateShipmentParams, ctx: DispatchContext): Promise<ShipmentResult>;
707
+ getQuotes?(params: ShippingQuoteParams, ctx: DispatchContext): Promise<ShippingQuote[]>;
708
+ /** Pure — a tracking URL never depends on tenant config. */
709
+ getTrackingUrl(trackingNumber: string): string;
710
+ parseWebhook?(body: unknown, headers: Record<string, string>, ctx: DispatchContext): TrackingEvent | null;
711
+ isConfigured?(ctx: DispatchContext): boolean;
712
+ }
713
+
714
+ declare enum SuppliersFeature {
715
+ /** Browse/search the supplier catalog. */
716
+ CatalogSearch = "catalog-search",
717
+ /** Pull a full listing snapshot to seed a native product. */
718
+ CatalogImport = "catalog-import",
719
+ /** Place + track orders against the supplier. */
720
+ Orders = "orders",
721
+ /** Supplier pushes order status to a registered webhook. */
722
+ FulfillmentSync = "fulfillment-sync",
723
+ /**
724
+ * Poll the supplier for stock levels. SoyDrop has no inventory webhook, so
725
+ * stock is reconciled by polling rather than push.
726
+ */
727
+ StockSync = "stock-sync"
728
+ }
729
+ /**
730
+ * ISO-4217 money in minor units — mirrors the store's Money VO
731
+ * (`core/source/contracts/source-dtos.ts`) so the host maps 1:1 without a
732
+ * currency guess. Kept local so the SDK stays dependency-free.
733
+ */
734
+ interface SupplierMoney {
735
+ amountMinor: number;
736
+ currency: string;
737
+ }
738
+ interface SuppliersDefaultConfig {
739
+ global?: Record<string, unknown>;
740
+ countries?: Record<string, Record<string, unknown>>;
741
+ }
742
+ interface SuppliersPluginConfig {
743
+ supportedCountries: string[];
744
+ supportedCapabilities: SuppliersFeature[];
745
+ configSchema: PluginConfigField[];
746
+ defaultConfig?: SuppliersDefaultConfig;
747
+ }
748
+ interface SuppliersStaticConfig {
749
+ code: string;
750
+ displayName: string;
751
+ description: string;
752
+ iconUrl?: string;
753
+ supportedCountries: string[];
754
+ supportedCapabilities: SuppliersFeature[];
755
+ configSchema: PluginConfigField[];
756
+ }
757
+ interface SupplierCatalogQuery {
758
+ text?: string;
759
+ country?: string;
760
+ cursor?: string;
761
+ limit?: number;
762
+ }
763
+ interface SupplierListingVariant {
764
+ externalVariantId: string;
765
+ sku?: string | null;
766
+ title: string;
767
+ wholesalePrice: SupplierMoney;
768
+ options?: Record<string, string>;
769
+ imageUrl?: string | null;
770
+ available?: boolean;
771
+ }
772
+ interface SupplierListingAttribute {
773
+ name: string;
774
+ value: string;
775
+ }
776
+ interface SupplierStock {
777
+ externalId: string;
778
+ quantity: number;
779
+ inStock: boolean;
780
+ }
781
+ interface SupplierListing {
782
+ /** Native id in the supplier catalog (product or catalog-item id). */
783
+ externalId: string;
784
+ title: string;
785
+ description?: string | null;
786
+ sku?: string | null;
787
+ /** ABSOLUTE image URLs. `imageUrl` is the primary; `images` the gallery. */
788
+ imageUrl?: string | null;
789
+ images?: string[];
790
+ /** Wholesale cost the merchant pays the supplier (per unit). */
791
+ wholesalePrice: SupplierMoney;
792
+ /** Supplier's suggested retail, when provided. */
793
+ suggestedPrice?: SupplierMoney | null;
794
+ stock: SupplierStock;
795
+ categories?: string[];
796
+ attributes?: SupplierListingAttribute[];
797
+ hasVariants: boolean;
798
+ variants?: SupplierListingVariant[];
799
+ moq?: number | null;
800
+ /** Untouched supplier payload, for diagnostics. Never trusted for control flow. */
801
+ raw?: unknown;
802
+ }
803
+ interface SupplierListingPage {
804
+ items: SupplierListing[];
805
+ nextCursor?: string | null;
806
+ }
807
+ interface SupplierOrderCustomer {
808
+ name: string;
809
+ lastName?: string | null;
810
+ phoneAreaCode: string;
811
+ phone: string;
812
+ email?: string | null;
813
+ }
814
+ interface SupplierOrderAddressLevel {
815
+ levelKey: string;
816
+ valueId: string;
817
+ }
818
+ interface SupplierShipTo {
819
+ address: string;
820
+ referencePoint?: string | null;
821
+ /**
822
+ * Supplier-native geo ids. Optional: when the host cannot produce the
823
+ * supplier's own ids (the common case — the host only knows its own geo),
824
+ * it leaves these empty and passes the human-readable names below. The
825
+ * plugin resolves names → supplier ids against its own geo endpoints.
826
+ */
827
+ stateId?: string;
828
+ cityId?: string;
829
+ /** ISO 3166-1 alpha-2 of the destination country (e.g. "GT", "SV"). */
830
+ countryCode?: string;
831
+ /** Human-readable admin-area (state/department) name for plugin-side resolution. */
832
+ stateName?: string;
833
+ /** Human-readable locality (city/municipality) name for plugin-side resolution. */
834
+ cityName?: string;
835
+ extraLevels?: SupplierOrderAddressLevel[];
836
+ latitude?: number;
837
+ longitude?: number;
838
+ instructions?: string | null;
839
+ }
840
+ interface SupplierOrderLine {
841
+ /** Supplier catalog/variant id to order. */
842
+ externalListingId: string;
843
+ quantity: number;
844
+ /** Optional price override; omit to use the supplier's catalog price. */
845
+ unitPrice?: SupplierMoney | null;
846
+ }
847
+ interface PlaceSupplierOrderInput {
848
+ /** Host order id — the idempotency key the supplier dedupes on. */
849
+ externalOrderId: string;
850
+ customer: SupplierOrderCustomer;
851
+ shipTo: SupplierShipTo;
852
+ lines: SupplierOrderLine[];
853
+ cod: boolean;
854
+ /** Exact amount to collect on COD; defaults to the line total. */
855
+ codAmount?: SupplierMoney | null;
856
+ /** Optional carrier choice; the supplier picks a default when omitted. */
857
+ courierId?: string | null;
858
+ note?: string | null;
859
+ }
860
+ /**
861
+ * Normalized supplier-order status. `unknown` is a first-class value for
862
+ * supplier states the adapter does not map — the host treats it as a no-op.
863
+ */
864
+ type SupplierOrderStatus = 'pending' | 'accepted' | 'in_transit' | 'shipped' | 'delivered' | 'cancelled' | 'rejected' | 'unknown';
865
+ interface SupplierOrderResult {
866
+ /** Supplier-side order id. */
867
+ externalId: string;
868
+ orderNumber?: string | null;
869
+ /** The host order id echoed back, when the supplier stores it. */
870
+ externalOrderId?: string | null;
871
+ status: SupplierOrderStatus;
872
+ statusLabel?: string | null;
873
+ trackingCode?: string | null;
874
+ trackingUrl?: string | null;
875
+ carrier?: string | null;
876
+ shippingCost?: SupplierMoney | null;
877
+ codAmount?: SupplierMoney | null;
878
+ raw?: unknown;
879
+ }
880
+ /**
881
+ * Normalized, supplier-agnostic fulfillment event produced by
882
+ * {@link ISuppliersPlugin.parseWebhook}. The host matches `externalOrderId`
883
+ * against its stored SupplierOrder to resolve the local order, so the adapter
884
+ * never needs host identifiers.
885
+ */
886
+ interface SupplierFulfillmentEvent {
887
+ externalOrderId: string;
888
+ status: SupplierOrderStatus;
889
+ trackingCode?: string | null;
890
+ carrier?: string | null;
891
+ /** ISO-8601 instant the supplier reported the change, when available. */
892
+ occurredAt?: string;
893
+ statusDescription?: string;
894
+ /** Untouched supplier payload, for diagnostics. */
895
+ raw?: unknown;
896
+ }
897
+ interface ISuppliersPlugin extends IPlugin {
898
+ readonly name: string;
899
+ readonly suppliersConfig: SuppliersPluginConfig;
900
+ searchCatalog(query: SupplierCatalogQuery, ctx: DispatchContext): Promise<SupplierListingPage>;
901
+ getListing(externalId: string, ctx: DispatchContext): Promise<SupplierListing>;
902
+ placeOrder(order: PlaceSupplierOrderInput, ctx: DispatchContext): Promise<SupplierOrderResult>;
903
+ getOrderStatus(externalOrderId: string, ctx: DispatchContext): Promise<SupplierOrderResult>;
904
+ cancelOrder?(externalOrderId: string, ctx: DispatchContext): Promise<SupplierOrderResult>;
905
+ syncStock?(externalIds: string[], ctx: DispatchContext): Promise<SupplierStock[]>;
906
+ parseWebhook?(body: unknown, headers: Record<string, string>, ctx: DispatchContext): SupplierFulfillmentEvent | null;
907
+ isConfigured?(ctx: DispatchContext): boolean;
908
+ }
909
+
910
+ declare enum AnalyticsFeature {
911
+ /** Ecommerce funnel events: view_item, add_to_cart, begin_checkout, purchase. */
912
+ Ecommerce = "ecommerce",
913
+ /** Generic page_view tracking. */
914
+ PageViews = "page-views"
915
+ }
916
+ /**
917
+ * ISO-4217 money in minor units — mirrors the store's Money VO so the host maps
918
+ * 1:1 without a currency guess. Kept local so the SDK stays dependency-free.
919
+ */
920
+ interface AnalyticsMoney {
921
+ amountMinor: number;
922
+ currency: string;
923
+ }
924
+ /** A single line item attached to an ecommerce event. */
925
+ interface AnalyticsItem {
926
+ /** Product id as the store knows it (Meta `content_ids`, GA4 `item_id`). */
927
+ id: string;
928
+ /** Variant id for catalog-level granularity, when the line resolves one. */
929
+ variantId?: string | null;
930
+ name?: string;
931
+ sku?: string | null;
932
+ /** Brand for catalog feeds that key on it. */
933
+ brand?: string | null;
934
+ quantity?: number;
935
+ price?: AnalyticsMoney;
936
+ category?: string | null;
937
+ }
938
+ /**
939
+ * Canonical, provider-agnostic event names the host emits. Closed set: the host
940
+ * only ever maps a domain event to one of these. An adapter that does not care
941
+ * about a given name simply ignores it.
942
+ */
943
+ type AnalyticsEventName = 'page_view' | 'view_item' | 'add_to_cart' | 'remove_from_cart' | 'begin_checkout' | 'purchase';
944
+ /**
945
+ * Where the action physically happened. Maps to Meta `action_source`; other
946
+ * providers ignore it. Defaults to `website` when the host omits it.
947
+ */
948
+ type AnalyticsActionSource = 'website' | 'app' | 'server' | 'phone_call' | 'chat' | 'email' | 'physical_store' | 'other';
949
+ /**
950
+ * Identity + match-quality signals. EVERYTHING here is optional — the host
951
+ * populates whatever it can resolve for the event in scope.
952
+ *
953
+ * @rule The `*Sha256` fields arrive ALREADY hashed by the host (SHA256 hex of
954
+ * the normalized value: lowercased + trimmed; phone in E.164 digits).
955
+ * The plugin NEVER receives raw PII. `externalId`, `clientIpAddress`,
956
+ * `clientUserAgent` and the click ids are the exception — providers
957
+ * require them PLAIN.
958
+ */
959
+ interface AnalyticsUserData {
960
+ emailSha256?: string | null;
961
+ phoneSha256?: string | null;
962
+ firstNameSha256?: string | null;
963
+ lastNameSha256?: string | null;
964
+ citySha256?: string | null;
965
+ stateSha256?: string | null;
966
+ zipSha256?: string | null;
967
+ countrySha256?: string | null;
968
+ genderSha256?: string | null;
969
+ /** Date of birth, YYYYMMDD normalized then SHA256. */
970
+ dobSha256?: string | null;
971
+ /** Store customer/user id — Meta `external_id` (not hashed by spec). */
972
+ externalId?: string | null;
973
+ /** Plain IPv4/IPv6 — never hashed (geo match). */
974
+ clientIpAddress?: string | null;
975
+ /** Plain browser UA — never hashed (device match). */
976
+ clientUserAgent?: string | null;
977
+ /** Meta browser cookie `_fbp`. */
978
+ fbp?: string | null;
979
+ /** Meta click id `_fbc`. */
980
+ fbc?: string | null;
981
+ /** Google Ads click id. */
982
+ gclid?: string | null;
983
+ /** TikTok click id. */
984
+ ttclid?: string | null;
985
+ }
986
+ /** Marketing attribution captured at the session that led to the event. */
987
+ interface AnalyticsUtm {
988
+ source?: string | null;
989
+ medium?: string | null;
990
+ campaign?: string | null;
991
+ term?: string | null;
992
+ content?: string | null;
993
+ }
994
+ /**
995
+ * Consent signals the host resolved for this event. Providers that honor
996
+ * consent (Meta LDU, Google Consent Mode) gate on it; others ignore it.
997
+ */
998
+ interface AnalyticsConsent {
999
+ adStorage?: boolean;
1000
+ adUserData?: boolean;
1001
+ adPersonalization?: boolean;
1002
+ }
1003
+ /**
1004
+ * Normalized analytics event. The host builds this from a DomainEvent and hands
1005
+ * it to every active analytics adapter. The adapter translates it to its own
1006
+ * wire format (GA4 event, Meta event, ...). See the @rule SUPERSET note at the
1007
+ * top of the module: this is the superset of what Meta CAPI needs.
1008
+ */
1009
+ interface AnalyticsEvent {
1010
+ name: AnalyticsEventName;
1011
+ /** ISO-8601 instant the underlying action happened. */
1012
+ occurredAt: string;
1013
+ /**
1014
+ * Stable id for cross-channel deduplication against the browser pixel (Meta
1015
+ * `event_id`). For `purchase` the host sets it to the order id so the pixel
1016
+ * and the server event collapse into one.
1017
+ */
1018
+ eventId?: string | null;
1019
+ /** Where the action happened — Meta `action_source`. Defaults to `website`. */
1020
+ actionSource?: AnalyticsActionSource;
1021
+ /**
1022
+ * Pseudonymous client identity for sessionization (maps to GA4 `client_id`
1023
+ * / Meta `external_id`). Null when the host cannot resolve a stable id.
1024
+ */
1025
+ clientId?: string | null;
1026
+ sessionId?: string | null;
1027
+ /** Hashed identity + client signals + click ids. See {@link AnalyticsUserData}. */
1028
+ user?: AnalyticsUserData;
1029
+ /** Monetary value for value-bearing events (purchase, begin_checkout). */
1030
+ value?: AnalyticsMoney | null;
1031
+ /** Order id for `purchase` — providers dedupe transactions on it. */
1032
+ transactionId?: string | null;
1033
+ items?: AnalyticsItem[];
1034
+ /** Page context for `page_view`. */
1035
+ pageLocation?: string | null;
1036
+ /** Referring URL, when the host knows it. */
1037
+ pageReferrer?: string | null;
1038
+ pageTitle?: string | null;
1039
+ /** Marketing attribution for the session that produced the event. */
1040
+ utm?: AnalyticsUtm;
1041
+ /** Consent signals resolved by the host for this event. */
1042
+ consent?: AnalyticsConsent;
1043
+ /** Extra params forwarded verbatim as custom dimensions. */
1044
+ params?: Record<string, unknown>;
1045
+ }
1046
+ interface AnalyticsPluginConfig {
1047
+ supportedCapabilities: AnalyticsFeature[];
1048
+ configSchema: PluginConfigField[];
1049
+ /** GLOBAL-scope defaults merged under the tenant DB config by the host. */
1050
+ defaultConfig?: Record<string, unknown>;
1051
+ }
1052
+ interface AnalyticsStaticConfig {
1053
+ code: string;
1054
+ displayName: string;
1055
+ description: string;
1056
+ iconUrl?: string;
1057
+ supportedCapabilities: AnalyticsFeature[];
1058
+ configSchema: PluginConfigField[];
1059
+ }
1060
+ interface IAnalyticsPlugin extends IPlugin {
1061
+ readonly name: string;
1062
+ readonly analyticsConfig: AnalyticsPluginConfig;
1063
+ /**
1064
+ * Forward one canonical event to the provider's ingestion API. Stateless: the
1065
+ * tenant's resolved credentials arrive in `ctx.config` on every call.
1066
+ *
1067
+ * @rule BEST-EFFORT — never throw on a transport/provider error. Analytics is
1068
+ * fire-and-forget and MUST NOT break the domain flow that triggered it.
1069
+ */
1070
+ track(event: AnalyticsEvent, ctx: DispatchContext): Promise<void>;
1071
+ /**
1072
+ * Optional readiness probe. Availability = enabled AND isConfigured.
1073
+ * @compat When omitted, the host assumes the adapter is ready.
1074
+ */
1075
+ isConfigured?(ctx: DispatchContext): boolean;
1076
+ }
1077
+
1078
+ declare enum InvoicingFeature {
1079
+ /** Emit a fiscal document against the authority. */
1080
+ Issue = "issue",
1081
+ /** Invalidate / annul a previously issued document. */
1082
+ Void = "void",
1083
+ /** Query the current authority status of an issued document. */
1084
+ StatusQuery = "status-query",
1085
+ /** Retrieve the rendered PDF of an issued document. */
1086
+ Pdf = "pdf"
1087
+ }
1088
+ /**
1089
+ * Generic fiscal document types. The adapter maps each to its national catalog
1090
+ * (e.g. SV MH: Invoice→Factura Consumidor Final "01", CreditInvoice→Comprobante
1091
+ * de Crédito Fiscal "03", ExcludedSubject→Factura Sujeto Excluido "14", ...).
1092
+ */
1093
+ declare enum InvoiceDocumentType {
1094
+ /** Final-consumer invoice. */
1095
+ Invoice = "invoice",
1096
+ /** Tax-credit document for registered taxpayers (SV CCF). */
1097
+ CreditInvoice = "credit-invoice",
1098
+ /** Credit note against a prior document. */
1099
+ CreditNote = "credit-note",
1100
+ /** Debit note against a prior document. */
1101
+ DebitNote = "debit-note",
1102
+ /** Document for suppliers not registered as taxpayers (SV Sujeto Excluido). */
1103
+ ExcludedSubject = "excluded-subject",
1104
+ /** Export invoice. */
1105
+ Export = "export"
1106
+ }
1107
+ /**
1108
+ * Authority status of an issued document. `unknown` is a first-class value for
1109
+ * provider states the adapter does not map — the host treats it as "not yet
1110
+ * resolved" instead of guessing.
1111
+ */
1112
+ declare enum InvoiceStatus {
1113
+ /** Submitted, awaiting the authority's acceptance. */
1114
+ Pending = "pending",
1115
+ /** Accepted and sealed by the authority. */
1116
+ Accepted = "accepted",
1117
+ /** Rejected by the authority (see statusDescription for the reason). */
1118
+ Rejected = "rejected",
1119
+ /** Previously accepted, later invalidated/annulled. */
1120
+ Voided = "voided",
1121
+ Unknown = "unknown"
1122
+ }
1123
+ /**
1124
+ * ISO-4217 money in minor units — mirrors the store's Money VO so the host maps
1125
+ * 1:1 without a currency guess. Kept local so the SDK stays dependency-free.
1126
+ */
1127
+ interface InvoiceMoney {
1128
+ amountMinor: number;
1129
+ currency: string;
1130
+ }
1131
+ /**
1132
+ * A party to the document (issuer or recipient). Fields are generic; the adapter
1133
+ * maps `taxId` to the national identifier it needs (NIT/NRC/DUI in SV, RFC in
1134
+ * MX). Most are optional because a final-consumer invoice needs far less than a
1135
+ * tax-credit document.
1136
+ */
1137
+ interface InvoiceParty {
1138
+ /** Legal / trade name of the party. */
1139
+ name: string;
1140
+ /** National tax identifier (SV NIT, MX RFC, ...). */
1141
+ taxId?: string | null;
1142
+ /** Secondary fiscal registry id where the country splits them (SV NRC). */
1143
+ taxRegistryId?: string | null;
1144
+ /** Personal id when the party is a natural person (SV DUI). */
1145
+ personalId?: string | null;
1146
+ email?: string | null;
1147
+ phone?: string | null;
1148
+ address?: string | null;
1149
+ city?: string | null;
1150
+ state?: string | null;
1151
+ country?: string | null;
1152
+ /** Economic-activity code where the authority requires it. */
1153
+ activityCode?: string | null;
1154
+ activityDescription?: string | null;
1155
+ }
1156
+ /** A tax applied to a line or to the whole document. */
1157
+ interface InvoiceTax {
1158
+ /** Generic code (adapter maps to the national tax code, e.g. SV IVA "20"). */
1159
+ code: string;
1160
+ description?: string;
1161
+ /** Rate as a fraction (0.13 for 13% IVA), when the tax is rate-based. */
1162
+ rate?: number;
1163
+ amount: InvoiceMoney;
1164
+ }
1165
+ /** A single line item of the document. */
1166
+ interface InvoiceLine {
1167
+ /** Store product id, for traceability back to the catalog. */
1168
+ productId?: string | null;
1169
+ variantId?: string | null;
1170
+ sku?: string | null;
1171
+ description: string;
1172
+ quantity: number;
1173
+ /** Unit of measure code where the authority requires it. */
1174
+ unitCode?: string | null;
1175
+ unitPrice: InvoiceMoney;
1176
+ /** Line discount, when any. */
1177
+ discount?: InvoiceMoney;
1178
+ /** Line subtotal (quantity * unitPrice - discount), before document taxes. */
1179
+ subtotal: InvoiceMoney;
1180
+ /** Per-line taxes, when the country computes tax at the line level. */
1181
+ taxes?: InvoiceTax[];
1182
+ }
1183
+ /**
1184
+ * The host builds this from an Order (or a manual admin action) and hands it to
1185
+ * the adapter. Fiscal identifiers are NOT part of the request — the provider
1186
+ * assigns them and returns them on {@link InvoiceResult}.
1187
+ */
1188
+ interface InvoiceRequest {
1189
+ /** Store order id, used by the adapter as the idempotency key. */
1190
+ orderId: string;
1191
+ documentType: InvoiceDocumentType;
1192
+ /** ISO-4217 currency of the whole document. */
1193
+ currency: string;
1194
+ /** The seller — defaults to the store's fiscal identity when omitted by host. */
1195
+ issuer?: InvoiceParty;
1196
+ /** The buyer. */
1197
+ recipient: InvoiceParty;
1198
+ lines: InvoiceLine[];
1199
+ /** Document-level taxes (when the country totals tax at the document level). */
1200
+ taxes?: InvoiceTax[];
1201
+ /** Sum of line subtotals before taxes. */
1202
+ subtotal: InvoiceMoney;
1203
+ /** Total taxes applied. */
1204
+ taxTotal?: InvoiceMoney;
1205
+ /** Grand total the customer pays. */
1206
+ total: InvoiceMoney;
1207
+ /** ISO-8601 instant the sale happened. */
1208
+ issuedAt?: string;
1209
+ /** Free-form notes to print on the document. */
1210
+ notes?: string;
1211
+ /** Provider-specific extras the host cannot model generically. */
1212
+ metadata?: Record<string, unknown>;
1213
+ }
1214
+ /**
1215
+ * Outcome of an issue / void / query. The fiscal identifiers are provider output
1216
+ * — all optional because a Rejected result may carry none of them. `status`
1217
+ * drives the host's control flow; `rawResponse` is for diagnostics only.
1218
+ */
1219
+ interface InvoiceResult {
1220
+ status: InvoiceStatus;
1221
+ /** Authority generation code / UUID (SV "Código de Generación"). */
1222
+ generationCode?: string | null;
1223
+ /** Control number assigned to the document (SV "Número de Control"). */
1224
+ controlNumber?: string | null;
1225
+ /** Authority reception seal confirming validity (SV "Sello de Recepción"). */
1226
+ receptionSeal?: string | null;
1227
+ /** URL to the rendered PDF, when the provider exposes one. */
1228
+ pdfUrl?: string | null;
1229
+ /** URL to the signed JSON artifact, when the provider exposes one. */
1230
+ jsonUrl?: string | null;
1231
+ /** Human-readable authority/provider status, for audit/logging. */
1232
+ statusDescription?: string;
1233
+ /** Untouched provider payload, for diagnostics. Never trusted for control flow. */
1234
+ rawResponse?: Record<string, unknown>;
1235
+ }
1236
+ /** Input to invalidate a previously issued document. */
1237
+ interface VoidInvoiceInput {
1238
+ /** The generation code of the document to invalidate. */
1239
+ generationCode: string;
1240
+ /** Reason for the invalidation, required by most authorities. */
1241
+ reason: string;
1242
+ }
1243
+ interface InvoicingDefaultConfig {
1244
+ global?: Record<string, unknown>;
1245
+ countries?: Record<string, Record<string, unknown>>;
1246
+ }
1247
+ interface InvoicingPluginConfig {
1248
+ supportedCountries: string[];
1249
+ supportedFeatures: InvoicingFeature[];
1250
+ supportedDocumentTypes: InvoiceDocumentType[];
1251
+ configSchema: PluginConfigField[];
1252
+ /** GLOBAL + per-country defaults merged under the tenant DB config by the host. */
1253
+ defaultConfig?: InvoicingDefaultConfig;
1254
+ }
1255
+ interface InvoicingStaticConfig {
1256
+ code: string;
1257
+ displayName: string;
1258
+ description: string;
1259
+ iconUrl?: string;
1260
+ supportedCountries: string[];
1261
+ supportedFeatures: InvoicingFeature[];
1262
+ supportedDocumentTypes: InvoiceDocumentType[];
1263
+ configSchema: PluginConfigField[];
1264
+ }
1265
+ interface IInvoicingPlugin extends IPlugin {
1266
+ readonly name: string;
1267
+ readonly invoicingConfig: InvoicingPluginConfig;
1268
+ /**
1269
+ * Emit a fiscal document against the authority. Stateless: the tenant's
1270
+ * resolved credentials arrive in `ctx.config` on every call. The adapter uses
1271
+ * `req.orderId` as the idempotency key so a retry never double-issues.
1272
+ */
1273
+ issueInvoice(req: InvoiceRequest, ctx: DispatchContext): Promise<InvoiceResult>;
1274
+ /**
1275
+ * Invalidate a previously issued document.
1276
+ * @compat Only plugins advertising the {@link InvoicingFeature.Void} feature.
1277
+ */
1278
+ voidInvoice?(input: VoidInvoiceInput, ctx: DispatchContext): Promise<InvoiceResult>;
1279
+ /**
1280
+ * Query the authority's current status of an issued document.
1281
+ * @compat Only plugins advertising the {@link InvoicingFeature.StatusQuery} feature.
1282
+ */
1283
+ queryStatus?(ref: {
1284
+ generationCode: string;
1285
+ }, ctx: DispatchContext): Promise<InvoiceResult>;
1286
+ /**
1287
+ * Optional readiness probe. Availability = enabled AND isConfigured.
1288
+ * @compat When omitted, the host assumes the adapter is ready.
1289
+ */
1290
+ isConfigured?(ctx: DispatchContext): boolean;
1291
+ }
1292
+
1293
+ /** A named unit of behaviour a plugin exposes to the host registry. */
1294
+ interface Capability<T = unknown> {
1295
+ kind: string;
1296
+ impl: T;
1297
+ }
1298
+ /**
1299
+ * Canonical built-in capability kinds. Open set — a plugin MAY register a
1300
+ * `kind` not listed here; the host routes by string. These constants exist so
1301
+ * first-party contracts share one spelling.
1302
+ */
1303
+ declare const CapabilityKind: {
1304
+ readonly Payment: "payment";
1305
+ readonly Shipping: "shipping";
1306
+ readonly Suppliers: "suppliers";
1307
+ readonly Analytics: "analytics";
1308
+ readonly Invoicing: "invoicing";
1309
+ };
1310
+ type CapabilityKind = (typeof CapabilityKind)[keyof typeof CapabilityKind];
1311
+ declare function capability<T>(kind: string, impl: T): Capability<T>;
1312
+ declare function paymentCapability(impl: IPaymentPlugin): Capability<IPaymentPlugin>;
1313
+ declare function shippingCapability(impl: IShippingPlugin): Capability<IShippingPlugin>;
1314
+ declare function suppliersCapability(impl: ISuppliersPlugin): Capability<ISuppliersPlugin>;
1315
+ declare function analyticsCapability(impl: IAnalyticsPlugin): Capability<IAnalyticsPlugin>;
1316
+ declare function invoicingCapability(impl: IInvoicingPlugin): Capability<IInvoicingPlugin>;
1317
+ /**
1318
+ * A constructed plugin. `capabilities` is the static declaration the host reads
1319
+ * up front; `onActivate` runs when the host mounts the plugin for a tenant and
1320
+ * is where side-effecting registration/subscription happens.
1321
+ */
1322
+ interface Plugin {
1323
+ readonly manifest: PluginManifest;
1324
+ readonly capabilities: Capability[];
1325
+ onActivate?(host: HostApi): void | Promise<void>;
1326
+ onDeactivate?(): void | Promise<void>;
1327
+ }
1328
+ /**
1329
+ * The plugin entry point. The compiled bundle's default export is a
1330
+ * `PluginFactory`; the host calls it with a per-tenant `HostApi`. No NestJS,
1331
+ * no decorators — the host owns dependency injection on its side.
1332
+ */
1333
+ type PluginFactory = (host: HostApi) => Plugin | Promise<Plugin>;
1334
+ /**
1335
+ * Anchors the factory type for plugin authors AND brands the function so the
1336
+ * host loader can tell a `PluginFactory` (new, framework-agnostic) apart from a
1337
+ * legacy NestJS `@Module` default export at runtime.
1338
+ */
1339
+ declare function definePlugin(factory: PluginFactory): PluginFactory;
1340
+ /** True when `value` was produced by {@link definePlugin}. */
1341
+ declare function isPluginFactory(value: unknown): value is PluginFactory;
1342
+
1343
+ interface Logger {
1344
+ debug(message: string, ...meta: unknown[]): void;
1345
+ info(message: string, ...meta: unknown[]): void;
1346
+ warn(message: string, ...meta: unknown[]): void;
1347
+ error(message: string, ...meta: unknown[]): void;
1348
+ }
1349
+ interface HttpRequest {
1350
+ method: string;
1351
+ url: string;
1352
+ headers?: Record<string, string>;
1353
+ body?: unknown;
1354
+ }
1355
+ interface HttpResponse {
1356
+ status: number;
1357
+ headers: Record<string, string>;
1358
+ body: unknown;
1359
+ }
1360
+ /**
1361
+ * Outbound HTTP mediated by the host. The host enforces the plugin's
1362
+ * `http.outbound:<host>` permissions before the request leaves the process —
1363
+ * the plugin cannot reach the network directly.
1364
+ */
1365
+ interface HttpClient {
1366
+ request(req: HttpRequest): Promise<HttpResponse>;
1367
+ }
1368
+ /**
1369
+ * Secret access mediated by the host (never `process.env` directly). Gated by
1370
+ * `secrets.read:<key>` permissions. Returns the resolved per-tenant secret.
1371
+ */
1372
+ interface SecretStore {
1373
+ get(key: string): Promise<string | undefined>;
1374
+ }
1375
+ /** Object storage mediated by the host (presigned broker under the hood). */
1376
+ interface StorageBroker {
1377
+ getUrl(key: string): Promise<string>;
1378
+ put(key: string, body: Uint8Array | string): Promise<void>;
1379
+ }
1380
+ /**
1381
+ * Monotonic sequence numbers owned and PERSISTED by the host. A stateless
1382
+ * plugin cannot keep its own counter (it holds no per-tenant state and the same
1383
+ * object serves every tenant), yet some gateways require a strictly increasing,
1384
+ * unique-per-merchant number (e.g. Serfinsa's `audit_no`). The plugin asks the
1385
+ * host for the next value under a plugin-chosen `scope` string; the host does
1386
+ * the atomic increment and durable storage.
1387
+ *
1388
+ * `scope` namespaces the counter — encode everything that must have its own run
1389
+ * of numbers, e.g. `serfinsa:audit_no:<merchantId>`. Two calls with the same
1390
+ * scope never return the same value; different scopes are independent.
1391
+ */
1392
+ interface SequenceStore {
1393
+ next(scope: string): Promise<number>;
1394
+ }
1395
+ type DomainEventName = string;
1396
+ type DomainEventHandler = (payload: unknown) => void | Promise<void>;
1397
+ /**
1398
+ * The one object a plugin factory receives. It is the plugin's ENTIRE window
1399
+ * into the host — no `@nestjs/*`, no prisma, no `process`. Everything the
1400
+ * plugin can do flows through these ports, which lets the host enforce the
1401
+ * plugin's declared permissions and swap the transport (in-process today,
1402
+ * worker/WASM later) without touching plugin code.
1403
+ */
1404
+ interface HostApi {
1405
+ readonly logger: Logger;
1406
+ readonly http: HttpClient;
1407
+ readonly secrets: SecretStore;
1408
+ readonly storage: StorageBroker;
1409
+ readonly sequence: SequenceStore;
1410
+ /** Subscribe to a host domain event (order.created, product.updated, ...). */
1411
+ on(event: DomainEventName, handler: DomainEventHandler): void;
1412
+ /** Expose a capability to the host's generic registry. */
1413
+ registerCapability<T = unknown>(cap: Capability<T>): void;
1414
+ }
1415
+
1416
+ export { type IAnalyticsPlugin as $, AUTHOR_HANDLE_RE as A, type BannerBlock as B, ConfigScope as C, type ConditionalBlock as D, type ConditionalEquals as E, FieldType as F, type ConditionalWhen as G, type ConfigurablePlugin as H, type CopyFieldBlock as I, type CreateShipmentParams as J, type DispatchContext as K, type DividerBlock as L, type DomainEventHandler as M, type DomainEventName as N, type EmbedInit as O, type PluginConfigField as P, type FieldBlock as Q, type FieldOption as R, type SelectOption as S, FieldVariant as T, GatewayEnvironment as U, type GatewayStaticConfig as V, type GroupBlock as W, type HostApi as X, type HttpClient as Y, type HttpRequest as Z, type HttpResponse as _, type AccordionBlock as a, type StepStatusSource as a$, type IInvoicingPlugin as a0, type IPaymentPlugin as a1, type IPlugin as a2, type IShippingPlugin as a3, type ISuppliersPlugin as a4, type InlineRowBlock as a5, InvoiceDocumentType as a6, type InvoiceLine as a7, type InvoiceMoney as a8, type InvoiceParty as a9, type PluginActionContext as aA, type PluginActionResult as aB, type PluginAuthor as aC, type PluginConfigFieldDependency as aD, type PluginFactory as aE, type PluginManifest as aF, PluginType as aG, type RepeaterBlock as aH, RepeaterSource as aI, type SecretStore as aJ, type SequenceStore as aK, type SetupAction as aL, type SetupLayout as aM, type SetupLayoutValidation as aN, type SetupStep as aO, type ShipmentAddress as aP, type ShipmentParcel as aQ, type ShipmentResult as aR, type ShippingDefaultConfig as aS, ShippingFeature as aT, type ShippingPluginConfig as aU, type ShippingQuote as aV, type ShippingQuoteParams as aW, type ShippingStaticConfig as aX, StepStatusFrom as aY, type StepStatusFromAction as aZ, type StepStatusFromFields as a_, type InvoiceRequest as aa, type InvoiceResult as ab, InvoiceStatus as ac, type InvoiceTax as ad, type InvoicingDefaultConfig as ae, InvoicingFeature as af, type InvoicingPluginConfig as ag, type InvoicingStaticConfig as ah, type KeyValueBlock as ai, type KeyValueRow as aj, LayoutMode as ak, type Logger as al, type MarkdownBlock as am, MessageVariant as an, type PaymentCard as ao, type PaymentCustomer as ap, type PaymentGatewayCode as aq, type PaymentPayload as ar, type PaymentPluginConfig as as, type PaymentRenderMode as at, type PaymentResult as au, type PaymentSessionPayload as av, type PaymentSessionResult as aw, type PaymentVerifyPayload as ax, type PlaceSupplierOrderInput as ay, type Plugin as az, type AccordionSection as b, type StorageBroker as b0, type SupplierCatalogQuery as b1, type SupplierFulfillmentEvent as b2, type SupplierListing as b3, type SupplierListingAttribute as b4, type SupplierListingPage as b5, type SupplierListingVariant as b6, type SupplierMoney as b7, type SupplierOrderAddressLevel as b8, type SupplierOrderCustomer as b9, validatePluginAuthor as bA, validateSetupLayout as bB, type SupplierOrderLine as ba, type SupplierOrderResult as bb, type SupplierOrderStatus as bc, type SupplierShipTo as bd, type SupplierStock as be, type SuppliersDefaultConfig as bf, SuppliersFeature as bg, type SuppliersPluginConfig as bh, type SuppliersStaticConfig as bi, type TabItem as bj, type TabsBlock as bk, type ToggleBlock as bl, type TrackingEvent as bm, TransactionStatus as bn, type VoidInvoiceInput as bo, type WebhookSignatureResult as bp, analyticsCapability as bq, capability as br, crossValidateSetupLayout as bs, definePlugin as bt, invoicingCapability as bu, isConfigurablePlugin as bv, isPluginFactory as bw, paymentCapability as bx, shippingCapability as by, suppliersCapability as bz, type ActionBlock as c, ActionVariant as d, type AnalyticsActionSource as e, type AnalyticsConsent as f, type AnalyticsEvent as g, type AnalyticsEventName as h, AnalyticsFeature as i, type AnalyticsItem as j, type AnalyticsMoney as k, type AnalyticsPluginConfig as l, type AnalyticsStaticConfig as m, type AnalyticsUserData as n, type AnalyticsUtm as o, type AuthorValidation as p, type Block as q, type BlockBase as r, BlockType as s, type CalloutBlock as t, type Capability as u, CapabilityKind as v, type CardBlock as w, type CodConfig as x, type CodFeeConfig as y, CodFeeType as z };