@feelflow/ffid-sdk 5.27.0 → 5.29.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.
@@ -1,5 +1,5 @@
1
- import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo } from '../ffid-client-BqnzUmG7.js';
2
- export { f as FFIDAddMemberParams, g as FFIDAddMemberRequest, h as FFIDAddMemberResponse, i as FFIDAssignableMemberRole, j as FFIDCacheConfig, k as FFIDClient, l as FFIDConfig, m as FFIDListMembersResponse, n as FFIDMemberRole, o as FFIDOrganization, p as FFIDOrganizationMember, q as FFIDOtpSendResponse, r as FFIDOtpVerifyResponse, s as FFIDPasswordResetConfirmResponse, t as FFIDPasswordResetResponse, u as FFIDPasswordResetVerifyResponse, v as FFIDProfileCallOptions, w as FFIDProvisionMemberPlan, x as FFIDProvisionMemberPlanStatus, y as FFIDProvisionMemberResult, z as FFIDProvisionMemberStatus, A as FFIDProvisionOrganizationDryRun, B as FFIDProvisionOrganizationMemberInput, C as FFIDProvisionOrganizationOutcome, D as FFIDProvisionOrganizationParams, E as FFIDProvisionOrganizationResponse, G as FFIDProvisionUserDryRun, H as FFIDProvisionUserOutcome, I as FFIDProvisionUserParams, J as FFIDProvisionUserProfileInput, K as FFIDProvisionUserResponse, L as FFIDProvisionedOrganization, M as FFIDProvisionedUser, N as FFIDRemoveMemberResponse, O as FFIDResetSessionResponse, P as FFIDSubscription, Q as FFIDUpdateMemberRoleResponse, R as FFIDUpdateUserProfileRequest, S as FFIDUser, T as FFIDUserProfile, U as TokenData, V as TokenStore, W as createFFIDClient, X as createTokenStore } from '../ffid-client-BqnzUmG7.js';
1
+ import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo, f as FFIDCatalogEnvironment, g as FFIDBillingInterval, h as FFIDTaxBehavior, i as FFIDPublishedCatalog } from '../ffid-client-DCS-MoxK.js';
2
+ export { j as FFIDAddMemberParams, k as FFIDAddMemberRequest, l as FFIDAddMemberResponse, m as FFIDAssignableMemberRole, n as FFIDCacheConfig, o as FFIDCatalogPublication, p as FFIDCatalogPublicationStatus, q as FFIDClient, r as FFIDConfig, s as FFIDListMembersResponse, t as FFIDMemberRole, u as FFIDOrganization, v as FFIDOrganizationMember, w as FFIDOtpSendResponse, x as FFIDOtpVerifyResponse, y as FFIDPasswordResetConfirmResponse, z as FFIDPasswordResetResponse, A as FFIDPasswordResetVerifyResponse, B as FFIDProfileCallOptions, C as FFIDProvisionMemberPlan, D as FFIDProvisionMemberPlanStatus, E as FFIDProvisionMemberResult, G as FFIDProvisionMemberStatus, H as FFIDProvisionOrganizationDryRun, I as FFIDProvisionOrganizationMemberInput, J as FFIDProvisionOrganizationOutcome, K as FFIDProvisionOrganizationParams, L as FFIDProvisionOrganizationResponse, M as FFIDProvisionUserDryRun, N as FFIDProvisionUserOutcome, O as FFIDProvisionUserParams, P as FFIDProvisionUserProfileInput, Q as FFIDProvisionUserResponse, R as FFIDProvisionedOrganization, S as FFIDProvisionedUser, T as FFIDPublishedPlan, U as FFIDRemoveMemberResponse, V as FFIDResetSessionResponse, W as FFIDSubscription, X as FFIDUpdateMemberRoleResponse, Y as FFIDUpdateUserProfileRequest, Z as FFIDUser, _ as FFIDUserProfile, $ as FFID_CATALOG_ENVIRONMENTS, a0 as FFID_CATALOG_PUBLICATION_STATUSES, a1 as TokenData, a2 as TokenStore, a3 as createFFIDClient, a4 as createTokenStore } from '../ffid-client-DCS-MoxK.js';
3
3
  export { D as DEFAULT_API_BASE_URL, a as DEFAULT_OAUTH_SCOPES } from '../constants-D61jqRIO.js';
