From d3e882c29c177307ee4997a4ba573efc984d5f3d Mon Sep 17 00:00:00 2001 From: Victor Chu Date: Mon, 17 Aug 2026 16:01:40 -0700 Subject: [PATCH 1/5] Add types for POS intercept ValidationAdd target values Assisted-By: devx/60256637-da58-4f80-9e3a-977e038d8a71 --- .changeset/pos-validation-target-grammar.md | 5 ++ .../src/surfaces/point-of-sale/events.ts | 88 +++++++++++++++++-- 2 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 .changeset/pos-validation-target-grammar.md diff --git a/.changeset/pos-validation-target-grammar.md b/.changeset/pos-validation-target-grammar.md new file mode 100644 index 0000000000..6c1343a976 --- /dev/null +++ b/.changeset/pos-validation-target-grammar.md @@ -0,0 +1,5 @@ +--- +'@shopify/ui-extensions': minor +--- + +Add types for POS intercept `ValidationAdd.target` values (`$.cart`, `$.cart.lineItems['']`, `$.payment`), narrowed per intercepted event. diff --git a/packages/ui-extensions/src/surfaces/point-of-sale/events.ts b/packages/ui-extensions/src/surfaces/point-of-sale/events.ts index 35daaebb83..9d9c91238f 100644 --- a/packages/ui-extensions/src/surfaces/point-of-sale/events.ts +++ b/packages/ui-extensions/src/surfaces/point-of-sale/events.ts @@ -110,10 +110,60 @@ export interface PaymentValidationsEvent extends Event { readonly amount: MoneyV2; } +/** + * Targets the whole cart. Validations with this target render at the cart + * scope (for example the cart banner). + * + * @private + */ +export type CartTarget = '$.cart'; + +/** + * Targets one cart line item by its `uuid` from this event's `cart` snapshot, + * for example `$.cart.lineItems['adfd6b06-4a24-4f5f-9f4b-ea21f4432dd4']`. + * + * @private + */ +export type CartLineItemTarget = `$.cart.lineItems['${string}']`; + +/** @private */ +export type CartValidationTarget = CartTarget | CartLineItemTarget; + +/** + * Targets the payment attempt being intercepted. + * + * @private + */ +export type PaymentTarget = '$.payment'; + +/** @private */ +export type PaymentValidationTarget = PaymentTarget; + +/** + * Where a validation applies, as an enumerated token following the + * [Functions validation target model](https://shopify.dev/docs/api/functions/latest/cart-and-checkout-validation#supported-checkout-field-targets). + * Targets are matched as exact strings, never evaluated as JSON paths. + * + * @private + */ +export type ValidationTarget = CartValidationTarget | PaymentValidationTarget; + +/** + * The validation targets valid for a given intercepted event. + * + * @private + */ +export type ValidationTargetFor = + TEvent extends CartValidationsEvent + ? CartValidationTarget + : TEvent extends PaymentValidationsEvent + ? PaymentValidationTarget + : ValidationTarget; + /** @private */ export type ShopifyInterceptor = ( event: TEvent, -) => InterceptResult; +) => InterceptResult>; /** * The result an interceptor returns. An empty `operations` list allows the @@ -121,8 +171,10 @@ export type ShopifyInterceptor = ( * * @private */ -export interface InterceptResult { - operations: Operation[]; +export interface InterceptResult< + TTarget extends ValidationTarget = ValidationTarget, +> { + operations: Operation[]; } /** @@ -130,8 +182,10 @@ export interface InterceptResult { * * @private */ -export interface Operation { - validationAdd?: ValidationAdd; +export interface Operation< + TTarget extends ValidationTarget = ValidationTarget, +> { + validationAdd?: ValidationAdd; } /** @private */ @@ -142,15 +196,31 @@ export type ValidationLevel = 'WARNING' | 'ERROR'; * * @private */ -export interface ValidationAdd { +export interface ValidationAdd< + TTarget extends ValidationTarget = ValidationTarget, +> { /** `ERROR` blocks the workflow. `WARNING` does not. */ level: ValidationLevel; - /** Stable identifier for this validation. */ + /** + * Stable identifier for this validation. Handles are namespaced per + * extension and may repeat across targets: the same handle on two line + * items is two validations. + */ handle: string; - /** JSON-path locator for where the validation applies. */ - target?: string; + /** + * Locates the data the validation applies to; the host decides where it + * renders, falling back to the event's root scope. Defaults to the root + * scope (`$.cart` / `$.payment`). + * + * Line item uuids are only valid within the event that delivered them: + * echo `lineItems[n].uuid` from this event's cart snapshot, don't cache + * uuids across events. Bundle components are not addressable; target their + * parent line. Unrecognized targets degrade to the root scope — the + * validation still applies, rendered less specifically. + */ + target?: TTarget; } export type { From 42a934fbde37177c8d0b34819a6d29b2969456e1 Mon Sep 17 00:00:00 2001 From: Victor Chu Date: Mon, 17 Aug 2026 16:47:38 -0700 Subject: [PATCH 2/5] refactor: address review feedback on validation target types Assisted-By: devx/b501e5b3-28c0-4a4d-a1e9-b05fd7759da3 --- .../src/surfaces/point-of-sale/events.ts | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/ui-extensions/src/surfaces/point-of-sale/events.ts b/packages/ui-extensions/src/surfaces/point-of-sale/events.ts index 9d9c91238f..f687ace14e 100644 --- a/packages/ui-extensions/src/surfaces/point-of-sale/events.ts +++ b/packages/ui-extensions/src/surfaces/point-of-sale/events.ts @@ -111,8 +111,8 @@ export interface PaymentValidationsEvent extends Event { } /** - * Targets the whole cart. Validations with this target render at the cart - * scope (for example the cart banner). + * Targets the whole cart. Validations with this target apply at the cart + * scope rather than to a specific line item. * * @private */ @@ -148,16 +148,24 @@ export type PaymentValidationTarget = PaymentTarget; */ export type ValidationTarget = CartValidationTarget | PaymentValidationTarget; +/** + * Maps POS interceptable workflow names to their valid validation targets. + * + * @private + */ +interface ValidationTargetMap { + [POS_INTERCEPT_NAMES.CART_VALIDATIONS]: CartValidationTarget; + [POS_INTERCEPT_NAMES.PAYMENT_VALIDATIONS]: PaymentValidationTarget; +} + /** * The validation targets valid for a given intercepted event. * * @private */ export type ValidationTargetFor = - TEvent extends CartValidationsEvent - ? CartValidationTarget - : TEvent extends PaymentValidationsEvent - ? PaymentValidationTarget + TEvent['type'] extends keyof ValidationTargetMap + ? ValidationTargetMap[TEvent['type']] : ValidationTarget; /** @private */ From 0859501054583f1a2c0e3ef9da43b7c7de64a40c Mon Sep 17 00:00:00 2001 From: Victor Chu Date: Tue, 18 Aug 2026 07:34:59 -0700 Subject: [PATCH 3/5] docs: replace 'echo' with 'use' in ValidationAdd target comment Assisted-By: devx/dd2a89f5-3570-4e4c-8f23-528ac5964ead --- packages/ui-extensions/src/surfaces/point-of-sale/events.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui-extensions/src/surfaces/point-of-sale/events.ts b/packages/ui-extensions/src/surfaces/point-of-sale/events.ts index f687ace14e..85bb2feaf1 100644 --- a/packages/ui-extensions/src/surfaces/point-of-sale/events.ts +++ b/packages/ui-extensions/src/surfaces/point-of-sale/events.ts @@ -223,7 +223,7 @@ export interface ValidationAdd< * scope (`$.cart` / `$.payment`). * * Line item uuids are only valid within the event that delivered them: - * echo `lineItems[n].uuid` from this event's cart snapshot, don't cache + * use `lineItems[n].uuid` from this event's cart snapshot, don't cache * uuids across events. Bundle components are not addressable; target their * parent line. Unrecognized targets degrade to the root scope — the * validation still applies, rendered less specifically. From 165a26cda87784c8c7b0a1bfd441c5f6c9bb7420 Mon Sep 17 00:00:00 2001 From: Victor Chu Date: Tue, 18 Aug 2026 07:40:48 -0700 Subject: [PATCH 4/5] docs: deduplicate root-scope fallback and tighten CartTarget comment Assisted-By: devx/dd2a89f5-3570-4e4c-8f23-528ac5964ead --- .../src/surfaces/point-of-sale/events.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/ui-extensions/src/surfaces/point-of-sale/events.ts b/packages/ui-extensions/src/surfaces/point-of-sale/events.ts index 85bb2feaf1..362a7c0457 100644 --- a/packages/ui-extensions/src/surfaces/point-of-sale/events.ts +++ b/packages/ui-extensions/src/surfaces/point-of-sale/events.ts @@ -111,8 +111,7 @@ export interface PaymentValidationsEvent extends Event { } /** - * Targets the whole cart. Validations with this target apply at the cart - * scope rather than to a specific line item. + * Targets the whole cart rather than a specific line item. * * @private */ @@ -219,14 +218,14 @@ export interface ValidationAdd< /** * Locates the data the validation applies to; the host decides where it - * renders, falling back to the event's root scope. Defaults to the root - * scope (`$.cart` / `$.payment`). + * renders. Omitted or unrecognized targets fall back to the event's root + * scope (`$.cart` / `$.payment`) — the validation still applies, rendered + * less specifically. * * Line item uuids are only valid within the event that delivered them: * use `lineItems[n].uuid` from this event's cart snapshot, don't cache - * uuids across events. Bundle components are not addressable; target their - * parent line. Unrecognized targets degrade to the root scope — the - * validation still applies, rendered less specifically. + * uuids across events. Bundle components are not addressable; target + * their parent line. */ target?: TTarget; } From 740a3c3a8a8d76566f3fd62fedad7fef66e371cb Mon Sep 17 00:00:00 2001 From: Victor Chu Date: Tue, 18 Aug 2026 09:56:04 -0700 Subject: [PATCH 5/5] docs: drop Functions docs link from ValidationTarget comment Assisted-By: devx/dd2a89f5-3570-4e4c-8f23-528ac5964ead --- packages/ui-extensions/src/surfaces/point-of-sale/events.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/ui-extensions/src/surfaces/point-of-sale/events.ts b/packages/ui-extensions/src/surfaces/point-of-sale/events.ts index 362a7c0457..805ceb8fa2 100644 --- a/packages/ui-extensions/src/surfaces/point-of-sale/events.ts +++ b/packages/ui-extensions/src/surfaces/point-of-sale/events.ts @@ -139,9 +139,8 @@ export type PaymentTarget = '$.payment'; export type PaymentValidationTarget = PaymentTarget; /** - * Where a validation applies, as an enumerated token following the - * [Functions validation target model](https://shopify.dev/docs/api/functions/latest/cart-and-checkout-validation#supported-checkout-field-targets). - * Targets are matched as exact strings, never evaluated as JSON paths. + * Where a validation applies, as an enumerated token. Targets are matched + * as exact strings, never evaluated as JSON paths. * * @private */