Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/pos-validation-target-grammar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/ui-extensions': minor
---

Add types for POS intercept `ValidationAdd.target` values (`$.cart`, `$.cart.lineItems['<uuid>']`, `$.payment`), narrowed per intercepted event.
94 changes: 85 additions & 9 deletions packages/ui-extensions/src/surfaces/point-of-sale/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,28 +113,88 @@ export interface PaymentValidationsEvent extends Event {
readonly amount: MoneyV2;
}

/**
* Targets the whole cart rather than a specific line item.
*
* @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;
Comment thread
henryStelle marked this conversation as resolved.

/**
* Targets the payment attempt being intercepted.
*
* @private
*/
export type PaymentTarget = '$.payment';

/** @private */
export type PaymentValidationTarget = PaymentTarget;

/**
* Where a validation applies, as an enumerated token. Targets are matched
* as exact strings, never evaluated as JSON paths.
*
* @private
*/
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 Event> =
TEvent['type'] extends keyof ValidationTargetMap
? ValidationTargetMap[TEvent['type']]
: ValidationTarget;

/** @private */
export type ShopifyInterceptor<TEvent extends Event> = (
event: TEvent,
) => InterceptResult;
) => InterceptResult<ValidationTargetFor<TEvent>>;

/**
* The result an interceptor returns. An empty `operations` list allows the
* workflow; an `ERROR` validation blocks it.
*
* @private
*/
export interface InterceptResult {
operations: Operation[];
export interface InterceptResult<
TTarget extends ValidationTarget = ValidationTarget,
> {
operations: Operation<TTarget>[];
}

/**
* A single host operation produced by an interceptor.
*
* @private
*/
export interface Operation {
validationAdd?: ValidationAdd;
export interface Operation<
TTarget extends ValidationTarget = ValidationTarget,
> {
validationAdd?: ValidationAdd<TTarget>;
}

/** @private */
Expand All @@ -145,15 +205,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. 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.
*/
target?: TTarget;
Comment thread
vctrchu marked this conversation as resolved.
}

export type {
Expand Down
Loading