4
4
  import { z } from 'zod';
5
5
  import '../types-Cjb9J0rL.js';
@@ -389,4 +389,181 @@ declare const sessionCookies: {
389
389
  */
390
390
  declare function generateServerState(): string;
391
391
 
392
- export { ALL_DENIED_EXCEPT_NECESSARY, type BuildAuthorizeUrlOptions, type BuildAuthorizeUrlResult, CONSENT_COOKIE_NAME, COOKIE_VERSION, type CookieOptions, FFIDCacheAdapter, type FFIDConsentCategories, type FFIDConsentCategoryCode, type FFIDCookieStoreLike, type FFIDErrorCode, FFIDOAuthUserInfo, FFIDVerifyAccessTokenOptions, FFID_ERROR_CODES, FFID_SESSION_COOKIE_NAME, type HandleCallbackInput, type KVNamespaceLike, type ServerAuthClient, type ServerAuthConfig, type ServerSessionTokens, type ServerTokenResult, createKVCacheAdapter, createMemoryCacheAdapter, createServerAuthClient, createVerifyAccessToken, decodeConsentCookie, encodeConsentCookie, generateServerState, getFFIDConsentFromCookieHeader, getFFIDConsentFromCookies, sessionCookies };
392
+ /**
393
+ * Catalog approval hash helpers (#4342 / ASDD-PRICING-002 SP4A)
394
+ *
395
+ * Shared canonicalization + SHA-256 helpers that FIX the cross-repo hash
396
+ * contract between FFID (publisher) and consumers (Praxis). Both sides compute
397
+ * hashes with these helpers and refuse new sales on any mismatch, so the
398
+ * canonical form below is a compatibility contract:
399
+ *
400
+ * 1. Objects serialize with keys sorted by UTF-16 code unit order,
401
+ * recursively. Arrays keep their element order.
402
+ * 2. Serialization is `JSON.stringify`-compatible per token (string
403
+ * escaping, number formatting) with NO whitespace.
404
+ * 3. `undefined`, non-finite numbers (NaN/±Infinity), functions, symbols,
405
+ * bigints and non-plain objects (Date, Map, class instances) are
406
+ * REJECTED with FFIDCatalogHashError — never silently dropped, because a
407
+ * dropped key would let two semantically different payloads collide.
408
+ * Only own enumerable string-keyed properties participate; symbol-keyed
409
+ * properties are ignored (same as `JSON.stringify`).
410
+ * 4. The hash is lowercase SHA-256 hex over the UTF-8 bytes of the
411
+ * canonical string (no domain-separation prefix).
412
+ *
413
+ * Changing ANY of these rules is a breaking change to published catalog
414
+ * verification and requires re-publishing catalogs — the fixed test vectors
415
+ * in `__tests__/catalog-hash.test.ts` exist to make such a change loud.
416
+ */
417
+
418
+ /** Lowercase SHA-256 hex format shared by all catalog approval hashes */
419
+ declare const CATALOG_HASH_PATTERN: RegExp;
420
+ /**
421
+ * Thrown when a value cannot be canonicalized (or an embedded hash is
422
+ * malformed). Always indicates a programming error on the hashing side —
423
+ * never caused by end-user input.
424
+ */
425
+ declare class FFIDCatalogHashError extends Error {
426
+ constructor(message: string);
427
+ }
428
+ /** JSON value accepted by the canonicalizer */
429
+ type FFIDCanonicalJsonValue = string | number | boolean | null | FFIDCanonicalJsonValue[] | {
430
+ [key: string]: FFIDCanonicalJsonValue;
431
+ };
432
+ /**
433
+ * Serializes a JSON-plain value into its canonical string form (see module
434
+ * doc for the exact rules). Exposed so both sides can debug hash mismatches
435
+ * by diffing canonical strings instead of opaque digests.
436
+ */
437
+ declare function canonicalizeCatalogValue(value: FFIDCanonicalJsonValue): string;
438
+ /**
439
+ * Lowercase SHA-256 hex of the UTF-8 bytes of `text`.
440
+ *
441
+ * Requires Web Crypto (`globalThis.crypto.subtle`) — available by default in
442
+ * Node.js >= 19 (Node 18 needs `--experimental-global-webcrypto`), browsers,
443
+ * and edge runtimes. Throws instead of falling back to a weaker digest so a
444
+ * missing crypto implementation can never silently change the contract.
445
+ */
446
+ declare function sha256Hex(text: string): Promise<string>;
447
+ /** Canonicalizes `value` and returns its lowercase SHA-256 hex digest. */
448
+ declare function hashCanonicalJson(value: FFIDCanonicalJsonValue): Promise<string>;
449
+ /**
450
+ * Human-approved tax configuration snapshot hashed into
451
+ * `publication.taxConfigurationHash`. FFID re-computes this from its live
452
+ * Stripe Tax settings at paid-checkout start and refuses the session on
453
+ * mismatch.
454
+ */
455
+ interface FFIDTaxConfiguration {
456
+ /** ISO 3166-1 alpha-2 country/region codes where paid sales are allowed */
457
+ allowedCountries: string[];
458
+ /** Country/region codes with completed Stripe Tax registrations */
459
+ stripeTaxRegistrations: string[];
460
+ /** Whether full billing address collection is required before paid checkout */
461
+ billingAddressRequired: boolean;
462
+ /** Stripe Product tax code per plan code (e.g. `txcd_10000000`) */
463
+ planTaxCodes: Record<string, string>;
464
+ /** Tax behavior per plan code */
465
+ planTaxBehaviors: Record<string, FFIDTaxBehavior>;
466
+ }
467
+ /**
468
+ * Human-approved payment configuration snapshot hashed into
469
+ * `publication.paymentConfigurationHash`.
470
+ */
471
+ interface FFIDPaymentConfiguration {
472
+ environment: FFIDCatalogEnvironment;
473
+ /** Lowercase ISO 4217 currency (e.g. `jpy`) */
474
+ currency: string;
475
+ /** Explicit Stripe payment method allow-list (initial sales: `['card']`) */
476
+ paymentMethodTypes: string[];
477
+ /** Stripe Dashboard dynamic payment methods must stay disabled for sales */
478
+ dynamicPaymentMethodsEnabled: boolean;
479
+ manualInvoiceAllowed: boolean;
480
+ customerBalanceAllowed: boolean;
481
+ delayedPaymentMethodsAllowed: boolean;
482
+ /** Required Stripe Customer invoice credit balance before checkout (spec: 0) */
483
+ customerInvoiceBalanceRequired: number;
484
+ }
485
+ /** Sales-scope-relevant snapshot of one published plan (subset of the catalog) */
486
+ interface FFIDScopeHashPlan {
487
+ code: string;
488
+ priceMonthly: number | null;
489
+ priceYearly: number | null;
490
+ currency: string;
491
+ billingIntervals: FFIDBillingInterval[];
492
+ taxBehavior: FFIDTaxBehavior;
493
+ minSeats: number;
494
+ }
495
+ /**
496
+ * Input of `computeScopeHash` — the published plans' sales conditions plus
497
+ * the five content/config hashes. Changing any of them changes `scopeHash`,
498
+ * which invalidates the previous publication approval (fail-closed).
499
+ */
500
+ interface FFIDScopeHashInput {
501
+ plans: FFIDScopeHashPlan[];
502
+ praxisCopyHash: string;
503
+ ffidCheckoutCopyHash: string;
504
+ legalDisclosureHash: string;
505
+ taxConfigurationHash: string;
506
+ paymentConfigurationHash: string;
507
+ }
508
+ /**
509
+ * `praxisCopyHash`: hash of the consumer's entire structured sales
510
+ * presentation (`PRAXIS_SALES_PRESENTATION` — locales, plan display names,
511
+ * prices, feature matrix, CTAs, eligibility). The consumer re-hashes what it
512
+ * actually renders and must refuse new sales on mismatch.
513
+ */
514
+ declare function computePraxisCopyHash(presentation: Record<string, FFIDCanonicalJsonValue>): Promise<string>;
515
+ /** `ffidCheckoutCopyHash`: hash of FFID's structured final-confirmation copy */
516
+ declare function computeFFIDCheckoutCopyHash(checkoutCopy: Record<string, FFIDCanonicalJsonValue>): Promise<string>;
517
+ /**
518
+ * `legalDisclosureHash`: hash of the canonical body text of the reviewed
519
+ * legal disclosure document version. Line endings are normalized to LF before
520
+ * hashing so the digest is independent of platform line-ending conventions;
521
+ * the text is otherwise hashed byte-exact (no trimming, no Unicode
522
+ * normalization).
523
+ */
524
+ declare function computeLegalDisclosureHash(canonicalText: string): Promise<string>;
525
+ /**
526
+ * `taxConfigurationHash`: hash of the human-approved tax configuration.
527
+ * Country lists are treated as sets (sorted + deduplicated) so representation
528
+ * order cannot flip the digest.
529
+ */
530
+ declare function computeTaxConfigurationHash(config: FFIDTaxConfiguration): Promise<string>;
531
+ /**
532
+ * `paymentConfigurationHash`: hash of the human-approved payment
533
+ * configuration. `paymentMethodTypes` is treated as a set.
534
+ */
535
+ declare function computePaymentConfigurationHash(config: FFIDPaymentConfiguration): Promise<string>;
536
+ /**
537
+ * `scopeHash`: hash of the published sales scope — plan codes, amounts,
538
+ * currency, intervals, tax behavior, minimum seats, plus the five embedded
539
+ * content/config hashes. Plans are sorted by `code`; duplicate codes are
540
+ * rejected. Embedded hashes are validated for format so a malformed digest
541
+ * can never be baked into an approval.
542
+ */
543
+ declare function computeScopeHash(input: FFIDScopeHashInput): Promise<string>;
544
+
545
+ /** Published catalog methods (#4342 / ASDD-PRICING-002 SP4A) */
546
+
547
+ /**
548
+ * Validates the full published-catalog shape the SDK's compile-time types
549
+ * promise to consumers:
550
+ * - `service.code` / `service.name` are non-empty strings
551
+ * - top-level `catalogVersion` equals `publication.catalogVersion`
552
+ * - `publication.status` is one of the four known literals (unknown statuses
553
+ * must fail loud here, NOT be treated as sellable downstream) and
554
+ * `publication.environment` is a known environment
555
+ * - the six approval hashes are lowercase SHA-256 hex; `salesEpoch` is a
556
+ * positive integer; timestamps are present (or explicitly null where the
557
+ * type allows null)
558
+ * - every plan carries the full `FFIDPublishedPlan` field set with
559
+ * cross-field invariants (an offered interval has its price;
560
+ * `minSeats <= maxSeats`), and no plan field NAME mentions Stripe
561
+ * (defense-in-depth heuristic — the authoritative no-Stripe-ID guard is
562
+ * the FFID server's `.strict()` snapshot schema)
563
+ *
564
+ * Throws FFIDSDKError on violation so a server regression surfaces at the
565
+ * SDK boundary instead of silently reaching a consumer's sales gate.
566
+ */
567
+ declare function validatePublishedCatalog(catalog: unknown): asserts catalog is FFIDPublishedCatalog;
568
+
569
+ export { ALL_DENIED_EXCEPT_NECESSARY, type BuildAuthorizeUrlOptions, type BuildAuthorizeUrlResult, CATALOG_HASH_PATTERN, CONSENT_COOKIE_NAME, COOKIE_VERSION, type CookieOptions, FFIDCacheAdapter, type FFIDCanonicalJsonValue, FFIDCatalogEnvironment, FFIDCatalogHashError, type FFIDConsentCategories, type FFIDConsentCategoryCode, type FFIDCookieStoreLike, type FFIDErrorCode, FFIDOAuthUserInfo, type FFIDPaymentConfiguration, FFIDPublishedCatalog, type FFIDScopeHashInput, type FFIDScopeHashPlan, FFIDTaxBehavior, type FFIDTaxConfiguration, FFIDVerifyAccessTokenOptions, FFID_ERROR_CODES, FFID_SESSION_COOKIE_NAME, type HandleCallbackInput, type KVNamespaceLike, type ServerAuthClient, type ServerAuthConfig, type ServerSessionTokens, type ServerTokenResult, canonicalizeCatalogValue, computeFFIDCheckoutCopyHash, computeLegalDisclosureHash, computePaymentConfigurationHash, computePraxisCopyHash, computeScopeHash, computeTaxConfigurationHash, createKVCacheAdapter, createMemoryCacheAdapter, createServerAuthClient, createVerifyAccessToken, decodeConsentCookie, encodeConsentCookie, generateServerState, getFFIDConsentFromCookieHeader, getFFIDConsentFromCookies, hashCanonicalJson, sessionCookies, sha256Hex, validatePublishedCatalog };
@@ -850,6 +850,157 @@ var FFID_CATALOG_PUBLICATION_STATUSES = [
850
850
 
851
851
  // src/shared/catalog-hash.ts
852
852
  var CATALOG_HASH_PATTERN = /^[0-9a-f]{64}$/;
853
+ var HEX_RADIX = 16;
854
+ var HEX_DIGITS_PER_BYTE = 2;
855
+ var FFIDCatalogHashError = class extends Error {
856
+ constructor(message) {
857
+ super(message);
858
+ this.name = "FFIDCatalogHashError";
859
+ }
860
+ };
861
+ function isPlainObject(value) {
862
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
863
+ return false;
864
+ }
865
+ const proto = Object.getPrototypeOf(value);
866
+ return proto === Object.prototype || proto === null;
867
+ }
868
+ function canonicalize(value, path) {
869
+ if (value === null) return "null";
870
+ switch (typeof value) {
871
+ case "string":
872
+ return JSON.stringify(value);
873
+ case "boolean":
874
+ return value ? "true" : "false";
875
+ case "number":
876
+ if (!Number.isFinite(value)) {
877
+ throw new FFIDCatalogHashError(
878
+ `canonicalize: non-finite number at ${path} \u2014 hashes require finite numbers`
879
+ );
880
+ }
881
+ return JSON.stringify(value);
882
+ case "undefined":
883
+ throw new FFIDCatalogHashError(
884
+ `canonicalize: undefined at ${path} \u2014 omit the key entirely or use null`
885
+ );
886
+ }
887
+ if (Array.isArray(value)) {
888
+ const items = value.map((item, index) => canonicalize(item, `${path}[${index}]`));
889
+ return `[${items.join(",")}]`;
890
+ }
891
+ if (isPlainObject(value)) {
892
+ const keys = Object.keys(value).sort();
893
+ const entries = [];
894
+ for (const key of keys) {
895
+ const child = value[key];
896
+ if (child === void 0) {
897
+ throw new FFIDCatalogHashError(
898
+ `canonicalize: undefined at ${path}.${key} \u2014 omit the key entirely or use null`
899
+ );
900
+ }
901
+ entries.push(`${JSON.stringify(key)}:${canonicalize(child, `${path}.${key}`)}`);
902
+ }
903
+ return `{${entries.join(",")}}`;
904
+ }
905
+ throw new FFIDCatalogHashError(
906
+ `canonicalize: unsupported value type (${typeof value}${typeof value === "object" ? `: ${Object.prototype.toString.call(value)}` : ""}) at ${path} \u2014 only JSON-plain values are hashable`
907
+ );
908
+ }
909
+ function canonicalizeCatalogValue(value) {
910
+ return canonicalize(value, "$");
911
+ }
912
+ async function sha256Hex(text) {
913
+ const subtle = globalThis.crypto?.subtle;
914
+ if (!subtle) {
915
+ throw new FFIDCatalogHashError(
916
+ "sha256Hex: Web Crypto (crypto.subtle) is not available in this runtime"
917
+ );
918
+ }
919
+ const digest = await subtle.digest("SHA-256", new TextEncoder().encode(text));
920
+ return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(HEX_RADIX).padStart(HEX_DIGITS_PER_BYTE, "0")).join("");
921
+ }
922
+ async function hashCanonicalJson(value) {
923
+ return sha256Hex(canonicalizeCatalogValue(value));
924
+ }
925
+ function assertHashFormat(hash, field) {
926
+ if (!CATALOG_HASH_PATTERN.test(hash)) {
927
+ throw new FFIDCatalogHashError(
928
+ `${field} must be lowercase SHA-256 hex (64 chars), got: ${JSON.stringify(hash)}`
929
+ );
930
+ }
931
+ }
932
+ function normalizeStringSet(values) {
933
+ return [...new Set(values)].sort();
934
+ }
935
+ function compareByCodeUnits(a, b) {
936
+ if (a.code < b.code) return -1;
937
+ if (a.code > b.code) return 1;
938
+ return 0;
939
+ }
940
+ async function computePraxisCopyHash(presentation) {
941
+ return hashCanonicalJson(presentation);
942
+ }
943
+ async function computeFFIDCheckoutCopyHash(checkoutCopy) {
944
+ return hashCanonicalJson(checkoutCopy);
945
+ }
946
+ async function computeLegalDisclosureHash(canonicalText) {
947
+ return sha256Hex(canonicalText.replace(/\r\n?/g, "\n"));
948
+ }
949
+ async function computeTaxConfigurationHash(config) {
950
+ return hashCanonicalJson({
951
+ allowedCountries: normalizeStringSet(config.allowedCountries),
952
+ stripeTaxRegistrations: normalizeStringSet(config.stripeTaxRegistrations),
953
+ billingAddressRequired: config.billingAddressRequired,
954
+ planTaxCodes: config.planTaxCodes,
955
+ planTaxBehaviors: config.planTaxBehaviors
956
+ });
957
+ }
958
+ async function computePaymentConfigurationHash(config) {
959
+ return hashCanonicalJson({
960
+ environment: config.environment,
961
+ currency: config.currency,
962
+ paymentMethodTypes: normalizeStringSet(config.paymentMethodTypes),
963
+ dynamicPaymentMethodsEnabled: config.dynamicPaymentMethodsEnabled,
964
+ manualInvoiceAllowed: config.manualInvoiceAllowed,
965
+ customerBalanceAllowed: config.customerBalanceAllowed,
966
+ delayedPaymentMethodsAllowed: config.delayedPaymentMethodsAllowed,
967
+ customerInvoiceBalanceRequired: config.customerInvoiceBalanceRequired
968
+ });
969
+ }
970
+ async function computeScopeHash(input) {
971
+ assertHashFormat(input.praxisCopyHash, "praxisCopyHash");
972
+ assertHashFormat(input.ffidCheckoutCopyHash, "ffidCheckoutCopyHash");
973
+ assertHashFormat(input.legalDisclosureHash, "legalDisclosureHash");
974
+ assertHashFormat(input.taxConfigurationHash, "taxConfigurationHash");
975
+ assertHashFormat(input.paymentConfigurationHash, "paymentConfigurationHash");
976
+ const seenCodes = /* @__PURE__ */ new Set();
977
+ for (const plan of input.plans) {
978
+ if (seenCodes.has(plan.code)) {
979
+ throw new FFIDCatalogHashError(
980
+ `computeScopeHash: duplicate plan code ${JSON.stringify(plan.code)}`
981
+ );
982
+ }
983
+ seenCodes.add(plan.code);
984
+ }
985
+ const plans = [...input.plans].sort(compareByCodeUnits).map((plan) => ({
986
+ code: plan.code,
987
+ priceMonthly: plan.priceMonthly,
988
+ priceYearly: plan.priceYearly,
989
+ currency: plan.currency,
990
+ // Set semantics — interval order must not flip the digest.
991
+ billingIntervals: normalizeStringSet(plan.billingIntervals),
992
+ taxBehavior: plan.taxBehavior,
993
+ minSeats: plan.minSeats
994
+ }));
995
+ return hashCanonicalJson({
996
+ plans,
997
+ praxisCopyHash: input.praxisCopyHash,
998
+ ffidCheckoutCopyHash: input.ffidCheckoutCopyHash,
999
+ legalDisclosureHash: input.legalDisclosureHash,
1000
+ taxConfigurationHash: input.taxConfigurationHash,
1001
+ paymentConfigurationHash: input.paymentConfigurationHash
1002
+ });
1003
+ }
853
1004
 
