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