@feelflow/ffid-sdk 5.25.0 → 5.28.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.
@@ -124,6 +124,16 @@ function normalizeEntitlements(raw) {
124
124
  return trimmed.length > 0 ? [trimmed] : [];
125
125
  });
126
126
  }
127
+ function normalizeSeatAssignmentClaim(raw, organizationId, subscriptionId) {
128
+ if (!raw || typeof raw !== "object") return null;
129
+ const { organization_id, subscription_id, status } = raw;
130
+ if (typeof organization_id !== "string" || typeof subscription_id !== "string" || typeof status !== "string" || !["active", "revoking", "revoked"].includes(status) || organization_id !== organizationId || subscription_id !== subscriptionId) return null;
131
+ return {
132
+ organizationId: organization_id,
133
+ subscriptionId: subscription_id,
134
+ status
135
+ };
136
+ }
127
137
  function normalizeUserinfo(raw) {
128
138
  const hasOrganizationId = hasOwnKey(raw, "organization_id");
129
139
  const hasOrganizationName = hasOwnKey(raw, "organization_name");
@@ -157,6 +167,11 @@ function normalizeUserinfo(raw) {
157
167
  organizationId: raw.subscription.organization_id ?? null,
158
168
  organizationName: hasOwnKey(raw.subscription, "organization_name") ? raw.subscription.organization_name ?? null : null,
159
169
  hasSeatAssignment: raw.subscription.has_seat_assignment ?? false,
170
+ seatAssignment: hasOwnKey(raw.subscription, "seat_assignment") ? normalizeSeatAssignmentClaim(
171
+ raw.subscription.seat_assignment,
172
+ raw.subscription.organization_id ?? null,
173
+ raw.subscription.subscription_id ?? null
174
+ ) : void 0,
160
175
  // #3464: legacy FFID (< this issue) omits the field on the wire.
161
176
  // The "unknown" axis is unused — no consumer differentiates "server
162
177
  // didn't tell us" from "definitely not scheduled" because the
@@ -455,6 +470,7 @@ function createVerifyAccessToken(deps) {
455
470
  organization_id: introspectResponse.subscription.organization_id,
456
471
  organization_name: introspectResponse.subscription.organization_name ?? null,
457
472
  has_seat_assignment: introspectResponse.subscription.has_seat_assignment ?? false,
473
+ ...Object.prototype.hasOwnProperty.call(introspectResponse.subscription, "seat_assignment") ? { seat_assignment: introspectResponse.subscription.seat_assignment } : {},
458
474
  // #4333: forward pack entitlement codes through the introspect →
459
475
  // userinfo normalization boundary. normalizeUserinfo guards the
460
476
  // value, so an older FFID that omits it degrades to `[]`.
@@ -829,6 +845,364 @@ function createSubscriptionMethods(deps) {
829
845
  };
830
846
  }
831
847
 
848
+ // src/types/published-catalog.ts
849
+ var FFID_CATALOG_ENVIRONMENTS = ["staging", "production"];
850
+ var FFID_CATALOG_PUBLICATION_STATUSES = [
851
+ "approved",
852
+ "superseded",
853
+ "revoking",
854
+ "revoked"
855
+ ];
856
+
857
+ // src/shared/catalog-hash.ts
858
+ var CATALOG_HASH_PATTERN = /^[0-9a-f]{64}$/;
859
+ var HEX_RADIX = 16;
860
+ var HEX_DIGITS_PER_BYTE = 2;
861
+ var FFIDCatalogHashError = class extends Error {
862
+ constructor(message) {
863
+ super(message);
864
+ this.name = "FFIDCatalogHashError";
865
+ }
866
+ };
867
+ function isPlainObject(value) {
868
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
869
+ return false;
870
+ }
871
+ const proto = Object.getPrototypeOf(value);
872
+ return proto === Object.prototype || proto === null;
873
+ }
874
+ function canonicalize(value, path) {
875
+ if (value === null) return "null";
876
+ switch (typeof value) {
877
+ case "string":
878
+ return JSON.stringify(value);
879
+ case "boolean":
880
+ return value ? "true" : "false";
881
+ case "number":
882
+ if (!Number.isFinite(value)) {
883
+ throw new FFIDCatalogHashError(
884
+ `canonicalize: non-finite number at ${path} \u2014 hashes require finite numbers`
885
+ );
886
+ }
887
+ return JSON.stringify(value);
888
+ case "undefined":
889
+ throw new FFIDCatalogHashError(
890
+ `canonicalize: undefined at ${path} \u2014 omit the key entirely or use null`
891
+ );
892
+ }
893
+ if (Array.isArray(value)) {
894
+ const items = value.map((item, index) => canonicalize(item, `${path}[${index}]`));
895
+ return `[${items.join(",")}]`;
896
+ }
897
+ if (isPlainObject(value)) {
898
+ const keys = Object.keys(value).sort();
899
+ const entries = [];
900
+ for (const key of keys) {
901
+ const child = value[key];
902
+ if (child === void 0) {
903
+ throw new FFIDCatalogHashError(
904
+ `canonicalize: undefined at ${path}.${key} \u2014 omit the key entirely or use null`
905
+ );
906
+ }
907
+ entries.push(`${JSON.stringify(key)}:${canonicalize(child, `${path}.${key}`)}`);
908
+ }
909
+ return `{${entries.join(",")}}`;
910
+ }
911
+ throw new FFIDCatalogHashError(
912
+ `canonicalize: unsupported value type (${typeof value}${typeof value === "object" ? `: ${Object.prototype.toString.call(value)}` : ""}) at ${path} \u2014 only JSON-plain values are hashable`
913
+ );
914
+ }
915
+ function canonicalizeCatalogValue(value) {
916
+ return canonicalize(value, "$");
917
+ }
918
+ async function sha256Hex(text) {
919
+ const subtle = globalThis.crypto?.subtle;
920
+ if (!subtle) {
921
+ throw new FFIDCatalogHashError(
922
+ "sha256Hex: Web Crypto (crypto.subtle) is not available in this runtime"
923
+ );
924
+ }
925
+ const digest = await subtle.digest("SHA-256", new TextEncoder().encode(text));
926
+ return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(HEX_RADIX).padStart(HEX_DIGITS_PER_BYTE, "0")).join("");
927
+ }
928
+ async function hashCanonicalJson(value) {
929
+ return sha256Hex(canonicalizeCatalogValue(value));
930
+ }
931
+ function assertHashFormat(hash, field) {
932
+ if (!CATALOG_HASH_PATTERN.test(hash)) {
933
+ throw new FFIDCatalogHashError(
934
+ `${field} must be lowercase SHA-256 hex (64 chars), got: ${JSON.stringify(hash)}`
935
+ );
936
+ }
937
+ }
938
+ function normalizeStringSet(values) {
939
+ return [...new Set(values)].sort();
940
+ }
941
+ function compareByCodeUnits(a, b) {
942
+ if (a.code < b.code) return -1;
943
+ if (a.code > b.code) return 1;
944
+ return 0;
945
+ }
946
+ async function computePraxisCopyHash(presentation) {
947
+ return hashCanonicalJson(presentation);
948
+ }
949
+ async function computeFFIDCheckoutCopyHash(checkoutCopy) {
950
+ return hashCanonicalJson(checkoutCopy);
951
+ }
952
+ async function computeLegalDisclosureHash(canonicalText) {
953
+ return sha256Hex(canonicalText.replace(/\r\n?/g, "\n"));
954
+ }
955
+ async function computeTaxConfigurationHash(config) {
956
+ return hashCanonicalJson({
957
+ allowedCountries: normalizeStringSet(config.allowedCountries),
958
+ stripeTaxRegistrations: normalizeStringSet(config.stripeTaxRegistrations),
959
+ billingAddressRequired: config.billingAddressRequired,
960
+ planTaxCodes: config.planTaxCodes,
961
+ planTaxBehaviors: config.planTaxBehaviors
962
+ });
963
+ }
964
+ async function computePaymentConfigurationHash(config) {
965
+ return hashCanonicalJson({
966
+ environment: config.environment,
967
+ currency: config.currency,
968
+ paymentMethodTypes: normalizeStringSet(config.paymentMethodTypes),
969
+ dynamicPaymentMethodsEnabled: config.dynamicPaymentMethodsEnabled,
970
+ manualInvoiceAllowed: config.manualInvoiceAllowed,
971
+ customerBalanceAllowed: config.customerBalanceAllowed,
972
+ delayedPaymentMethodsAllowed: config.delayedPaymentMethodsAllowed,
973
+ customerInvoiceBalanceRequired: config.customerInvoiceBalanceRequired
974
+ });
975
+ }
976
+ async function computeScopeHash(input) {
977
+ assertHashFormat(input.praxisCopyHash, "praxisCopyHash");
978
+ assertHashFormat(input.ffidCheckoutCopyHash, "ffidCheckoutCopyHash");
979
+ assertHashFormat(input.legalDisclosureHash, "legalDisclosureHash");
980
+ assertHashFormat(input.taxConfigurationHash, "taxConfigurationHash");
981
+ assertHashFormat(input.paymentConfigurationHash, "paymentConfigurationHash");
982
+ const seenCodes = /* @__PURE__ */ new Set();
983
+ for (const plan of input.plans) {
984
+ if (seenCodes.has(plan.code)) {
985
+ throw new FFIDCatalogHashError(
986
+ `computeScopeHash: duplicate plan code ${JSON.stringify(plan.code)}`
987
+ );
988
+ }
989
+ seenCodes.add(plan.code);
990
+ }
991
+ const plans = [...input.plans].sort(compareByCodeUnits).map((plan) => ({
992
+ code: plan.code,
993
+ priceMonthly: plan.priceMonthly,
994
+ priceYearly: plan.priceYearly,
995
+ currency: plan.currency,
996
+ // Set semantics — interval order must not flip the digest.
997
+ billingIntervals: normalizeStringSet(plan.billingIntervals),
998
+ taxBehavior: plan.taxBehavior,
999
+ minSeats: plan.minSeats
1000
+ }));
1001
+ return hashCanonicalJson({
1002
+ plans,
1003
+ praxisCopyHash: input.praxisCopyHash,
1004
+ ffidCheckoutCopyHash: input.ffidCheckoutCopyHash,
1005
+ legalDisclosureHash: input.legalDisclosureHash,
1006
+ taxConfigurationHash: input.taxConfigurationHash,
1007
+ paymentConfigurationHash: input.paymentConfigurationHash
1008
+ });
1009
+ }
1010
+
1011
+ // src/client/catalog-methods.ts
1012
+ var EXT_CATALOG_ENDPOINT = "/api/v1/subscriptions/ext/catalog";
1013
+ var MALFORMED_CATALOG_CODE = "MALFORMED_PUBLISHED_CATALOG";
1014
+ var PUBLICATION_STATUSES = new Set(
1015
+ FFID_CATALOG_PUBLICATION_STATUSES
1016
+ );
1017
+ var CATALOG_ENVIRONMENTS = new Set(FFID_CATALOG_ENVIRONMENTS);
1018
+ var BILLING_INTERVALS = /* @__PURE__ */ new Set(["monthly", "yearly"]);
1019
+ var TAX_BEHAVIORS = /* @__PURE__ */ new Set(["inclusive", "exclusive"]);
1020
+ var PUBLICATION_HASH_FIELDS = [
1021
+ "praxisCopyHash",
1022
+ "ffidCheckoutCopyHash",
1023
+ "legalDisclosureHash",
1024
+ "taxConfigurationHash",
1025
+ "paymentConfigurationHash",
1026
+ "scopeHash"
1027
+ ];
1028
+ var PUBLICATION_REQUIRED_TIMESTAMP_FIELDS = ["reviewedAt", "effectiveAt"];
1029
+ var PUBLICATION_NULLABLE_TIMESTAMP_FIELDS = [
1030
+ "supersededAt",
1031
+ "sellThroughUntil",
1032
+ "revocationEffectiveAt"
1033
+ ];
1034
+ function malformed(detail) {
1035
+ return new FFIDSDKError(
1036
+ MALFORMED_CATALOG_CODE,
1037
+ `SDK: server returned malformed PublishedCatalog \u2014 ${detail}`
1038
+ );
1039
+ }
1040
+ function isNullableIsoString(value) {
1041
+ return value === null || typeof value === "string" && value.trim() !== "";
1042
+ }
1043
+ function isNonNegativeIntOrNull(value) {
1044
+ return value === null || typeof value === "number" && Number.isInteger(value) && value >= 0;
1045
+ }
1046
+ function validatePublishedPlan(plan, index) {
1047
+ if (plan === null || typeof plan !== "object") {
1048
+ throw malformed(`plans[${index}] must be an object`);
1049
+ }
1050
+ const record = plan;
1051
+ for (const key of Object.keys(record)) {
1052
+ if (/stripe/i.test(key)) {
1053
+ throw malformed(
1054
+ `plans[${index}] contains a Stripe identifier field (${JSON.stringify(key)}) \u2014 published catalogs must never expose Stripe IDs`
1055
+ );
1056
+ }
1057
+ }
1058
+ if (isBlankString(record.code)) {
1059
+ throw malformed(`plans[${index}].code must be a non-empty string`);
1060
+ }
1061
+ if (isBlankString(record.name)) {
1062
+ throw malformed(`plans[${index}].name must be a non-empty string`);
1063
+ }
1064
+ if (record.description !== null && typeof record.description !== "string") {
1065
+ throw malformed(`plans[${index}].description must be string or null`);
1066
+ }
1067
+ if (isBlankString(record.currency)) {
1068
+ throw malformed(`plans[${index}].currency must be a non-empty string`);
1069
+ }
1070
+ if (!isNonNegativeIntOrNull(record.priceMonthly)) {
1071
+ throw malformed(`plans[${index}].priceMonthly must be a non-negative integer or null`);
1072
+ }
1073
+ if (!isNonNegativeIntOrNull(record.priceYearly)) {
1074
+ throw malformed(`plans[${index}].priceYearly must be a non-negative integer or null`);
1075
+ }
1076
+ if (typeof record.taxBehavior !== "string" || !TAX_BEHAVIORS.has(record.taxBehavior)) {
1077
+ throw malformed(
1078
+ `plans[${index}].taxBehavior must be inclusive | exclusive (got ${JSON.stringify(record.taxBehavior)})`
1079
+ );
1080
+ }
1081
+ const intervals = record.billingIntervals;
1082
+ if (!Array.isArray(intervals) || intervals.length === 0 || intervals.some((interval) => typeof interval !== "string" || !BILLING_INTERVALS.has(interval))) {
1083
+ throw malformed(
1084
+ `plans[${index}].billingIntervals must be a non-empty array of monthly | yearly`
1085
+ );
1086
+ }
1087
+ if (intervals.includes("monthly") && record.priceMonthly === null) {
1088
+ throw malformed(`plans[${index}] offers monthly billing but priceMonthly is null`);
1089
+ }
1090
+ if (intervals.includes("yearly") && record.priceYearly === null) {
1091
+ throw malformed(`plans[${index}] offers yearly billing but priceYearly is null`);
1092
+ }
1093
+ if (typeof record.minSeats !== "number" || !Number.isInteger(record.minSeats) || record.minSeats < 1) {
1094
+ throw malformed(`plans[${index}].minSeats must be a positive integer`);
1095
+ }
1096
+ if (record.maxSeats !== null && (typeof record.maxSeats !== "number" || !Number.isInteger(record.maxSeats) || record.maxSeats < 1)) {
1097
+ throw malformed(`plans[${index}].maxSeats must be a positive integer or null`);
1098
+ }
1099
+ if (record.maxSeats !== null && record.maxSeats < record.minSeats) {
1100
+ throw malformed(`plans[${index}].maxSeats must be >= minSeats`);
1101
+ }
1102
+ if (typeof record.displayOrder !== "number" || !Number.isInteger(record.displayOrder)) {
1103
+ throw malformed(`plans[${index}].displayOrder must be an integer`);
1104
+ }
1105
+ }
1106
+ function validatePublishedCatalog(catalog) {
1107
+ if (catalog === null || typeof catalog !== "object") {
1108
+ throw malformed(
1109
+ `expected object, got ${catalog === null ? "null" : typeof catalog}`
1110
+ );
1111
+ }
1112
+ const c = catalog;
1113
+ const service = c.service;
1114
+ if (service === null || typeof service !== "object") {
1115
+ throw malformed("service must be an object");
1116
+ }
1117
+ const s = service;
1118
+ if (isBlankString(s.code)) {
1119
+ throw malformed("service.code must be a non-empty string");
1120
+ }
1121
+ if (isBlankString(s.name)) {
1122
+ throw malformed("service.name must be a non-empty string");
1123
+ }
1124
+ if (isBlankString(c.catalogVersion)) {
1125
+ throw malformed("catalogVersion must be a non-empty string");
1126
+ }
1127
+ const publication = c.publication;
1128
+ if (publication === null || typeof publication !== "object") {
1129
+ throw malformed("publication must be an object");
1130
+ }
1131
+ const p = publication;
1132
+ if (isBlankString(p.serviceCode)) {
1133
+ throw malformed("publication.serviceCode must be a non-empty string");
1134
+ }
1135
+ if (typeof p.environment !== "string" || !CATALOG_ENVIRONMENTS.has(p.environment)) {
1136
+ throw malformed(
1137
+ `publication.environment must be staging | production (got ${JSON.stringify(p.environment)})`
1138
+ );
1139
+ }
1140
+ if (p.catalogVersion !== c.catalogVersion) {
1141
+ throw malformed(
1142
+ `publication.catalogVersion (${JSON.stringify(p.catalogVersion)}) does not match top-level catalogVersion (${JSON.stringify(c.catalogVersion)})`
1143
+ );
1144
+ }
1145
+ if (typeof p.status !== "string" || !PUBLICATION_STATUSES.has(p.status)) {
1146
+ throw malformed(
1147
+ `publication.status must be one of approved | superseded | revoking | revoked (got ${JSON.stringify(p.status)})`
1148
+ );
1149
+ }
1150
+ for (const field of PUBLICATION_HASH_FIELDS) {
1151
+ const value = p[field];
1152
+ if (typeof value !== "string" || !CATALOG_HASH_PATTERN.test(value)) {
1153
+ throw malformed(`publication.${field} must be lowercase SHA-256 hex (64 chars)`);
1154
+ }
1155
+ }
1156
+ if (typeof p.salesEpoch !== "number" || !Number.isInteger(p.salesEpoch) || p.salesEpoch < 1) {
1157
+ throw malformed(
1158
+ `publication.salesEpoch must be a positive integer (got ${JSON.stringify(p.salesEpoch)})`
1159
+ );
1160
+ }
1161
+ if (isBlankString(p.approvalId)) {
1162
+ throw malformed("publication.approvalId must be a non-empty string");
1163
+ }
1164
+ for (const field of PUBLICATION_REQUIRED_TIMESTAMP_FIELDS) {
1165
+ if (isBlankString(p[field])) {
1166
+ throw malformed(`publication.${field} must be a non-empty ISO 8601 string`);
1167
+ }
1168
+ }
1169
+ for (const field of PUBLICATION_NULLABLE_TIMESTAMP_FIELDS) {
1170
+ if (!isNullableIsoString(p[field])) {
1171
+ throw malformed(`publication.${field} must be a non-empty ISO 8601 string or null`);
1172
+ }
1173
+ }
1174
+ if (!Array.isArray(c.plans)) {
1175
+ throw malformed(`plans must be an array (got ${typeof c.plans})`);
1176
+ }
1177
+ for (const [index, plan] of c.plans.entries()) {
1178
+ validatePublishedPlan(plan, index);
1179
+ }
1180
+ }
1181
+ function createCatalogMethods(deps) {
1182
+ const { fetchWithAuth, createError } = deps;
1183
+ async function getPublishedCatalog(serviceCode) {
1184
+ if (isBlankString(serviceCode)) {
1185
+ return {
1186
+ error: createError("VALIDATION_ERROR", "serviceCode \u306F\u5FC5\u9808\u3067\u3059")
1187
+ };
1188
+ }
1189
+ const requestedCode = serviceCode.trim();
1190
+ const response = await fetchWithAuth(
1191
+ `${EXT_CATALOG_ENDPOINT}/${encodeURIComponent(requestedCode)}`
1192
+ );
1193
+ if (response.data !== void 0) {
1194
+ validatePublishedCatalog(response.data);
1195
+ if (response.data.service.code !== requestedCode || response.data.publication.serviceCode !== requestedCode) {
1196
+ throw malformed(
1197
+ `response is for service ${JSON.stringify(response.data.service.code)} (publication: ${JSON.stringify(response.data.publication.serviceCode)}) but ${JSON.stringify(requestedCode)} was requested`
1198
+ );
1199
+ }
1200
+ }
1201
+ return response;
1202
+ }
1203
+ return { getPublishedCatalog };
1204
+ }
1205
+
832
1206
  // src/client/members-methods.ts