854
1005
  // src/client/catalog-methods.ts
855
1006
  var EXT_CATALOG_ENDPOINT = "/api/v1/subscriptions/ext/catalog";
@@ -1132,6 +1283,7 @@ function createMembersMethods(deps) {
1132
1283
 
1133
1284
  // src/client/provisioning-methods.ts
1134
1285
  var USER_PROVISION_ENDPOINT = "/api/v1/ext/users/provision";
1286
+ var USER_WELCOME_EMAIL_ENDPOINT = "/api/v1/ext/users/welcome-email";
1135
1287
  var ORGANIZATION_PROVISION_ENDPOINT = "/api/v1/ext/organizations/provision";
1136
1288
  var MAX_PROVISION_MEMBERS = 100;
1137
1289
  var ASSIGNABLE_MEMBER_ROLES = {
@@ -1148,7 +1300,7 @@ function createProvisioningMethods(deps) {
1148
1300
  if (authMode === "service-key") return null;
1149
1301
  return createError(
1150
1302
  "VALIDATION_ERROR",
1151
- "provisionUser / provisionOrganization \u306F service-key \u8A8D\u8A3C\uFF08X-Service-Api-Key\uFF09\u3067\u306E\u307F\u5229\u7528\u3067\u304D\u307E\u3059\u3002Bearer / cookie \u30E2\u30FC\u30C9\u3067\u306F\u547C\u3073\u51FA\u305B\u307E\u305B\u3093"
1303
+ "provisionUser / sendProvisionWelcomeEmail / provisionOrganization \u306F service-key \u8A8D\u8A3C\uFF08X-Service-Api-Key\uFF09\u3067\u306E\u307F\u5229\u7528\u3067\u304D\u307E\u3059\u3002Bearer / cookie \u30E2\u30FC\u30C9\u3067\u306F\u547C\u3073\u51FA\u305B\u307E\u305B\u3093"
1152
1304
  );
1153
1305
  }
1154
1306
  async function provisionUser(params) {
@@ -1165,6 +1317,17 @@ function createProvisioningMethods(deps) {
1165
1317
  body: JSON.stringify(body)
1166
1318
  });
1167
1319
  }
1320
+ async function sendProvisionWelcomeEmail(params) {
1321
+ const modeError = serviceKeyModeError();
1322
+ if (modeError) return { error: modeError };
1323
+ if (!params.email || !params.email.trim()) {
1324
+ return { error: createError("VALIDATION_ERROR", "email \u306F\u5FC5\u9808\u3067\u3059") };
1325
+ }
1326
+ return fetchWithAuth(USER_WELCOME_EMAIL_ENDPOINT, {
1327
+ method: "POST",
1328
+ body: JSON.stringify({ email: params.email.trim() })
1329
+ });
1330
+ }
1168
1331
  async function provisionOrganization(params) {
1169
1332
  const modeError = serviceKeyModeError();
1170
1333
  if (modeError) return { error: modeError };
@@ -1214,7 +1377,7 @@ function createProvisioningMethods(deps) {
1214
1377
  body: JSON.stringify(body)
1215
1378
  });
1216
1379
  }
