@stackable-labs/sdk-extension-contracts 2.23.0 → 2.25.0

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/index.d.ts CHANGED
@@ -436,14 +436,36 @@ interface IdentityEvent {
436
436
  timestamp: ISOTimestamp;
437
437
  };
438
438
  }
439
- /** Base claims sent to extend:identity handlers before JWT signing */
439
+ /** Base claims sent to identity.extend handlers before JWT signing */
440
440
  interface IdentityBaseClaims {
441
441
  external_id: string;
442
442
  email?: string;
443
443
  name?: string;
444
444
  [key: string]: unknown;
445
445
  }
446
- /** Handler type for useExtendIdentity — use with useCallback for memoized handlers */
446
+ /** Standard JWT claims (host/Lambda always allow these; declaration is redundant). */
447
+ declare const STANDARD_IDENTITY_CLAIM_KEYS: ReadonlySet<string>;
448
+ /** RFC 7519 registered + Zendesk-managed claims — host-side sanitizer always
449
+ * wins on collision, so these must never appear in manifest.identityClaims. */
450
+ declare const RESERVED_IDENTITY_CLAIM_KEYS: ReadonlySet<string>;
451
+ /** Valid identityClaims key format: lowercase identifier, ≤64 chars. */
452
+ declare const IDENTITY_CLAIM_KEY_PATTERN: RegExp;
453
+ /** Maximum number of custom identityClaims entries per extension. */
454
+ declare const IDENTITY_CLAIMS_MAX_KEYS = 20;
455
+ /** Maximum length of any single string value sent through identity.extend / custom_claims. */
456
+ declare const IDENTITY_CLAIM_MAX_STRING_VALUE_LEN = 256;
457
+ /**
458
+ * Handler type for useExtendIdentity. Use with useCallback for memoized handlers.
459
+ *
460
+ * Returned keys are filtered host-side against the extension's
461
+ * `manifest.identityClaims` declaration:
462
+ * - Standard JWT overrides (`external_id`, `email`, `name`) always allowed
463
+ * - Custom keys must be declared in `manifest.identityClaims`
464
+ * - Undeclared keys are dropped with a console.warn
465
+ *
466
+ * Values must be `string | number | boolean` for inclusion in the signed JWT;
467
+ * non-primitive values are dropped by the Lambda sanitizer.
468
+ */
447
469
  type ExtendIdentityHandler = (claims: IdentityBaseClaims) => Record<string, unknown> | Promise<Record<string, unknown>>;
448
470
 
449
471
  /**
@@ -549,6 +571,23 @@ interface ContextData {
549
571
  /** Keys managed exclusively by the host RPC layer — must not appear in surface-level context */
550
572
  declare const RESERVED_CONTEXT_KEYS: readonly ["identity", "messaging", "settings"];
551
573
  type ReservedContextKey = typeof RESERVED_CONTEXT_KEYS[number];
574
+ /**
575
+ * Payload sent from extension → host for `identity.extend`.
576
+ *
577
+ * `patch` is a partial set of claims the extension wants to merge into its
578
+ * cached enrichment slot on the host. Host filters the patch against
579
+ * `manifest.identityClaims` (same rules as the initial-login enrichment path),
580
+ * re-signs the JWT, updates `identityState.user.metadata`, and broadcasts
581
+ * `identity:refresh`.
582
+ */
583
+ interface IdentityExtendPayload {
584
+ patch: Record<string, unknown>;
585
+ }
586
+ /** Response returned to the extension after the host completes the re-sign flow. */
587
+ interface IdentityExtendResponse {
588
+ /** The newly-signed JWT — same token that was pushed to outbound auth (Zendesk). */
589
+ token: string;
590
+ }
552
591
  /** Union of all capability call types */
