@feelflow/ffid-sdk 8.1.0 → 10.0.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/README.md +1010 -960
- package/dist/{FFIDCookieLink-Bwr0FBrV.d.cts → FFIDCookieLink-yt5SUr-W.d.cts} +47 -3
- package/dist/{FFIDCookieLink-Bwr0FBrV.d.ts → FFIDCookieLink-yt5SUr-W.d.ts} +47 -3
- package/dist/{chunk-CPXTIRB6.cjs → chunk-2LU2F3F2.cjs} +283 -11
- package/dist/{chunk-P5A6C5SK.js → chunk-OXW6QSTO.js} +270 -12
- package/dist/{chunk-XELXXL2F.cjs → chunk-RZCQ22GH.cjs} +29 -9
- package/dist/{chunk-RHBZHSRD.js → chunk-S2AEBJRX.js} +29 -9
- package/dist/components/index.cjs +8 -8
- package/dist/components/index.d.cts +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +1 -1
- package/dist/consent/index.cjs +63 -63
- package/dist/consent/index.d.cts +2 -2
- package/dist/consent/index.d.ts +2 -2
- package/dist/consent/index.js +1 -1
- package/dist/{ffid-client-B0cORHac.d.ts → ffid-client-5uxXRgIO.d.ts} +686 -376
- package/dist/{ffid-client-6aLs9Fqc.d.cts → ffid-client-CumygXXz.d.cts} +686 -376
- package/dist/{index-BG7g99pK.d.ts → index-KjewcNn1.d.cts} +813 -2
- package/dist/{index-BG7g99pK.d.cts → index-KjewcNn1.d.ts} +813 -2
- package/dist/index.cjs +151 -95
- package/dist/index.d.cts +116 -505
- package/dist/index.d.ts +116 -505
- package/dist/index.js +3 -3
- package/dist/server/index.cjs +253 -11
- package/dist/server/index.d.cts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +253 -11
- package/dist/server/test/index.d.cts +1 -1
- package/dist/server/test/index.d.ts +1 -1
- package/dist/webhooks/index.d.cts +112 -7
- package/dist/webhooks/index.d.ts +112 -7
- package/package.json +131 -131
|
@@ -987,10 +987,23 @@ interface FFIDCookieBannerProps {
|
|
|
987
987
|
* `useFFIDConsent()` instead of overriding the label.
|
|
988
988
|
*/
|
|
989
989
|
closeLabel?: ReactNode;
|
|
990
|
+
/**
|
|
991
|
+
* Accessible name of the banner dialog (`aria-label`). Default: "Cookie 同意".
|
|
992
|
+
*
|
|
993
|
+
* Screen readers announce this before any visible copy, so localize it together with
|
|
994
|
+
* `message` and the button labels. Pass a non-empty string: an empty `aria-label`
|
|
995
|
+
* leaves the dialog without an accessible name.
|
|
996
|
+
*/
|
|
997
|
+
ariaLabel?: string;
|
|
998
|
+
/**
|
|
999
|
+
* Label of the preferences button while the preference center is loading.
|
|
1000
|
+
* Default: "読み込み中...".
|
|
1001
|
+
*/
|
|
1002
|
+
preferencesLoadingLabel?: ReactNode;
|
|
990
1003
|
/** Custom root style. */
|
|
991
1004
|
style?: CSSProperties;
|
|
992
1005
|
}
|
|
993
|
-
declare function FFIDCookieBanner({ className, classNames, message, acceptAllLabel, necessaryOnlyLabel, preferencesLabel, closeLabel, style, }: FFIDCookieBannerProps): React.JSX.Element | null;
|
|
1006
|
+
declare function FFIDCookieBanner({ className, classNames, message, acceptAllLabel, necessaryOnlyLabel, preferencesLabel, closeLabel, ariaLabel, preferencesLoadingLabel, style, }: FFIDCookieBannerProps): React.JSX.Element | null;
|
|
994
1007
|
|
|
995
1008
|
/**
|
|
996
1009
|
* `<FFIDCookieSettings>` — preference center modal.
|
|
@@ -1001,6 +1014,7 @@ declare function FFIDCookieBanner({ className, classNames, message, acceptAllLab
|
|
|
1001
1014
|
*
|
|
1002
1015
|
* Refs: docs/02-design/COOKIE_CONSENT.md §6.2 + §6.3
|
|
1003
1016
|
*/
|
|
1017
|
+
|
|
1004
1018
|
interface FFIDCookieSettingsClassNames {
|
|
1005
1019
|
root?: string;
|
|
1006
1020
|
backdrop?: string;
|
|
@@ -1014,10 +1028,40 @@ interface FFIDCookieSettingsProps {
|
|
|
1014
1028
|
classNames?: FFIDCookieSettingsClassNames;
|
|
1015
1029
|
/** Open/close override; defaults to the context's `isPreferencesOpen`. */
|
|
1016
1030
|
open?: boolean;
|
|
1017
|
-
/** When true, also expose
|
|
1031
|
+
/** When true, also expose the withdraw-all button (label: `withdrawLabel`). Default: true. */
|
|
1018
1032
|
showWithdrawButton?: boolean;
|
|
1033
|
+
/**
|
|
1034
|
+
* Heading of the modal. Default: "Cookie 設定". Also the default accessible name of
|
|
1035
|
+
* the dialog unless `ariaLabel` is given, so pass a non-empty string: `title=""`
|
|
1036
|
+
* without `ariaLabel` leaves the dialog without an accessible name.
|
|
1037
|
+
*/
|
|
1038
|
+
title?: string;
|
|
1039
|
+
/**
|
|
1040
|
+
* Accessible name of the dialog (`aria-label`). Default: `title`, then "Cookie 設定".
|
|
1041
|
+
* Pass a non-empty string: an empty `aria-label` leaves the dialog without an
|
|
1042
|
+
* accessible name.
|
|
1043
|
+
*/
|
|
1044
|
+
ariaLabel?: string;
|
|
1045
|
+
/** Save button label. Default: "変更を保存". */
|
|
1046
|
+
saveLabel?: ReactNode;
|
|
1047
|
+
/** Save button label while the save is in flight. Default: "保存中...". */
|
|
1048
|
+
savingLabel?: ReactNode;
|
|
1049
|
+
/** Withdraw button label. Default: "すべて撤回". */
|
|
1050
|
+
withdrawLabel?: ReactNode;
|
|
1051
|
+
/** Withdraw button label while the withdrawal is in flight. Default: "撤回中...". */
|
|
1052
|
+
withdrawingLabel?: ReactNode;
|
|
1053
|
+
/**
|
|
1054
|
+
* Category names keyed by category code. Each given entry wins over both the category
|
|
1055
|
+
* master fetched from FFID and the built-in fallback shown before the master loads.
|
|
1056
|
+
* Missing codes keep those. Under `FFIDAnalyticsProvider` the master is always Japanese
|
|
1057
|
+
* (its consent client keeps the default `defaultLang: 'ja'`), so non-Japanese pages
|
|
1058
|
+
* need this prop.
|
|
1059
|
+
*/
|
|
1060
|
+
categoryLabels?: Partial<Record<FFIDConsentCategoryCode, string>>;
|
|
1061
|
+
/** Category descriptions keyed by category code. Same precedence as `categoryLabels`. */
|
|
1062
|
+
categoryDescriptions?: Partial<Record<FFIDConsentCategoryCode, string>>;
|
|
1019
1063
|
}
|
|
1020
|
-
declare function FFIDCookieSettings({ className, classNames, open, showWithdrawButton, }: FFIDCookieSettingsProps): React.JSX.Element | null;
|
|
1064
|
+
declare function FFIDCookieSettings({ className, classNames, open, showWithdrawButton, title, ariaLabel, saveLabel, savingLabel, withdrawLabel, withdrawingLabel, categoryLabels, categoryDescriptions, }: FFIDCookieSettingsProps): React.JSX.Element | null;
|
|
1021
1065
|
|
|
1022
1066
|
/**
|
|
1023
1067
|
* `<FFIDCookieLink>` — footer link that opens the preference center.
|
|
@@ -987,10 +987,23 @@ interface FFIDCookieBannerProps {
|
|
|
987
987
|
* `useFFIDConsent()` instead of overriding the label.
|
|
988
988
|
*/
|
|
989
989
|
closeLabel?: ReactNode;
|
|
990
|
+
/**
|
|
991
|
+
* Accessible name of the banner dialog (`aria-label`). Default: "Cookie 同意".
|
|
992
|
+
*
|
|
993
|
+
* Screen readers announce this before any visible copy, so localize it together with
|
|
994
|
+
* `message` and the button labels. Pass a non-empty string: an empty `aria-label`
|
|
995
|
+
* leaves the dialog without an accessible name.
|
|
996
|
+
*/
|
|
997
|
+
ariaLabel?: string;
|
|
998
|
+
/**
|
|
999
|
+
* Label of the preferences button while the preference center is loading.
|
|
1000
|
+
* Default: "読み込み中...".
|
|
1001
|
+
*/
|
|
1002
|
+
preferencesLoadingLabel?: ReactNode;
|
|
990
1003
|
/** Custom root style. */
|
|
991
1004
|
style?: CSSProperties;
|
|
992
1005
|
}
|
|
993
|
-
declare function FFIDCookieBanner({ className, classNames, message, acceptAllLabel, necessaryOnlyLabel, preferencesLabel, closeLabel, style, }: FFIDCookieBannerProps): React.JSX.Element | null;
|
|
1006
|
+
declare function FFIDCookieBanner({ className, classNames, message, acceptAllLabel, necessaryOnlyLabel, preferencesLabel, closeLabel, ariaLabel, preferencesLoadingLabel, style, }: FFIDCookieBannerProps): React.JSX.Element | null;
|
|
994
1007
|
|
|
995
1008
|
/**
|
|
996
1009
|
* `<FFIDCookieSettings>` — preference center modal.
|
|
@@ -1001,6 +1014,7 @@ declare function FFIDCookieBanner({ className, classNames, message, acceptAllLab
|
|
|
1001
1014
|
*
|
|
1002
1015
|
* Refs: docs/02-design/COOKIE_CONSENT.md §6.2 + §6.3
|
|
1003
1016
|
*/
|
|
1017
|
+
|
|
1004
1018
|
interface FFIDCookieSettingsClassNames {
|
|
1005
1019
|
root?: string;
|
|
1006
1020
|
backdrop?: string;
|
|
@@ -1014,10 +1028,40 @@ interface FFIDCookieSettingsProps {
|
|
|
1014
1028
|
classNames?: FFIDCookieSettingsClassNames;
|
|
1015
1029
|
/** Open/close override; defaults to the context's `isPreferencesOpen`. */
|
|
1016
1030
|
open?: boolean;
|
|
1017
|
-
/** When true, also expose
|
|
1031
|
+
/** When true, also expose the withdraw-all button (label: `withdrawLabel`). Default: true. */
|
|
1018
1032
|
showWithdrawButton?: boolean;
|
|
1033
|
+
/**
|
|
1034
|
+
* Heading of the modal. Default: "Cookie 設定". Also the default accessible name of
|
|
1035
|
+
* the dialog unless `ariaLabel` is given, so pass a non-empty string: `title=""`
|
|
1036
|
+
* without `ariaLabel` leaves the dialog without an accessible name.
|
|
1037
|
+
*/
|
|
1038
|
+
title?: string;
|
|
1039
|
+
/**
|
|
1040
|
+
* Accessible name of the dialog (`aria-label`). Default: `title`, then "Cookie 設定".
|
|
1041
|
+
* Pass a non-empty string: an empty `aria-label` leaves the dialog without an
|
|
1042
|
+
* accessible name.
|
|
1043
|
+
*/
|
|
1044
|
+
ariaLabel?: string;
|
|
1045
|
+
/** Save button label. Default: "変更を保存". */
|
|
1046
|
+
saveLabel?: ReactNode;
|
|
1047
|
+
/** Save button label while the save is in flight. Default: "保存中...". */
|
|
1048
|
+
savingLabel?: ReactNode;
|
|
1049
|
+
/** Withdraw button label. Default: "すべて撤回". */
|
|
1050
|
+
withdrawLabel?: ReactNode;
|
|
1051
|
+
/** Withdraw button label while the withdrawal is in flight. Default: "撤回中...". */
|
|
1052
|
+
withdrawingLabel?: ReactNode;
|
|
1053
|
+
/**
|
|
1054
|
+
* Category names keyed by category code. Each given entry wins over both the category
|
|
1055
|
+
* master fetched from FFID and the built-in fallback shown before the master loads.
|
|
1056
|
+
* Missing codes keep those. Under `FFIDAnalyticsProvider` the master is always Japanese
|
|
1057
|
+
* (its consent client keeps the default `defaultLang: 'ja'`), so non-Japanese pages
|
|
1058
|
+
* need this prop.
|
|
1059
|
+
*/
|
|
1060
|
+
categoryLabels?: Partial<Record<FFIDConsentCategoryCode, string>>;
|
|
1061
|
+
/** Category descriptions keyed by category code. Same precedence as `categoryLabels`. */
|
|
1062
|
+
categoryDescriptions?: Partial<Record<FFIDConsentCategoryCode, string>>;
|
|
1019
1063
|
}
|
|
1020
|
-
declare function FFIDCookieSettings({ className, classNames, open, showWithdrawButton, }: FFIDCookieSettingsProps): React.JSX.Element | null;
|
|
1064
|
+
declare function FFIDCookieSettings({ className, classNames, open, showWithdrawButton, title, ariaLabel, saveLabel, savingLabel, withdrawLabel, withdrawingLabel, categoryLabels, categoryDescriptions, }: FFIDCookieSettingsProps): React.JSX.Element | null;
|
|
1021
1065
|
|
|
1022
1066
|
/**
|
|
1023
1067
|
* `<FFIDCookieLink>` — footer link that opens the preference center.
|
|
@@ -228,18 +228,16 @@ function mapUserinfoSubscriptionToSession(userinfo, serviceCode) {
|
|
|
228
228
|
];
|
|
229
229
|
}
|
|
230
230
|
var JWKS_ENDPOINT = "/.well-known/jwks.json";
|
|
231
|
-
var LEGACY_JWT_ISSUER = "https://id.feelflow.net";
|
|
232
231
|
var JWT_ALGORITHM = "ES256";
|
|
233
232
|
var JWT_CLOCK_TOLERANCE_SECONDS = 30;
|
|
234
|
-
function
|
|
235
|
-
|
|
236
|
-
return deployedOrigin === LEGACY_JWT_ISSUER ? [LEGACY_JWT_ISSUER] : [deployedOrigin, LEGACY_JWT_ISSUER];
|
|
233
|
+
function resolveAcceptedIssuer(baseUrl) {
|
|
234
|
+
return new URL(baseUrl).origin;
|
|
237
235
|
}
|
|
238
236
|
function createJwtVerifier(deps) {
|
|
239
237
|
const { baseUrl, serviceCode, logger, createError, errorCodes } = deps;
|
|
240
238
|
const jwksUrl = new URL(JWKS_ENDPOINT, baseUrl);
|
|
241
239
|
const jwks = jose.createRemoteJWKSet(jwksUrl);
|
|
242
|
-
const
|
|
240
|
+
const acceptedIssuer = resolveAcceptedIssuer(baseUrl);
|
|
243
241
|
async function verifyJwt(accessToken) {
|
|
244
242
|
if (!accessToken || !accessToken.trim()) {
|
|
245
243
|
return {
|
|
@@ -252,7 +250,7 @@ function createJwtVerifier(deps) {
|
|
|
252
250
|
try {
|
|
253
251
|
const { payload } = await jose.jwtVerify(accessToken, jwks, {
|
|
254
252
|
algorithms: [JWT_ALGORITHM],
|
|
255
|
-
issuer:
|
|
253
|
+
issuer: acceptedIssuer,
|
|
256
254
|
audience: serviceCode,
|
|
257
255
|
clockTolerance: JWT_CLOCK_TOLERANCE_SECONDS
|
|
258
256
|
});
|
|
@@ -1378,7 +1376,7 @@ var FFID_ORGANIZATION_PROFILE_ERROR_CODES = {
|
|
|
1378
1376
|
};
|
|
1379
1377
|
|
|
1380
1378
|
// src/client/version-check.ts
|
|
1381
|
-
var SDK_VERSION = "
|
|
1379
|
+
var SDK_VERSION = "10.0.0";
|
|
1382
1380
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
1383
1381
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
1384
1382
|
function sdkHeaders() {
|
|
@@ -1734,8 +1732,11 @@ function createProvisioningMethods(deps) {
|
|
|
1734
1732
|
}
|
|
1735
1733
|
|
|
1736
1734
|
// src/client/seat-methods.ts
|
|
1735
|
+
function seatSummaryEndpoint(subscriptionId) {
|
|
1736
|
+
return `/api/v1/subscriptions/ext/${encodeURIComponent(subscriptionId)}/seats`;
|
|
1737
|
+
}
|
|
1737
1738
|
function seatsEndpoint(subscriptionId) {
|
|
1738
|
-
return
|
|
1739
|
+
return `${seatSummaryEndpoint(subscriptionId)}/assignments`;
|
|
1739
1740
|
}
|
|
1740
1741
|
function createSeatMethods(deps) {
|
|
1741
1742
|
const { fetchWithAuth, createError } = deps;
|
|
@@ -1745,6 +1746,12 @@ function createSeatMethods(deps) {
|
|
|
1745
1746
|
}
|
|
1746
1747
|
return fetchWithAuth(seatsEndpoint(params.subscriptionId));
|
|
1747
1748
|
}
|
|
1749
|
+
async function getSeats(subscriptionId) {
|
|
1750
|
+
if (!subscriptionId || !subscriptionId.trim()) {
|
|
1751
|
+
return { error: createError("VALIDATION_ERROR", "subscriptionId \u306F\u5FC5\u9808\u3067\u3059") };
|
|
1752
|
+
}
|
|
1753
|
+
return fetchWithAuth(seatSummaryEndpoint(subscriptionId));
|
|
1754
|
+
}
|
|
1748
1755
|
async function assignSeats(params) {
|
|
1749
1756
|
if (!params.subscriptionId) {
|
|
1750
1757
|
return { error: createError("VALIDATION_ERROR", "subscriptionId \u306F\u5FC5\u9808\u3067\u3059") };
|
|
@@ -1769,7 +1776,7 @@ function createSeatMethods(deps) {
|
|
|
1769
1776
|
{ method: "DELETE" }
|
|
1770
1777
|
);
|
|
1771
1778
|
}
|
|
1772
|
-
return { listSeatAssignments, assignSeats, unassignSeat };
|
|
1779
|
+
return { listSeatAssignments, getSeats, assignSeats, unassignSeat };
|
|
1773
1780
|
}
|
|
1774
1781
|
|
|
1775
1782
|
// src/client/profile-methods.ts
|
|
@@ -3634,6 +3641,225 @@ function createInquiryMethods(deps) {
|
|
|
3634
3641
|
return { create };
|
|
3635
3642
|
}
|
|
3636
3643
|
|
|
3644
|
+
// src/types/credits.ts
|
|
3645
|
+
var FFID_CREDITS_LOW_THRESHOLD_PERCENT = 20;
|
|
3646
|
+
var FFID_CREDITS_CONSUME_MIN_AMOUNT = 1;
|
|
3647
|
+
var FFID_CREDITS_CONSUME_MAX_AMOUNT = 1e6;
|
|
3648
|
+
var FFID_CREDITS_METADATA_MAX_BYTES = 4096;
|
|
3649
|
+
var FFID_CREDITS_HISTORY_MIN_LIMIT = 1;
|
|
3650
|
+
var FFID_CREDITS_HISTORY_MAX_LIMIT = 100;
|
|
3651
|
+
var FFID_CREDITS_HISTORY_DEFAULT_LIMIT = 20;
|
|
3652
|
+
var FFID_CREDIT_PACK_MIN_QUANTITY = 1;
|
|
3653
|
+
var FFID_CREDIT_PACK_MAX_QUANTITY = 10;
|
|
3654
|
+
var FFID_CREDITS_IDEMPOTENCY_KEY_PATTERN = /^[A-Za-z0-9._:-]{8,128}$/;
|
|
3655
|
+
var FFID_CREDITS_FEATURE_PATTERN = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
3656
|
+
var FFID_CREDIT_HISTORY_TYPES = ["grant", "consumption", "revocation"];
|
|
3657
|
+
var FFID_CREDIT_GRANT_KINDS = ["monthly", "manual", "pack"];
|
|
3658
|
+
|
|
3659
|
+
// src/client/credits-methods.ts
|
|
3660
|
+
var EXT_SUBSCRIPTIONS_PATH = "/api/v1/subscriptions/ext";
|
|
3661
|
+
var CREDIT_PACKS_ENDPOINT = `${EXT_SUBSCRIPTIONS_PATH}/credits/packs`;
|
|
3662
|
+
var CREDIT_PACK_CHECKOUT_ENDPOINT = `${CREDIT_PACKS_ENDPOINT}/checkout`;
|
|
3663
|
+
var PORTAL_SUBSCRIPTION_PATH = "/subscriptions";
|
|
3664
|
+
var BUY_CREDITS_QUERY = { key: "purchase", value: "credits" };
|
|
3665
|
+
var ORG_QUERY_KEY = "org";
|
|
3666
|
+
var FFID_CONTRACT_ID_PREFIX = "ffid:";
|
|
3667
|
+
var STRIPE_SUBSCRIPTION_ID_PREFIX = "sub_";
|
|
3668
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3669
|
+
var VALIDATION_ERROR = "VALIDATION_ERROR";
|
|
3670
|
+
var FORBIDDEN = "FORBIDDEN";
|
|
3671
|
+
var utf8Encoder = new TextEncoder();
|
|
3672
|
+
function creditsEndpoint(subscriptionId, action) {
|
|
3673
|
+
return `${EXT_SUBSCRIPTIONS_PATH}/${encodeURIComponent(subscriptionId)}/credits/${action}`;
|
|
3674
|
+
}
|
|
3675
|
+
function isPlainObject2(value) {
|
|
3676
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3677
|
+
}
|
|
3678
|
+
function isIntegerInRange(value, min, max) {
|
|
3679
|
+
return typeof value === "number" && Number.isInteger(value) && value >= min && value <= max;
|
|
3680
|
+
}
|
|
3681
|
+
function createCreditsMethods(deps) {
|
|
3682
|
+
const { fetchWithAuth, createError, authMode, baseUrl, serviceCode, logger } = deps;
|
|
3683
|
+
function validationError(field, message) {
|
|
3684
|
+
return { error: { ...createError(VALIDATION_ERROR, message), details: { field } } };
|
|
3685
|
+
}
|
|
3686
|
+
function forbidden(reason, message) {
|
|
3687
|
+
return { error: { ...createError(FORBIDDEN, message), details: { reason } } };
|
|
3688
|
+
}
|
|
3689
|
+
function missingSubscriptionId(subscriptionId) {
|
|
3690
|
+
return subscriptionId && subscriptionId.trim() ? null : validationError("subscriptionId", "subscriptionId \u306F\u5FC5\u9808\u3067\u3059");
|
|
3691
|
+
}
|
|
3692
|
+
async function getBalance(subscriptionId) {
|
|
3693
|
+
const invalid = missingSubscriptionId(subscriptionId);
|
|
3694
|
+
if (invalid) return invalid;
|
|
3695
|
+
return fetchWithAuth(creditsEndpoint(subscriptionId, "balance"));
|
|
3696
|
+
}
|
|
3697
|
+
async function listHistory(subscriptionId, options = {}) {
|
|
3698
|
+
const invalid = missingSubscriptionId(subscriptionId);
|
|
3699
|
+
if (invalid) return invalid;
|
|
3700
|
+
const query = new URLSearchParams();
|
|
3701
|
+
if (options.cursor !== void 0) query.set("cursor", options.cursor);
|
|
3702
|
+
if (options.limit !== void 0) {
|
|
3703
|
+
if (!isIntegerInRange(options.limit, FFID_CREDITS_HISTORY_MIN_LIMIT, FFID_CREDITS_HISTORY_MAX_LIMIT)) {
|
|
3704
|
+
return validationError(
|
|
3705
|
+
"limit",
|
|
3706
|
+
`limit \u306F ${FFID_CREDITS_HISTORY_MIN_LIMIT}\u301C${FFID_CREDITS_HISTORY_MAX_LIMIT} \u306E\u6574\u6570\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044`
|
|
3707
|
+
);
|
|
3708
|
+
}
|
|
3709
|
+
query.set("limit", String(options.limit));
|
|
3710
|
+
}
|
|
3711
|
+
if (options.type !== void 0) {
|
|
3712
|
+
if (!FFID_CREDIT_HISTORY_TYPES.includes(options.type)) {
|
|
3713
|
+
return validationError("type", `type \u306F ${FFID_CREDIT_HISTORY_TYPES.join(" / ")} \u306E\u3044\u305A\u308C\u304B\u3067\u3059`);
|
|
3714
|
+
}
|
|
3715
|
+
query.set("type", options.type);
|
|
3716
|
+
}
|
|
3717
|
+
const qs = query.toString();
|
|
3718
|
+
return fetchWithAuth(
|
|
3719
|
+
`${creditsEndpoint(subscriptionId, "history")}${qs ? `?${qs}` : ""}`
|
|
3720
|
+
);
|
|
3721
|
+
}
|
|
3722
|
+
async function listPacks(packServiceCode) {
|
|
3723
|
+
const resolved = packServiceCode ?? (authMode === "service-key" ? void 0 : serviceCode);
|
|
3724
|
+
if (resolved !== void 0 && !resolved.trim()) {
|
|
3725
|
+
return validationError("serviceCode", "serviceCode \u3092\u6307\u5B9A\u3059\u308B\u5834\u5408\u3001\u7A7A\u6587\u5B57\u306F\u4F7F\u7528\u3067\u304D\u307E\u305B\u3093");
|
|
3726
|
+
}
|
|
3727
|
+
const qs = resolved === void 0 ? "" : `?${new URLSearchParams({ serviceCode: resolved }).toString()}`;
|
|
3728
|
+
return fetchWithAuth(`${CREDIT_PACKS_ENDPOINT}${qs}`);
|
|
3729
|
+
}
|
|
3730
|
+
async function createPackCheckout(params) {
|
|
3731
|
+
if (authMode !== "token") {
|
|
3732
|
+
return forbidden(
|
|
3733
|
+
"bearer_required",
|
|
3734
|
+
"createPackCheckout \u306F token \u30E2\u30FC\u30C9\uFF08Bearer\uFF09\u3067\u306E\u307F\u5229\u7528\u3067\u304D\u307E\u3059\u3002\u8CFC\u5165\u8005\u306E\u8A18\u9332\u306B\u5B9F\u30E6\u30FC\u30B6\u30FC\u304C\u5FC5\u8981\u306A\u305F\u3081\u3067\u3059"
|
|
3735
|
+
);
|
|
3736
|
+
}
|
|
3737
|
+
const invalid = missingSubscriptionId(params.subscriptionId);
|
|
3738
|
+
if (invalid) return invalid;
|
|
3739
|
+
for (const field of ["packCode", "successUrl", "cancelUrl"]) {
|
|
3740
|
+
if (!params[field] || !params[field].trim()) {
|
|
3741
|
+
return validationError(field, `${field} \u306F\u5FC5\u9808\u3067\u3059`);
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
if (params.quantity !== void 0 && !isIntegerInRange(params.quantity, FFID_CREDIT_PACK_MIN_QUANTITY, FFID_CREDIT_PACK_MAX_QUANTITY)) {
|
|
3745
|
+
return validationError(
|
|
3746
|
+
"quantity",
|
|
3747
|
+
`quantity \u306F ${FFID_CREDIT_PACK_MIN_QUANTITY}\u301C${FFID_CREDIT_PACK_MAX_QUANTITY} \u306E\u6574\u6570\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044`
|
|
3748
|
+
);
|
|
3749
|
+
}
|
|
3750
|
+
const body = {
|
|
3751
|
+
subscriptionId: params.subscriptionId,
|
|
3752
|
+
packCode: params.packCode,
|
|
3753
|
+
successUrl: params.successUrl,
|
|
3754
|
+
cancelUrl: params.cancelUrl
|
|
3755
|
+
};
|
|
3756
|
+
if (params.quantity !== void 0) body.quantity = params.quantity;
|
|
3757
|
+
return fetchWithAuth(CREDIT_PACK_CHECKOUT_ENDPOINT, {
|
|
3758
|
+
method: "POST",
|
|
3759
|
+
body: JSON.stringify(body)
|
|
3760
|
+
});
|
|
3761
|
+
}
|
|
3762
|
+
function validateConsume(params) {
|
|
3763
|
+
const invalid = missingSubscriptionId(params.subscriptionId);
|
|
3764
|
+
if (invalid) return invalid;
|
|
3765
|
+
if (!isIntegerInRange(params.amount, FFID_CREDITS_CONSUME_MIN_AMOUNT, FFID_CREDITS_CONSUME_MAX_AMOUNT)) {
|
|
3766
|
+
return validationError(
|
|
3767
|
+
"amount",
|
|
3768
|
+
`amount \u306F ${FFID_CREDITS_CONSUME_MIN_AMOUNT}\u301C${FFID_CREDITS_CONSUME_MAX_AMOUNT} \u306E\u6574\u6570\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044`
|
|
3769
|
+
);
|
|
3770
|
+
}
|
|
3771
|
+
if (typeof params.idempotencyKey !== "string" || !FFID_CREDITS_IDEMPOTENCY_KEY_PATTERN.test(params.idempotencyKey)) {
|
|
3772
|
+
return validationError(
|
|
3773
|
+
"idempotencyKey",
|
|
3774
|
+
"idempotencyKey \u306E\u5F62\u5F0F\u304C\u4E0D\u6B63\u3067\u3059\uFF08\u82F1\u6570\u5B57\u3068 . _ : - \u306E 8\u301C128 \u6587\u5B57\u3002\u63A8\u5968\u306F feature:requestId\uFF09"
|
|
3775
|
+
);
|
|
3776
|
+
}
|
|
3777
|
+
if (typeof params.feature !== "string" || !FFID_CREDITS_FEATURE_PATTERN.test(params.feature)) {
|
|
3778
|
+
return validationError("feature", "feature \u306E\u5F62\u5F0F\u304C\u4E0D\u6B63\u3067\u3059\uFF08\u5C0F\u6587\u5B57\u82F1\u6570\u5B57\u3067\u59CB\u307E\u308B\u3001\u5C0F\u6587\u5B57\u82F1\u6570\u5B57\u3068 . _ - \u306E 1\u301C64 \u6587\u5B57\uFF09");
|
|
3779
|
+
}
|
|
3780
|
+
if (params.metadata !== void 0) {
|
|
3781
|
+
if (!isPlainObject2(params.metadata)) {
|
|
3782
|
+
return validationError("metadata", "metadata \u306F JSON \u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059");
|
|
3783
|
+
}
|
|
3784
|
+
let serialized;
|
|
3785
|
+
try {
|
|
3786
|
+
serialized = JSON.stringify(params.metadata);
|
|
3787
|
+
} catch {
|
|
3788
|
+
return validationError("metadata", "metadata \u306F JSON \u306B\u5909\u63DB\u3067\u304D\u308B\u5024\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\uFF08BigInt\u30FB\u5FAA\u74B0\u53C2\u7167\u306F\u4E0D\u53EF\uFF09");
|
|
3789
|
+
}
|
|
3790
|
+
if (utf8Encoder.encode(serialized).byteLength > FFID_CREDITS_METADATA_MAX_BYTES) {
|
|
3791
|
+
return validationError("metadata", `metadata \u306F ${FFID_CREDITS_METADATA_MAX_BYTES} \u30D0\u30A4\u30C8\u4EE5\u4E0B\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`);
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
if (params.userId !== void 0 && !UUID_PATTERN.test(params.userId)) {
|
|
3795
|
+
return validationError("userId", "userId \u306F\u6709\u52B9\u306AUUID\u5F62\u5F0F\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059");
|
|
3796
|
+
}
|
|
3797
|
+
return null;
|
|
3798
|
+
}
|
|
3799
|
+
async function consume(params) {
|
|
3800
|
+
if (authMode !== "service-key") {
|
|
3801
|
+
return forbidden(
|
|
3802
|
+
"api_key_required",
|
|
3803
|
+
"credits.consume \u306F service-key \u8A8D\u8A3C\uFF08X-Service-Api-Key\uFF09\u3067\u306E\u307F\u5229\u7528\u3067\u304D\u307E\u3059\u3002\u6D88\u8CBB\u306E\u5831\u544A\u306F\u30B5\u30FC\u30D3\u30B9\u306E\u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u304B\u3089\u884C\u3063\u3066\u304F\u3060\u3055\u3044"
|
|
3804
|
+
);
|
|
3805
|
+
}
|
|
3806
|
+
const invalid = validateConsume(params);
|
|
3807
|
+
if (invalid) return invalid;
|
|
3808
|
+
const body = {
|
|
3809
|
+
amount: params.amount,
|
|
3810
|
+
idempotencyKey: params.idempotencyKey,
|
|
3811
|
+
feature: params.feature
|
|
3812
|
+
};
|
|
3813
|
+
if (params.metadata !== void 0) body.metadata = params.metadata;
|
|
3814
|
+
if (params.userId !== void 0) body.userId = params.userId;
|
|
3815
|
+
return fetchWithAuth(creditsEndpoint(params.subscriptionId, "consume"), {
|
|
3816
|
+
method: "POST",
|
|
3817
|
+
body: JSON.stringify(body)
|
|
3818
|
+
});
|
|
3819
|
+
}
|
|
3820
|
+
function getBuyCreditsUrl(params) {
|
|
3821
|
+
const raw = params.subscriptionId?.trim() ?? "";
|
|
3822
|
+
if (raw.startsWith(STRIPE_SUBSCRIPTION_ID_PREFIX)) {
|
|
3823
|
+
return validationError(
|
|
3824
|
+
"subscriptionId",
|
|
3825
|
+
"getBuyCreditsUrl \u306B\u306F\u5951\u7D04\u306E UUID\uFF08\u307E\u305F\u306F ffid:<uuid>\uFF09\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002Stripe \u306E sub_... \u306F\u4F7F\u3048\u307E\u305B\u3093"
|
|
3826
|
+
);
|
|
3827
|
+
}
|
|
3828
|
+
const uuid = raw.startsWith(FFID_CONTRACT_ID_PREFIX) ? raw.slice(FFID_CONTRACT_ID_PREFIX.length) : raw;
|
|
3829
|
+
if (!UUID_PATTERN.test(uuid)) {
|
|
3830
|
+
return validationError("subscriptionId", "subscriptionId \u306F\u5951\u7D04\u306E UUID\uFF08\u307E\u305F\u306F ffid:<uuid>\uFF09\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059");
|
|
3831
|
+
}
|
|
3832
|
+
const query = new URLSearchParams({ [BUY_CREDITS_QUERY.key]: BUY_CREDITS_QUERY.value });
|
|
3833
|
+
if (params.orgId !== void 0 && params.orgId.trim()) {
|
|
3834
|
+
query.set(ORG_QUERY_KEY, params.orgId);
|
|
3835
|
+
}
|
|
3836
|
+
return { data: `${baseUrl}${PORTAL_SUBSCRIPTION_PATH}/${encodeURIComponent(uuid)}?${query.toString()}` };
|
|
3837
|
+
}
|
|
3838
|
+
function redirectToBuyCredits(params) {
|
|
3839
|
+
const result = getBuyCreditsUrl(params);
|
|
3840
|
+
if (result.error) {
|
|
3841
|
+
logger.warn("redirectToBuyCredits:", result.error.message);
|
|
3842
|
+
return { success: false, error: result.error.message };
|
|
3843
|
+
}
|
|
3844
|
+
if (typeof window === "undefined") {
|
|
3845
|
+
logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
|
|
3846
|
+
return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
|
|
3847
|
+
}
|
|
3848
|
+
logger.debug("Redirecting to buy credits:", result.data);
|
|
3849
|
+
window.location.href = result.data;
|
|
3850
|
+
return { success: true };
|
|
3851
|
+
}
|
|
3852
|
+
return {
|
|
3853
|
+
getBalance,
|
|
3854
|
+
listHistory,
|
|
3855
|
+
listPacks,
|
|
3856
|
+
createPackCheckout,
|
|
3857
|
+
consume,
|
|
3858
|
+
getBuyCreditsUrl,
|
|
3859
|
+
redirectToBuyCredits
|
|
3860
|
+
};
|
|
3861
|
+
}
|
|
3862
|
+
|
|
3637
3863
|
// src/subscriptions/types.ts
|
|
3638
3864
|
var EFFECTIVE_SUBSCRIPTION_STATUSES = [
|
|
3639
3865
|
"active",
|
|
@@ -3767,7 +3993,12 @@ function toServiceAccessDecision(response, allowGrace) {
|
|
|
3767
3993
|
planCode: response.planCode,
|
|
3768
3994
|
currentPeriodEnd: response.currentPeriodEnd,
|
|
3769
3995
|
gracePeriodEndsAt: response.gracePeriodEndsAt ?? null,
|
|
3770
|
-
reactivatable: response.reactivatable ?? false
|
|
3996
|
+
reactivatable: response.reactivatable ?? false,
|
|
3997
|
+
// Informational pass-through (10.0.0). Deliberately not part of hasAccess /
|
|
3998
|
+
// denialReason: credits and the member limit never gate access (CREDITS_API §1-5).
|
|
3999
|
+
...response.pricingModel !== void 0 ? { pricingModel: response.pricingModel } : {},
|
|
4000
|
+
...response.credits !== void 0 ? { credits: response.credits } : {},
|
|
4001
|
+
...response.memberLimitReached !== void 0 ? { memberLimitReached: response.memberLimitReached } : {}
|
|
3771
4002
|
};
|
|
3772
4003
|
}
|
|
3773
4004
|
function failClosedServiceAccessDecision(params, error) {
|
|
@@ -3814,6 +4045,19 @@ var FFID_SUBSCRIPTION_ERROR_CODES = {
|
|
|
3814
4045
|
FREE_DOWNGRADE_NOT_ALLOWED: "FREE_DOWNGRADE_NOT_ALLOWED",
|
|
3815
4046
|
PAYMENT_METHOD_REQUIRED: "PAYMENT_METHOD_REQUIRED"
|
|
3816
4047
|
};
|
|
4048
|
+
var FFID_CREDITS_ERROR_CODES = {
|
|
4049
|
+
CREDITS_INSUFFICIENT: "CREDITS_INSUFFICIENT",
|
|
4050
|
+
CREDITS_IDEMPOTENCY_CONFLICT: "CREDITS_IDEMPOTENCY_CONFLICT",
|
|
4051
|
+
CREDITS_NOT_APPLICABLE: "CREDITS_NOT_APPLICABLE",
|
|
4052
|
+
CREDITS_BALANCE_CAP_EXCEEDED: "CREDITS_BALANCE_CAP_EXCEEDED",
|
|
4053
|
+
SEAT_BILLING_NOT_APPLICABLE: "SEAT_BILLING_NOT_APPLICABLE",
|
|
4054
|
+
MEMBER_LIMIT_REACHED: "MEMBER_LIMIT_REACHED",
|
|
4055
|
+
CREDIT_PACK_NOT_FOUND: "CREDIT_PACK_NOT_FOUND",
|
|
4056
|
+
CREDIT_PACK_REGION_NOT_SUPPORTED: "CREDIT_PACK_REGION_NOT_SUPPORTED",
|
|
4057
|
+
CREDIT_PACK_STRIPE_NOT_SYNCED: "CREDIT_PACK_STRIPE_NOT_SYNCED",
|
|
4058
|
+
CREDIT_PACK_STRIPE_OUT_OF_SYNC: "CREDIT_PACK_STRIPE_OUT_OF_SYNC",
|
|
4059
|
+
CREDIT_PACK_CHANGED: "CREDIT_PACK_CHANGED"
|
|
4060
|
+
};
|
|
3817
4061
|
|
|
3818
4062
|
// src/client/ffid-client.ts
|
|
3819
4063
|
var UNAUTHORIZED_STATUS2 = 401;
|
|
@@ -4141,7 +4385,7 @@ function createFFIDClient(config) {
|
|
|
4141
4385
|
fetchWithAuth,
|
|
4142
4386
|
createError
|
|
4143
4387
|
});
|
|
4144
|
-
const { listSeatAssignments, assignSeats, unassignSeat } = createSeatMethods({
|
|
4388
|
+
const { listSeatAssignments, getSeats, assignSeats, unassignSeat } = createSeatMethods({
|
|
4145
4389
|
fetchWithAuth,
|
|
4146
4390
|
createError
|
|
4147
4391
|
});
|
|
@@ -4248,6 +4492,14 @@ function createFFIDClient(config) {
|
|
|
4248
4492
|
fetchWithAuth,
|
|
4249
4493
|
createError
|
|
4250
4494
|
});
|
|
4495
|
+
const credits = createCreditsMethods({
|
|
4496
|
+
fetchWithAuth,
|
|
4497
|
+
createError,
|
|
4498
|
+
authMode,
|
|
4499
|
+
baseUrl,
|
|
4500
|
+
serviceCode: config.serviceCode,
|
|
4501
|
+
logger
|
|
4502
|
+
});
|
|
4251
4503
|
const verifyAccessToken = createVerifyAccessToken({
|
|
4252
4504
|
authMode,
|
|
4253
4505
|
baseUrl,
|
|
@@ -4303,6 +4555,7 @@ function createFFIDClient(config) {
|
|
|
4303
4555
|
getInvoice,
|
|
4304
4556
|
retryPayment,
|
|
4305
4557
|
listSeatAssignments,
|
|
4558
|
+
getSeats,
|
|
4306
4559
|
assignSeats,
|
|
4307
4560
|
unassignSeat,
|
|
4308
4561
|
listPlans,
|
|
@@ -4313,13 +4566,16 @@ function createFFIDClient(config) {
|
|
|
4313
4566
|
cancelSubscription,
|
|
4314
4567
|
cancelPendingDowngrade,
|
|
4315
4568
|
previewPlanChange,
|
|
4569
|
+
/** @deprecated 10.0.0: per_seat (praxis) only; removed in 11.0.0 */
|
|
4316
4570
|
previewSeatChange,
|
|
4317
4571
|
verifyAccessToken,
|
|
4318
4572
|
getSubscribeUrl,
|
|
4319
4573
|
redirectToSubscribe,
|
|
4320
4574
|
getChangePlanUrl,
|
|
4321
4575
|
redirectToChangePlan,
|
|
4576
|
+
/** @deprecated 10.0.0: per_seat (praxis) only; removed in 11.0.0 */
|
|
4322
4577
|
getChangeSeatsUrl,
|
|
4578
|
+
/** @deprecated 10.0.0: per_seat (praxis) only; removed in 11.0.0 */
|
|
4323
4579
|
redirectToChangeSeats,
|
|
4324
4580
|
getRecoverPaymentUrl,
|
|
4325
4581
|
redirectToRecoverPayment,
|
|
@@ -4335,6 +4591,8 @@ function createFFIDClient(config) {
|
|
|
4335
4591
|
newsletter,
|
|
4336
4592
|
/** Inquiry methods (create) */
|
|
4337
4593
|
inquiry,
|
|
4594
|
+
/** Credits methods for flat plans (consume / balance / history / packs) */
|
|
4595
|
+
credits,
|
|
4338
4596
|
/** Token store (token mode only) */
|
|
4339
4597
|
tokenStore,
|
|
4340
4598
|
/**
|
|
@@ -6506,6 +6764,20 @@ exports.FFID_ANNOUNCEMENTS_ERROR_CODES = FFID_ANNOUNCEMENTS_ERROR_CODES;
|
|
|
6506
6764
|
exports.FFID_CATALOG_ENVIRONMENTS = FFID_CATALOG_ENVIRONMENTS;
|
|
6507
6765
|
exports.FFID_CATALOG_PUBLICATION_STATUSES = FFID_CATALOG_PUBLICATION_STATUSES;
|
|
6508
6766
|
exports.FFID_COMPANY_SIZE_CODES = FFID_COMPANY_SIZE_CODES;
|
|
6767
|
+
exports.FFID_CREDITS_CONSUME_MAX_AMOUNT = FFID_CREDITS_CONSUME_MAX_AMOUNT;
|
|
6768
|
+
exports.FFID_CREDITS_CONSUME_MIN_AMOUNT = FFID_CREDITS_CONSUME_MIN_AMOUNT;
|
|
6769
|
+
exports.FFID_CREDITS_ERROR_CODES = FFID_CREDITS_ERROR_CODES;
|
|
6770
|
+
exports.FFID_CREDITS_FEATURE_PATTERN = FFID_CREDITS_FEATURE_PATTERN;
|
|
6771
|
+
exports.FFID_CREDITS_HISTORY_DEFAULT_LIMIT = FFID_CREDITS_HISTORY_DEFAULT_LIMIT;
|
|
6772
|
+
exports.FFID_CREDITS_HISTORY_MAX_LIMIT = FFID_CREDITS_HISTORY_MAX_LIMIT;
|
|
6773
|
+
exports.FFID_CREDITS_HISTORY_MIN_LIMIT = FFID_CREDITS_HISTORY_MIN_LIMIT;
|
|
6774
|
+
exports.FFID_CREDITS_IDEMPOTENCY_KEY_PATTERN = FFID_CREDITS_IDEMPOTENCY_KEY_PATTERN;
|
|
6775
|
+
exports.FFID_CREDITS_LOW_THRESHOLD_PERCENT = FFID_CREDITS_LOW_THRESHOLD_PERCENT;
|
|
6776
|
+
exports.FFID_CREDITS_METADATA_MAX_BYTES = FFID_CREDITS_METADATA_MAX_BYTES;
|
|
6777
|
+
exports.FFID_CREDIT_GRANT_KINDS = FFID_CREDIT_GRANT_KINDS;
|
|
6778
|
+
exports.FFID_CREDIT_HISTORY_TYPES = FFID_CREDIT_HISTORY_TYPES;
|
|
6779
|
+
exports.FFID_CREDIT_PACK_MAX_QUANTITY = FFID_CREDIT_PACK_MAX_QUANTITY;
|
|
6780
|
+
exports.FFID_CREDIT_PACK_MIN_QUANTITY = FFID_CREDIT_PACK_MIN_QUANTITY;
|
|
6509
6781
|
exports.FFID_ERROR_CODES = FFID_ERROR_CODES;
|
|
6510
6782
|
exports.FFID_INDUSTRY_CODES = FFID_INDUSTRY_CODES;
|
|
6511
6783
|
exports.FFID_INQUIRY_ACQUISITION_SOURCES = FFID_INQUIRY_ACQUISITION_SOURCES;
|