1217
- return { provisionUser, provisionOrganization };
1380
+ return { provisionUser, sendProvisionWelcomeEmail, provisionOrganization };
1218
1381
  }
1219
1382
 
1220
1383
  // src/client/seat-methods.ts
@@ -1488,7 +1651,7 @@ function validateTokenResponse(tokenResponse) {
1488
1651
  }
1489
1652
 
1490
1653
  // src/client/version-check.ts
1491
- var SDK_VERSION = "5.27.0";
1654
+ var SDK_VERSION = "5.29.0";
1492
1655
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
1493
1656
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
1494
1657
  function sdkHeaders() {
@@ -2900,6 +3063,11 @@ function createInquiryMethods(deps) {
2900
3063
  name,
2901
3064
  message,
2902
3065
  category: params.category,
3066
+ // Normalize null ("survey not collected", e.g. forwarded verbatim
3067
+ // from FFIDInquiryFormSubmitData) to undefined so JSON.stringify
3068
+ // drops the key entirely — older FFID deployments never see it and
3069
+ // current ones treat the omission as NULL.
3070
+ acquisitionSource: params.acquisitionSource ?? void 0,
2903
3071
  company: params.company,
2904
3072
  phone: params.phone,
2905
3073
  locale: params.locale,
@@ -3359,7 +3527,7 @@ function createFFIDClient(config) {
3359
3527
  createError,
3360
3528
  serviceCode: config.serviceCode
3361
3529
  });
3362
- const { provisionUser, provisionOrganization } = createProvisioningMethods({
3530
+ const { provisionUser, sendProvisionWelcomeEmail, provisionOrganization } = createProvisioningMethods({
3363
3531
  fetchWithAuth,
3364
3532
  createError,
3365
3533
  authMode
@@ -3460,6 +3628,7 @@ function createFFIDClient(config) {
3460
3628
  updateMemberRole,
3461
3629
  removeMember,
3462
3630
  provisionUser,
3631
+ sendProvisionWelcomeEmail,
3463
3632
  provisionOrganization,
3464
3633
  getProfile,
3465
3634
  updateProfile,
@@ -3661,11 +3830,11 @@ function getFFIDConsentFromCookieHeader(cookieHeader) {
3661
3830
 
3662
3831
  // src/server/auth/server-state.ts
3663
3832
  var STATE_RANDOM_BYTES2 = 16;
3664
- var HEX_RADIX = 16;
3833
+ var HEX_RADIX2 = 16;
3665
3834
  function generateServerState() {
3666
3835
  const bytes = new Uint8Array(STATE_RANDOM_BYTES2);
3667
3836
  globalThis.crypto.getRandomValues(bytes);
3668
- return Array.from(bytes, (b) => b.toString(HEX_RADIX).padStart(2, "0")).join("");
3837
+ return Array.from(bytes, (b) => b.toString(HEX_RADIX2).padStart(2, "0")).join("");
3669
3838
  }
3670
3839
 
3671
3840
  // src/server/auth/server-oauth-token.ts
@@ -3915,4 +4084,4 @@ function clear(opts = {}) {
3915
4084
  }
3916
4085
  var sessionCookies = { serialize, read, clear };
3917
4086
 
3918
- export { ALL_DENIED_EXCEPT_NECESSARY, CONSENT_COOKIE_NAME, COOKIE_VERSION, FFID_ERROR_CODES, FFID_SESSION_COOKIE_NAME, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createServerAuthClient, createTokenStore, createVerifyAccessToken, decodeConsentCookie, encodeConsentCookie, generateServerState, getFFIDConsentFromCookieHeader, getFFIDConsentFromCookies, sessionCookies };
4087
+ export { ALL_DENIED_EXCEPT_NECESSARY, CATALOG_HASH_PATTERN, CONSENT_COOKIE_NAME, COOKIE_VERSION, FFIDCatalogHashError, FFID_CATALOG_ENVIRONMENTS, FFID_CATALOG_PUBLICATION_STATUSES, FFID_ERROR_CODES, FFID_SESSION_COOKIE_NAME, canonicalizeCatalogValue, computeFFIDCheckoutCopyHash, computeLegalDisclosureHash, computePaymentConfigurationHash, computePraxisCopyHash, computeScopeHash, computeTaxConfigurationHash, createFFIDClient, createKVCacheAdapter, createMemoryCacheAdapter, createServerAuthClient, createTokenStore, createVerifyAccessToken, decodeConsentCookie, encodeConsentCookie, generateServerState, getFFIDConsentFromCookieHeader, getFFIDConsentFromCookies, hashCanonicalJson, sessionCookies, sha256Hex, validatePublishedCatalog };
@@ -1,4 +1,4 @@
1
- import { k as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-BlVuH6ja.cjs';
1
+ import { q as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-hv2PuUr7.cjs';
2
2
  import '../../types-Cjb9J0rL.cjs';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { k as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-BqnzUmG7.js';
1
+ import { q as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-DCS-MoxK.js';
2
2
  import '../../types-Cjb9J0rL.js';
3
3
 
4
4
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feelflow/ffid-sdk",
3
- "version": "5.27.0",
3
+ "version": "5.29.0",
4
4
  "description": "FeelFlow ID Platform SDK for React/Next.js applications",
5
5
  "keywords": [
6
6
  "feelflow",