553
592
  type CapabilityCall = {
554
593
  type: 'context.read';
@@ -565,10 +604,8 @@ type CapabilityCall = {
565
604
  type: 'actions.invoke';
566
605
  payload: ActionInvokePayload;
567
606
  } | {
568
- type: 'extend.identity';
569
- payload: {
570
- claims: IdentityBaseClaims;
571
- };
607
+ type: 'identity.extend';
608
+ payload: IdentityExtendPayload;
572
609
  };
573
610
  type CapabilityType = CapabilityCall['type'];
574
611
 
@@ -694,7 +731,7 @@ interface InstanceOption {
694
731
  * Extensions declare required permissions in their manifest.
695
732
  * Host enforces: capability calls must be a subset of granted permissions.
696
733
  */
697
- declare const PERMISSIONS: readonly ["context:read", "data:query", "data:fetch", "actions:toast", "actions:invoke", "events:identity", "events:messaging", "events:activity", "extend:identity"];
734
+ declare const PERMISSIONS: readonly ["context:read", "data:query", "data:fetch", "actions:toast", "actions:invoke", "identity:extend", "events:identity", "events:messaging", "events:activity"];
698
735
  type Permission = (typeof PERMISSIONS)[number];
699
736
  /**
700
737
  * Maps capability types (dot notation, used in code) to the permission
@@ -706,7 +743,7 @@ declare const CAPABILITY_PERMISSION_MAP: {
706
743
  readonly 'data.fetch': "data:fetch";
707
744
  readonly 'actions.toast': "actions:toast";
708
745
  readonly 'actions.invoke': "actions:invoke";
709
- readonly 'extend.identity': "extend:identity";
746
+ readonly 'identity.extend': "identity:extend";
710
747
  };
711
748
  /** Capability key type — dot-notation API names (e.g., 'data.query', 'actions.toast') */
712
749
  type Capability = keyof typeof CAPABILITY_PERMISSION_MAP;
@@ -751,6 +788,24 @@ interface ExtensionManifest {
751
788
  permissions: Permission[];
752
789
  /** Specific events this extension listens for (e.g. ["identity:login", "messaging:postback:Buy Now"]) */
753
790
  events?: EventType[];
791
+ /**
792
+ * Specific custom identity claim keys this extension contributes via
793
+ * `useExtendIdentity`. Required (non-empty) when `permissions` includes
794
+ * `identity:extend` and the handler returns custom keys.
795
+ *
796
+ * Standard JWT claims (`external_id`, `email`, `name`) are exempt — they're
797
+ * always allowed as overrides without explicit declaration.
798
+ *
799
+ * Constraints (enforced at runtime by the host; install-time bundle scan
800
+ * adds the upload-side gate):
801
+ * - Keys must match /^[a-z_][a-z0-9_]{0,63}$/ (snake_case, ≤64 chars)
802
+ * - Reserved JWT/Zendesk claim names rejected
803
+ * - Max 20 declared keys per extension
804
+ *
805
+ * Returned-but-undeclared keys are dropped at runtime with a console.warn.
806
+ * Example: ["loyalty_tier", "verified", "preferred_pronoun"]
807
+ */
808
+ identityClaims?: string[];
754
809
  /**
755
810
  * Hostnames this extension is permitted to reach via data.fetch.
756
811
  * Prefer exact hostnames. Use `*.<suffix>` wildcards only if you need any
@@ -1111,4 +1166,4 @@ interface Order {
1111
1166
  declare const TEMPLATE_FLAVORS: readonly ["minimal", "starter", "kitchen-sink"];
1112
1167
  type TemplateFlavor = typeof TEMPLATE_FLAVORS[number];
1113
1168
 
1114
- export { ACTIVITY_EVENT, ALLOWED_ICONS, type ActionInvokePayload, type ActionPayloadMap, type ActivityEvent, type ActivityEventHandler, type ActivityEventType, type Address, type AllowedIconName, type ApiError, type ApiRequest, type ApiResponse, CAPABILITY_PERMISSION_MAP, type Capability, type CapabilityCall, type CapabilityRequest, type CapabilityResponse, type CapabilityType, type ContextData, type ConversationField, type ConversationTags, type Customer, type DataField, type DataFieldType, EVENT_HOOK_PERMISSION_MAP, type EncryptedPayload, type EventDomain, type EventHook, type EventType, type ExtendIdentityHandler, type ExtendIdentityRequest, type ExtendIdentityResponse, type ExtensionGroup, type ExtensionManifest, type ExtensionRegistryEntry, type FetchRequest, type FetchRequestInit, type FetchResponse, type HostToSandboxMessage, IDENTITY_EVENT, INVOKE_ACTION, type IdentityBaseClaims, type IdentityEvent, type IdentityEventType, type IdentityState, type InstanceConfig, type InstanceOption, type InvokeAction, type MarketplaceExtension, type MessagingContext, type MessagingEvent, type MessagingEventHandler, type MessagingEventType, type MessagingPostbackEvent, type MessengerCommand, type NamedEntity, type NewConversationPayload, type Order, type OrderAction, type OrderItem, type OrderStatus, type OrderStatuses, PERMISSIONS, type Permission, type Price, RESERVED_CONTEXT_KEYS, type ReservedContextKey, SURFACE_TARGET, type SandboxToHostMessage, type Shipment, type SurfaceContext, type SurfaceLifecycleMessage, type SurfaceTarget, TEMPLATE_FLAVORS, type Target, type TemplateFlavor, type Theme, type ToastPayload, type UITag, type UITagCategory, UI_TAGS, UI_TAG_ATTRIBUTES, UI_TAG_ATTRIBUTE_VALUES, UI_TAG_CATEGORIES, UI_TAG_CHILDREN, UI_TAG_CHILD_ONLY, UI_TAG_COMPOUND, UI_TAG_DEFINITIONS, UI_TAG_SELF_CLOSING, type UserIdentity, type WellKnownActivityEvent, type WidgetAction, tagToComponentName };
1169
+ export { ACTIVITY_EVENT, ALLOWED_ICONS, type ActionInvokePayload, type ActionPayloadMap, type ActivityEvent, type ActivityEventHandler, type ActivityEventType, type Address, type AllowedIconName, type ApiError, type ApiRequest, type ApiResponse, CAPABILITY_PERMISSION_MAP, type Capability, type CapabilityCall, type CapabilityRequest, type CapabilityResponse, type CapabilityType, type ContextData, type ConversationField, type ConversationTags, type Customer, type DataField, type DataFieldType, EVENT_HOOK_PERMISSION_MAP, type EncryptedPayload, type EventDomain, type EventHook, type EventType, type ExtendIdentityHandler, type ExtendIdentityRequest, type ExtendIdentityResponse, type ExtensionGroup, type ExtensionManifest, type ExtensionRegistryEntry, type FetchRequest, type FetchRequestInit, type FetchResponse, type HostToSandboxMessage, IDENTITY_CLAIMS_MAX_KEYS, IDENTITY_CLAIM_KEY_PATTERN, IDENTITY_CLAIM_MAX_STRING_VALUE_LEN, IDENTITY_EVENT, INVOKE_ACTION, type IdentityBaseClaims, type IdentityEvent, type IdentityEventType, type IdentityExtendPayload, type IdentityExtendResponse, type IdentityState, type InstanceConfig, type InstanceOption, type InvokeAction, type MarketplaceExtension, type MessagingContext, type MessagingEvent, type MessagingEventHandler, type MessagingEventType, type MessagingPostbackEvent, type MessengerCommand, type NamedEntity, type NewConversationPayload, type Order, type OrderAction, type OrderItem, type OrderStatus, type OrderStatuses, PERMISSIONS, type Permission, type Price, RESERVED_CONTEXT_KEYS, RESERVED_IDENTITY_CLAIM_KEYS, type ReservedContextKey, STANDARD_IDENTITY_CLAIM_KEYS, SURFACE_TARGET, type SandboxToHostMessage, type Shipment, type SurfaceContext, type SurfaceLifecycleMessage, type SurfaceTarget, TEMPLATE_FLAVORS, type Target, type TemplateFlavor, type Theme, type ToastPayload, type UITag, type UITagCategory, UI_TAGS, UI_TAG_ATTRIBUTES, UI_TAG_ATTRIBUTE_VALUES, UI_TAG_CATEGORIES, UI_TAG_CHILDREN, UI_TAG_CHILD_ONLY, UI_TAG_COMPOUND, UI_TAG_DEFINITIONS, UI_TAG_SELF_CLOSING, type UserIdentity, type WellKnownActivityEvent, type WidgetAction, tagToComponentName };
package/dist/index.js CHANGED
@@ -405,6 +405,27 @@ var IDENTITY_EVENT = {
405
405
  REFRESH: "refresh",
406
406
  EXPIRED: "expired"
407
407
  };
408
+ var STANDARD_IDENTITY_CLAIM_KEYS = /* @__PURE__ */ new Set(["external_id", "email", "name"]);
409
+ var RESERVED_IDENTITY_CLAIM_KEYS = /* @__PURE__ */ new Set([
410
+ // RFC 7519 registered claims
411
+ "iss",
412
+ "sub",
413
+ "aud",
414
+ "exp",
415
+ "nbf",
416
+ "iat",
417
+ "jti",
418
+ // Zendesk Messaging standard claims (Lambda sets these unconditionally)
419
+ "scope",
420
+ "external_id",
421
+ "email",
422
+ "email_verified",
423
+ "name",
424
+ "user_fields"
425
+ ]);
426
+ var IDENTITY_CLAIM_KEY_PATTERN = /^[a-z_][a-z0-9_]{0,63}$/;
427
+ var IDENTITY_CLAIMS_MAX_KEYS = 20;
428
+ var IDENTITY_CLAIM_MAX_STRING_VALUE_LEN = 256;
408
429
 
409
430
  // src/activity.ts
410
431
  var ACTIVITY_EVENT = {
@@ -424,10 +445,10 @@ var PERMISSIONS = [
424
445
  "data:fetch",
425
446
  "actions:toast",
426
447
  "actions:invoke",
448
+ "identity:extend",
427
449
  "events:identity",
428
450
  "events:messaging",
429
- "events:activity",
430
- "extend:identity"
451
+ "events:activity"
431
452
  ];
432
453
  var CAPABILITY_PERMISSION_MAP = {
433
454
  "context.read": "context:read",
@@ -435,7 +456,7 @@ var CAPABILITY_PERMISSION_MAP = {
435
456
  "data.fetch": "data:fetch",
436
457
  "actions.toast": "actions:toast",
437
458
  "actions.invoke": "actions:invoke",
438
- "extend.identity": "extend:identity"
459
+ "identity.extend": "identity:extend"
439
460
  };
440
461
  var EVENT_HOOK_PERMISSION_MAP = {
441
462
  useIdentityEvent: "events:identity",
@@ -454,4 +475,4 @@ var SURFACE_TARGET = {
454
475
  // src/scaffold.ts
455
476
  var TEMPLATE_FLAVORS = ["minimal", "starter", "kitchen-sink"];
456
477
 
457
- export { ACTIVITY_EVENT, ALLOWED_ICONS, CAPABILITY_PERMISSION_MAP, EVENT_HOOK_PERMISSION_MAP, EXTENSION_CATEGORY, EXTENSION_VISIBILITY, IDENTITY_EVENT, INVOKE_ACTION, PERMISSIONS, RESERVED_CONTEXT_KEYS, SURFACE_TARGET, TEMPLATE_FLAVORS, UI_TAGS, UI_TAG_ATTRIBUTES, UI_TAG_ATTRIBUTE_VALUES, UI_TAG_CATEGORIES, UI_TAG_CHILDREN, UI_TAG_CHILD_ONLY, UI_TAG_COMPOUND, UI_TAG_DEFINITIONS, UI_TAG_SELF_CLOSING, asClerkOrgId, asClerkUserId, asISOTimestamp, tagToComponentName };
478
+ export { ACTIVITY_EVENT, ALLOWED_ICONS, CAPABILITY_PERMISSION_MAP, EVENT_HOOK_PERMISSION_MAP, EXTENSION_CATEGORY, EXTENSION_VISIBILITY, IDENTITY_CLAIMS_MAX_KEYS, IDENTITY_CLAIM_KEY_PATTERN, IDENTITY_CLAIM_MAX_STRING_VALUE_LEN, IDENTITY_EVENT, INVOKE_ACTION, PERMISSIONS, RESERVED_CONTEXT_KEYS, RESERVED_IDENTITY_CLAIM_KEYS, STANDARD_IDENTITY_CLAIM_KEYS, SURFACE_TARGET, TEMPLATE_FLAVORS, UI_TAGS, UI_TAG_ATTRIBUTES, UI_TAG_ATTRIBUTE_VALUES, UI_TAG_CATEGORIES, UI_TAG_CHILDREN, UI_TAG_CHILD_ONLY, UI_TAG_COMPOUND, UI_TAG_DEFINITIONS, UI_TAG_SELF_CLOSING, asClerkOrgId, asClerkUserId, asISOTimestamp, tagToComponentName };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackable-labs/sdk-extension-contracts",
3
- "version": "2.23.0",
3
+ "version": "2.25.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "private": false,