833
1207
  var EXT_MEMBERS_ENDPOINT = "/api/v1/organizations/ext/members";
834
1208
  function createMembersMethods(deps) {
@@ -1271,7 +1645,7 @@ function validateTokenResponse(tokenResponse) {
1271
1645
  }
1272
1646
 
1273
1647
  // src/client/version-check.ts
1274
- var SDK_VERSION = "5.25.0";
1648
+ var SDK_VERSION = "5.28.0";
1275
1649
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
1276
1650
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
1277
1651
  function sdkHeaders() {
@@ -2778,6 +3152,11 @@ function createInquiryMethods(deps) {
2778
3152
  name,
2779
3153
  message,
2780
3154
  category: params.category,
3155
+ // Normalize null ("survey not collected", e.g. forwarded verbatim
3156
+ // from FFIDInquiryFormSubmitData) to undefined so JSON.stringify
3157
+ // drops the key entirely — older FFID deployments never see it and
3158
+ // current ones treat the omission as NULL.
3159
+ acquisitionSource: params.acquisitionSource ?? void 0,
2781
3160
  company: params.company,
2782
3161
  phone: params.phone,
2783
3162
  locale: params.locale,
@@ -2885,39 +3264,7 @@ function isBlockedFromUserinfo(sub) {
2885
3264
  return BLOCKING_EFFECTIVE_STATUSES.includes(effectiveStatus);
2886
3265
  }
2887
3266
 
2888
- // src/client/error-codes.ts
2889
- var FFID_ERROR_CODES = {
2890
- NETWORK_ERROR: "NETWORK_ERROR",
2891
- PARSE_ERROR: "PARSE_ERROR",
2892
- UNKNOWN_ERROR: "UNKNOWN_ERROR",
2893
- TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
2894
- TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
2895
- NO_TOKENS: "NO_TOKENS",
2896
- TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR",
2897
- TOKEN_EXPIRED: "TOKEN_EXPIRED",
2898
- STATE_MISMATCH_ERROR: "STATE_MISMATCH_ERROR"
2899
- };
2900
-
2901
- // src/client/ffid-client.ts
2902
- var UNAUTHORIZED_STATUS2 = 401;
2903
- var SDK_LOG_PREFIX = "[FFID SDK]";
2904
- var noopLogger = {
2905
- debug: () => {
2906
- },
2907
- info: () => {
2908
- },
2909
- warn: () => {
2910
- },
2911
- error: () => {
2912
- }
2913
- };
2914
- var consoleLogger = {
2915
- debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
2916
- info: (...args) => console.info(SDK_LOG_PREFIX, ...args),
2917
- warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
2918
- error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
2919
- };
2920
- var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
3267
+ // src/client/service-access-decision.ts
2921
3268
  var DEFAULT_ALLOW_GRACE = true;
2922
3269
  var DEFAULT_SERVICE_ACCESS_FAIL_POLICY = "failClosed";
2923
3270
  var SERVICE_ACCESS_DENIED_CODE = "SERVICE_ACCESS_DENIED";
@@ -2984,6 +3331,40 @@ function failClosedServiceAccessDecision(params, error) {
2984
3331
  error
2985
3332
  };
2986
3333
  }
3334
+
3335
+ // src/client/error-codes.ts
3336
+ var FFID_ERROR_CODES = {
3337
+ NETWORK_ERROR: "NETWORK_ERROR",
3338
+ PARSE_ERROR: "PARSE_ERROR",
3339
+ UNKNOWN_ERROR: "UNKNOWN_ERROR",
3340
+ TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
3341
+ TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
3342
+ NO_TOKENS: "NO_TOKENS",
3343
+ TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR",
3344
+ TOKEN_EXPIRED: "TOKEN_EXPIRED",
3345
+ STATE_MISMATCH_ERROR: "STATE_MISMATCH_ERROR"
3346
+ };
3347
+
3348
+ // src/client/ffid-client.ts
3349
+ var UNAUTHORIZED_STATUS2 = 401;
3350
+ var SDK_LOG_PREFIX = "[FFID SDK]";
3351
+ var noopLogger = {
3352
+ debug: () => {
3353
+ },
3354
+ info: () => {
3355
+ },
3356
+ warn: () => {
3357
+ },
3358
+ error: () => {
3359
+ }
3360
+ };
3361
+ var consoleLogger = {
3362
+ debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
3363
+ info: (...args) => console.info(SDK_LOG_PREFIX, ...args),
3364
+ warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
3365
+ error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
3366
+ };
3367
+ var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
2987
3368
  function resolveRedirectUri(raw, logger) {
2988
3369
  if (raw === null) return null;
2989
3370
  try {
@@ -3301,6 +3682,10 @@ function createFFIDClient(config) {
3301
3682
  fetchWithAuth,
3302
3683
  createError
3303
3684
  });
3685
+ const { getPublishedCatalog } = createCatalogMethods({
3686
+ fetchWithAuth,
3687
+ createError
3688
+ });
3304
3689
  const { listMembers, addMember, updateMemberRole, removeMember } = createMembersMethods({
3305
3690
  fetchWithAuth,
3306
3691
  createError,
@@ -3427,6 +3812,7 @@ function createFFIDClient(config) {
3427
3812
  assignSeats,
3428
3813
  unassignSeat,
3429
3814
  listPlans,
3815
+ getPublishedCatalog,
3430
3816
  getSubscription,
3431
3817
  subscribe,
3432
3818
  changePlan,
@@ -4981,6 +5367,19 @@ var FFID_INQUIRY_CATEGORIES_SITE_2026 = [
4981
5367
  "other"
4982
5368
  ];
4983
5369
  var isFFIDInquiryCategorySite2026 = (value) => FFID_INQUIRY_CATEGORIES_SITE_2026.includes(value);
5370
+ var FFID_INQUIRY_ACQUISITION_SOURCES = [
5371
+ "referral",
5372
+ "generative-ai",
5373
+ "social-media",
5374
+ "networking-event",
5375
+ "web-search",
5376
+ "company-blog",
5377
+ "sales-outreach",
5378
+ "press-release",
5379
+ "web-advertising",
5380
+ "other"
5381
+ ];
5382
+ var isFFIDInquiryAcquisitionSource = (value) => FFID_INQUIRY_ACQUISITION_SOURCES.includes(value);
4984
5383
  var CATEGORY_PLACEHOLDER_LABEL_JA = "\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044";
4985
5384
  var CATEGORY_REQUIRED_ERROR_JA = "\u304A\u554F\u3044\u5408\u308F\u305B\u7A2E\u5225\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002";
4986
5385
  var TERMS_REQUIRED_ERROR_JA = "\u5229\u7528\u898F\u7D04\u3078\u306E\u540C\u610F\u304C\u5FC5\u8981\u3067\u3059\u3002";
@@ -5113,6 +5512,7 @@ function FFIDInquiryForm({
5113
5512
  termsHref,
5114
5513
  privacyHref,
5115
5514
  turnstileToken = null,
5515
+ acquisitionSource = null,
5116
5516
  turnstileSlot,
5117
5517
  onSubmit,
5118
5518
  onChange,
@@ -5194,6 +5594,7 @@ function FFIDInquiryForm({
5194
5594
  company: company.trim() || null,
5195
5595
  phone: phone.trim() || null,
5196
5596
  category,
5597
+ acquisitionSource,
5197
5598
  message: message.trim(),
5198
5599
  organizationId,
5199
5600
  inquiryFollowupOptIn: inquiryFollowup,
@@ -5209,6 +5610,7 @@ function FFIDInquiryForm({
5209
5610
  company,
5210
5611
  phone,
5211
5612
  category,
5613
+ acquisitionSource,
5212
5614
  message,
5213
5615
  organizationId,
5214
5616
  inquiryFollowup,
@@ -5257,6 +5659,7 @@ function FFIDInquiryForm({
5257
5659
  company: company.trim() || null,
5258
5660
  phone: phone.trim() || null,
5259
5661
  category,
5662
+ acquisitionSource,
5260
5663
  message: message.trim(),
5261
5664
  organizationId,
5262
5665
  inquiryFollowupOptIn: inquiryFollowup,
@@ -5577,11 +5980,13 @@ function FFIDInquiryForm({
5577
5980
 
5578
5981
  exports.ACCESS_GRANTING_EFFECTIVE_STATUSES = ACCESS_GRANTING_EFFECTIVE_STATUSES;
5579
5982
  exports.BLOCKING_EFFECTIVE_STATUSES = BLOCKING_EFFECTIVE_STATUSES;
5983
+ exports.CATALOG_HASH_PATTERN = CATALOG_HASH_PATTERN;
5580
5984
  exports.DEFAULT_API_BASE_URL = DEFAULT_API_BASE_URL;
5581
5985
  exports.DEFAULT_OAUTH_SCOPES = DEFAULT_OAUTH_SCOPES;
5582
5986
  exports.EFFECTIVE_SUBSCRIPTION_STATUSES = EFFECTIVE_SUBSCRIPTION_STATUSES;
5583
5987
  exports.FFIDAnnouncementBadge = FFIDAnnouncementBadge;
5584
5988
  exports.FFIDAnnouncementList = FFIDAnnouncementList;
5989
+ exports.FFIDCatalogHashError = FFIDCatalogHashError;
5585
5990
  exports.FFIDInquiryForm = FFIDInquiryForm;
5586
5991
  exports.FFIDLoginButton = FFIDLoginButton;
5587
5992
  exports.FFIDOrganizationSwitcher = FFIDOrganizationSwitcher;
@@ -5590,11 +5995,21 @@ exports.FFIDSDKError = FFIDSDKError;
5590
5995
  exports.FFIDSubscriptionBadge = FFIDSubscriptionBadge;
5591
5996
  exports.FFIDUserMenu = FFIDUserMenu;
5592
5997
  exports.FFID_ANNOUNCEMENTS_ERROR_CODES = FFID_ANNOUNCEMENTS_ERROR_CODES;
5998
+ exports.FFID_CATALOG_ENVIRONMENTS = FFID_CATALOG_ENVIRONMENTS;
5999
+ exports.FFID_CATALOG_PUBLICATION_STATUSES = FFID_CATALOG_PUBLICATION_STATUSES;
5593
6000
  exports.FFID_ERROR_CODES = FFID_ERROR_CODES;
6001
+ exports.FFID_INQUIRY_ACQUISITION_SOURCES = FFID_INQUIRY_ACQUISITION_SOURCES;
5594
6002
  exports.FFID_INQUIRY_CATEGORIES = FFID_INQUIRY_CATEGORIES;
5595
6003
  exports.FFID_INQUIRY_CATEGORIES_SITE_2026 = FFID_INQUIRY_CATEGORIES_SITE_2026;
6004
+ exports.canonicalizeCatalogValue = canonicalizeCatalogValue;
5596
6005
  exports.cleanupStateStorage = cleanupStateStorage;
5597
6006
  exports.computeEffectiveStatusFromSession = computeEffectiveStatusFromSession;
6007
+ exports.computeFFIDCheckoutCopyHash = computeFFIDCheckoutCopyHash;
6008
+ exports.computeLegalDisclosureHash = computeLegalDisclosureHash;
6009
+ exports.computePaymentConfigurationHash = computePaymentConfigurationHash;
6010
+ exports.computePraxisCopyHash = computePraxisCopyHash;
6011
+ exports.computeScopeHash = computeScopeHash;
6012
+ exports.computeTaxConfigurationHash = computeTaxConfigurationHash;
5598
6013
  exports.createFFIDAnnouncementsClient = createFFIDAnnouncementsClient;
5599
6014
  exports.createFFIDClient = createFFIDClient;
5600
6015
  exports.createTokenStore = createTokenStore;
@@ -5602,15 +6017,19 @@ exports.generateCodeChallenge = generateCodeChallenge;
5602
6017
  exports.generateCodeVerifier = generateCodeVerifier;
5603
6018
  exports.handleRedirectCallback = handleRedirectCallback;
5604
6019
  exports.hasAccessFromUserinfo = hasAccessFromUserinfo;
6020
+ exports.hashCanonicalJson = hashCanonicalJson;
5605
6021
  exports.isBlockedFromUserinfo = isBlockedFromUserinfo;
6022
+ exports.isFFIDInquiryAcquisitionSource = isFFIDInquiryAcquisitionSource;
5606
6023
  exports.isFFIDInquiryCategorySite2026 = isFFIDInquiryCategorySite2026;
5607
6024
  exports.normalizeRedirectUri = normalizeRedirectUri;
5608
6025
  exports.retrieveCodeVerifier = retrieveCodeVerifier;
5609
6026
  exports.retrieveState = retrieveState;
6027
+ exports.sha256Hex = sha256Hex;
5610
6028
  exports.storeCodeVerifier = storeCodeVerifier;
5611
6029
  exports.storeState = storeState;
5612
6030
  exports.useFFID = useFFID;
5613
6031
  exports.useFFIDAnnouncements = useFFIDAnnouncements;
5614
6032
  exports.useFFIDContext = useFFIDContext;
5615
6033
  exports.useSubscription = useSubscription;
6034
+ exports.validatePublishedCatalog = validatePublishedCatalog;
5616
6035
  exports.withSubscription = withSubscription;
@@ -1,34 +1,34 @@
1
1
  'use strict';
2
2
 
3
- var chunkZFKXOTFF_cjs = require('../chunk-ZFKXOTFF.cjs');
3
+ var chunkS5FV6H2U_cjs = require('../chunk-S5FV6H2U.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
8
8
  enumerable: true,
9
- get: function () { return chunkZFKXOTFF_cjs.FFIDAnnouncementBadge; }
9
+ get: function () { return chunkS5FV6H2U_cjs.FFIDAnnouncementBadge; }
10
10
  });
11
11
  Object.defineProperty(exports, "FFIDAnnouncementList", {
12
12
  enumerable: true,
13
- get: function () { return chunkZFKXOTFF_cjs.FFIDAnnouncementList; }
13
+ get: function () { return chunkS5FV6H2U_cjs.FFIDAnnouncementList; }
14
14
  });
15
15
  Object.defineProperty(exports, "FFIDInquiryForm", {
16
16
  enumerable: true,
17
- get: function () { return chunkZFKXOTFF_cjs.FFIDInquiryForm; }
17
+ get: function () { return chunkS5FV6H2U_cjs.FFIDInquiryForm; }
18
18
  });
19
19
  Object.defineProperty(exports, "FFIDLoginButton", {
20
20
  enumerable: true,
21
- get: function () { return chunkZFKXOTFF_cjs.FFIDLoginButton; }
21
+ get: function () { return chunkS5FV6H2U_cjs.FFIDLoginButton; }
22
22
  });
23
23
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
24
24
  enumerable: true,
25
- get: function () { return chunkZFKXOTFF_cjs.FFIDOrganizationSwitcher; }
25
+ get: function () { return chunkS5FV6H2U_cjs.FFIDOrganizationSwitcher; }
26
26
  });
27
27
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
28
28
  enumerable: true,
29
- get: function () { return chunkZFKXOTFF_cjs.FFIDSubscriptionBadge; }
29
+ get: function () { return chunkS5FV6H2U_cjs.FFIDSubscriptionBadge; }
30
30
  });
31
31
  Object.defineProperty(exports, "FFIDUserMenu", {
32
32
  enumerable: true,
33
- get: function () { return chunkZFKXOTFF_cjs.FFIDUserMenu; }
33
+ get: function () { return chunkS5FV6H2U_cjs.FFIDUserMenu; }
34
34
  });
@@ -1,3 +1,3 @@
1
- export { G as FFIDAnnouncementBadge, ah as FFIDAnnouncementBadgeClassNames, ai as FFIDAnnouncementBadgeProps, H as FFIDAnnouncementList, aj as FFIDAnnouncementListClassNames, ak as FFIDAnnouncementListProps, R as FFIDInquiryForm, S as FFIDInquiryFormCategoryItem, U as FFIDInquiryFormClassNames, V as FFIDInquiryFormLegalLayout, W as FFIDInquiryFormOrganization, X as FFIDInquiryFormPlaceholderContext, Y as FFIDInquiryFormPrefill, Z as FFIDInquiryFormProps, _ as FFIDInquiryFormSubmitData, $ as FFIDInquiryFormSubmitResult, a1 as FFIDLoginButton, al as FFIDLoginButtonProps, a3 as FFIDOrganizationSwitcher, am as FFIDOrganizationSwitcherClassNames, an as FFIDOrganizationSwitcherProps, a7 as FFIDSubscriptionBadge, ao as FFIDSubscriptionBadgeClassNames, ap as FFIDSubscriptionBadgeProps, a8 as FFIDUserMenu, aq as FFIDUserMenuClassNames, ar as FFIDUserMenuProps } from '../index-Dd6CMIBd.cjs';
1
+ export { G as FFIDAnnouncementBadge, am as FFIDAnnouncementBadgeClassNames, an as FFIDAnnouncementBadgeProps, H as FFIDAnnouncementList, ao as FFIDAnnouncementListClassNames, ap as FFIDAnnouncementListProps, S as FFIDInquiryForm, U as FFIDInquiryFormCategoryItem, V as FFIDInquiryFormClassNames, W as FFIDInquiryFormLegalLayout, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a2 as FFIDLoginButton, aq as FFIDLoginButtonProps, a4 as FFIDOrganizationSwitcher, ar as FFIDOrganizationSwitcherClassNames, as as FFIDOrganizationSwitcherProps, aa as FFIDSubscriptionBadge, at as FFIDSubscriptionBadgeClassNames, au as FFIDSubscriptionBadgeProps, ab as FFIDUserMenu, av as FFIDUserMenuClassNames, aw as FFIDUserMenuProps } from '../index-_-MqJCN3.cjs';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1,3 +1,3 @@
1
- export { G as FFIDAnnouncementBadge, ah as FFIDAnnouncementBadgeClassNames, ai as FFIDAnnouncementBadgeProps, H as FFIDAnnouncementList, aj as FFIDAnnouncementListClassNames, ak as FFIDAnnouncementListProps, R as FFIDInquiryForm, S as FFIDInquiryFormCategoryItem, U as FFIDInquiryFormClassNames, V as FFIDInquiryFormLegalLayout, W as FFIDInquiryFormOrganization, X as FFIDInquiryFormPlaceholderContext, Y as FFIDInquiryFormPrefill, Z as FFIDInquiryFormProps, _ as FFIDInquiryFormSubmitData, $ as FFIDInquiryFormSubmitResult, a1 as FFIDLoginButton, al as FFIDLoginButtonProps, a3 as FFIDOrganizationSwitcher, am as FFIDOrganizationSwitcherClassNames, an as FFIDOrganizationSwitcherProps, a7 as FFIDSubscriptionBadge, ao as FFIDSubscriptionBadgeClassNames, ap as FFIDSubscriptionBadgeProps, a8 as FFIDUserMenu, aq as FFIDUserMenuClassNames, ar as FFIDUserMenuProps } from '../index-Dd6CMIBd.js';
1
+ export { G as FFIDAnnouncementBadge, am as FFIDAnnouncementBadgeClassNames, an as FFIDAnnouncementBadgeProps, H as FFIDAnnouncementList, ao as FFIDAnnouncementListClassNames, ap as FFIDAnnouncementListProps, S as FFIDInquiryForm, U as FFIDInquiryFormCategoryItem, V as FFIDInquiryFormClassNames, W as FFIDInquiryFormLegalLayout, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a2 as FFIDLoginButton, aq as FFIDLoginButtonProps, a4 as FFIDOrganizationSwitcher, ar as FFIDOrganizationSwitcherClassNames, as as FFIDOrganizationSwitcherProps, aa as FFIDSubscriptionBadge, at as FFIDSubscriptionBadgeClassNames, au as FFIDSubscriptionBadgeProps, ab as FFIDUserMenu, av as FFIDUserMenuClassNames, aw as FFIDUserMenuProps } from '../index-_-MqJCN3.js';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';