@feelflow/ffid-sdk 5.31.0 → 5.35.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.
@@ -162,6 +162,14 @@ function createFFIDAgencyClient(config = {}) {
162
162
  { method: "DELETE" }
163
163
  );
164
164
  }
165
+ async function provisionOrganization(agencyId, data) {
166
+ const err = validateIds([agencyId, "agencyId"]);
167
+ if (err) return err;
168
+ return fetchWithCredentials(`/${encodeURIComponent(agencyId)}/organizations/provision`, {
169
+ method: "POST",
170
+ body: JSON.stringify(data)
171
+ });
172
+ }
165
173
  async function getSubAgencies(agencyId) {
166
174
  const err = validateIds([agencyId, "agencyId"]);
167
175
  if (err) return err;
@@ -289,6 +297,7 @@ function createFFIDAgencyClient(config = {}) {
289
297
  getOrganizations,
290
298
  linkOrganization,
291
299
  unlinkOrganization,
300
+ provisionOrganization,
292
301
  // Sub-Agencies
293
302
  getSubAgencies,
294
303
  createSubAgency,
@@ -315,4 +324,106 @@ function createFFIDAgencyClient(config = {}) {
315
324
  };
316
325
  }
317
326
 
318
- export { FFID_AGENCY_ERROR_CODES, createFFIDAgencyClient };
327
+ // src/agency/ffid-agency-server-client.ts
328
+ var API_PREFIX2 = "/api/v1/ext/agencies";
329
+ var AGENCY_API_KEY_HEADER = "X-Agency-Api-Key";
330
+ var SDK_LOG_PREFIX2 = "[FFID Agency Server SDK]";
331
+ var noopLogger2 = {
332
+ debug: () => {
333
+ },
334
+ info: () => {
335
+ },
336
+ warn: () => {
337
+ },
338
+ error: () => {
339
+ }
340
+ };
341
+ var consoleLogger2 = {
342
+ debug: (...args) => console.debug(SDK_LOG_PREFIX2, ...args),
343
+ info: (...args) => console.info(SDK_LOG_PREFIX2, ...args),
344
+ warn: (...args) => console.warn(SDK_LOG_PREFIX2, ...args),
345
+ error: (...args) => console.error(SDK_LOG_PREFIX2, ...args)
346
+ };
347
+ var DEFAULT_TIMEOUT_MS = 3e4;
348
+ function createFFIDAgencyServerClient(config) {
349
+ const apiKey = config.apiKey?.trim() ?? "";
350
+ if (apiKey === "") {
351
+ throw new Error(
352
+ "createFFIDAgencyServerClient: apiKey is required (set it from an environment variable, never in browser code)"
353
+ );
354
+ }
355
+ const baseUrl = config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
356
+ const logger = config.logger ?? (config.debug ? consoleLogger2 : noopLogger2);
357
+ const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
358
+ async function request(endpoint, body) {
359
+ const url = `${baseUrl}${API_PREFIX2}${endpoint}`;
360
+ let response;
361
+ try {
362
+ response = await fetch(url, {
363
+ method: "POST",
364
+ headers: {
365
+ "Content-Type": "application/json",
366
+ [AGENCY_API_KEY_HEADER]: apiKey
367
+ },
368
+ body: JSON.stringify(body),
369
+ signal: AbortSignal.timeout(timeoutMs)
370
+ });
371
+ } catch (error) {
372
+ logger.error("Network error:", { url, error });
373
+ return {
374
+ error: {
375
+ code: FFID_AGENCY_ERROR_CODES.NETWORK_ERROR,
376
+ // タイムアウトも組織が作られた後かもしれない。再試行の前に確認を促す。
377
+ message: `${error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"}\u3002\u7D44\u7E54\u304C\u4F5C\u6210\u3055\u308C\u305F\u53EF\u80FD\u6027\u304C\u3042\u308B\u305F\u3081\u3001\u518D\u8A66\u884C\u524D\u306B\u7D44\u7E54\u4E00\u89A7\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044`
378
+ }
379
+ };
380
+ }
381
+ let payload;
382
+ try {
383
+ payload = await response.json();
384
+ } catch (parseError) {
385
+ logger.error("Parse error:", { url, status: response.status, parseError });
386
+ return {
387
+ error: {
388
+ code: FFID_AGENCY_ERROR_CODES.PARSE_ERROR,
389
+ message: `\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u4E0D\u6B63\u306A\u30EC\u30B9\u30DD\u30F3\u30B9\u3092\u53D7\u4FE1\u3057\u307E\u3057\u305F (status: ${response.status})\u3002\u7D44\u7E54\u304C\u4F5C\u6210\u3055\u308C\u305F\u53EF\u80FD\u6027\u304C\u3042\u308B\u305F\u3081\u3001\u518D\u8A66\u884C\u524D\u306B\u7D44\u7E54\u4E00\u89A7\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044`
390
+ }
391
+ };
392
+ }
393
+ if (payload === null || typeof payload !== "object") {
394
+ logger.error("Malformed response body:", { url, status: response.status });
395
+ return {
396
+ error: {
397
+ code: FFID_AGENCY_ERROR_CODES.PARSE_ERROR,
398
+ message: `\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u4E0D\u6B63\u306A\u30EC\u30B9\u30DD\u30F3\u30B9\u3092\u53D7\u4FE1\u3057\u307E\u3057\u305F (status: ${response.status})`
399
+ }
400
+ };
401
+ }
402
+ const parsed = payload;
403
+ if (!response.ok || parsed.success !== true) {
404
+ return {
405
+ error: {
406
+ code: parsed.error?.code ?? FFID_AGENCY_ERROR_CODES.UNKNOWN_ERROR,
407
+ message: parsed.error?.message ?? `\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u5931\u6557\u3057\u307E\u3057\u305F (${response.status})`
408
+ }
409
+ };
410
+ }
411
+ if (parsed.data === void 0) {
412
+ logger.error("Missing data in successful response:", { url });
413
+ return {
414
+ error: {
415
+ code: FFID_AGENCY_ERROR_CODES.UNKNOWN_ERROR,
416
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
417
+ }
418
+ };
419
+ }
420
+ return { data: parsed.data };
421
+ }
422
+ return {
423
+ async provisionCustomerOrganization(data) {
424
+ return request("/organizations/provision", data);
425
+ }
426
+ };
427
+ }
428
+
429
+ export { FFID_AGENCY_ERROR_CODES, createFFIDAgencyClient, createFFIDAgencyServerClient };
@@ -132,13 +132,18 @@ function normalizeSeatAssignmentClaim(raw, organizationId, subscriptionId) {
132
132
  status
133
133
  };
134
134
  }
135
+ var ACCOUNT_TYPE_WIRE_VALUES = ["personal", "organization"];
136
+ function normalizeAccountTypeValue(value) {
137
+ if (value === null) return null;
138
+ return typeof value === "string" && ACCOUNT_TYPE_WIRE_VALUES.includes(value) ? value : void 0;
139
+ }
135
140
  function normalizeUserinfo(raw) {
136
141
  const hasOrganizationId = hasOwnKey(raw, "organization_id");
137
142
  const hasOrganizationName = hasOwnKey(raw, "organization_name");
138
143
  const hasOrganizationMemberRole = hasOwnKey(raw, "organization_member_role");
139
144
  const organizationMemberRole = hasOrganizationMemberRole ? raw.organization_member_role ?? null : raw.subscription ? raw.subscription.member_role ?? null : void 0;
140
145
  const hasOrganizationAccountType = hasOwnKey(raw, "organization_account_type");
141
- const organizationAccountType = hasOrganizationAccountType ? raw.organization_account_type ?? null : raw.subscription && hasOwnKey(raw.subscription, "account_type") ? raw.subscription.account_type ?? null : void 0;
146
+ const organizationAccountType = hasOrganizationAccountType ? normalizeAccountTypeValue(raw.organization_account_type) : raw.subscription && hasOwnKey(raw.subscription, "account_type") ? normalizeAccountTypeValue(raw.subscription.account_type) : void 0;
142
147
  const organizationFields = {
143
148
  ...hasOrganizationId ? { organizationId: raw.organization_id ?? null } : {},
144
149
  ...hasOrganizationId || hasOrganizationName ? { organizationName: raw.organization_name ?? null } : {},
@@ -169,8 +174,9 @@ function normalizeUserinfo(raw) {
169
174
  organizationName: hasOwnKey(raw.subscription, "organization_name") ? raw.subscription.organization_name ?? null : null,
170
175
  // #4531: `undefined` means an older FFID omitted the field on the
171
176
  // wire — genuinely unknown, not "no account type". Do not default
172
- // to either literal.
173
- accountType: hasOwnKey(raw.subscription, "account_type") ? raw.subscription.account_type ?? null : void 0,
177
+ // to either literal. An unrecognized non-null wire value folds to
178
+ // `undefined` the same way (see `normalizeAccountTypeValue`).
179
+ accountType: hasOwnKey(raw.subscription, "account_type") ? normalizeAccountTypeValue(raw.subscription.account_type) : void 0,
174
180
  hasSeatAssignment: raw.subscription.has_seat_assignment ?? false,
175
181
  seatAssignment: hasOwnKey(raw.subscription, "seat_assignment") ? normalizeSeatAssignmentClaim(
176
182
  raw.subscription.seat_assignment,
@@ -1339,7 +1345,11 @@ function createProvisioningMethods(deps) {
1339
1345
  }
1340
1346
  return fetchWithAuth(USER_WELCOME_EMAIL_ENDPOINT, {
1341
1347
  method: "POST",
1342
- body: JSON.stringify({ email: params.email.trim() })
1348
+ body: JSON.stringify({
1349
+ email: params.email.trim(),
1350
+ ...params.template !== void 0 ? { template: params.template } : {},
1351
+ ...params.returnTo !== void 0 ? { returnTo: params.returnTo } : {}
1352
+ })
1343
1353
  });
1344
1354
  }
1345
1355
  async function provisionOrganization(params) {
@@ -1665,7 +1675,7 @@ function validateTokenResponse(tokenResponse) {
1665
1675
  }
1666
1676
 
1667
1677
  // src/client/version-check.ts
1668
- var SDK_VERSION = "5.31.0";
1678
+ var SDK_VERSION = "5.35.0";
1669
1679
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
1670
1680
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
1671
1681
  function sdkHeaders() {
@@ -134,13 +134,18 @@ function normalizeSeatAssignmentClaim(raw, organizationId, subscriptionId) {
134
134
  status
135
135
  };
136
136
  }
137
+ var ACCOUNT_TYPE_WIRE_VALUES = ["personal", "organization"];
138
+ function normalizeAccountTypeValue(value) {
139
+ if (value === null) return null;
140
+ return typeof value === "string" && ACCOUNT_TYPE_WIRE_VALUES.includes(value) ? value : void 0;
141
+ }
137
142
  function normalizeUserinfo(raw) {
138
143
  const hasOrganizationId = hasOwnKey(raw, "organization_id");
139
144
  const hasOrganizationName = hasOwnKey(raw, "organization_name");
140
145
  const hasOrganizationMemberRole = hasOwnKey(raw, "organization_member_role");
141
146
  const organizationMemberRole = hasOrganizationMemberRole ? raw.organization_member_role ?? null : raw.subscription ? raw.subscription.member_role ?? null : void 0;
142
147
  const hasOrganizationAccountType = hasOwnKey(raw, "organization_account_type");
143
- const organizationAccountType = hasOrganizationAccountType ? raw.organization_account_type ?? null : raw.subscription && hasOwnKey(raw.subscription, "account_type") ? raw.subscription.account_type ?? null : void 0;
148
+ const organizationAccountType = hasOrganizationAccountType ? normalizeAccountTypeValue(raw.organization_account_type) : raw.subscription && hasOwnKey(raw.subscription, "account_type") ? normalizeAccountTypeValue(raw.subscription.account_type) : void 0;
144
149
  const organizationFields = {
145
150
  ...hasOrganizationId ? { organizationId: raw.organization_id ?? null } : {},
146
151
  ...hasOrganizationId || hasOrganizationName ? { organizationName: raw.organization_name ?? null } : {},
@@ -171,8 +176,9 @@ function normalizeUserinfo(raw) {
171
176
  organizationName: hasOwnKey(raw.subscription, "organization_name") ? raw.subscription.organization_name ?? null : null,
172
177
  // #4531: `undefined` means an older FFID omitted the field on the
173
178
  // wire — genuinely unknown, not "no account type". Do not default
174
- // to either literal.
175
- accountType: hasOwnKey(raw.subscription, "account_type") ? raw.subscription.account_type ?? null : void 0,
179
+ // to either literal. An unrecognized non-null wire value folds to
180
+ // `undefined` the same way (see `normalizeAccountTypeValue`).
181
+ accountType: hasOwnKey(raw.subscription, "account_type") ? normalizeAccountTypeValue(raw.subscription.account_type) : void 0,
176
182
  hasSeatAssignment: raw.subscription.has_seat_assignment ?? false,
177
183
  seatAssignment: hasOwnKey(raw.subscription, "seat_assignment") ? normalizeSeatAssignmentClaim(
178
184
  raw.subscription.seat_assignment,
@@ -1341,7 +1347,11 @@ function createProvisioningMethods(deps) {
1341
1347
  }
1342
1348
  return fetchWithAuth(USER_WELCOME_EMAIL_ENDPOINT, {
1343
1349
  method: "POST",
1344
- body: JSON.stringify({ email: params.email.trim() })
1350
+ body: JSON.stringify({
1351
+ email: params.email.trim(),
1352
+ ...params.template !== void 0 ? { template: params.template } : {},
1353
+ ...params.returnTo !== void 0 ? { returnTo: params.returnTo } : {}
1354
+ })
1345
1355
  });
1346
1356
  }
1347
1357
  async function provisionOrganization(params) {
@@ -1667,7 +1677,7 @@ function validateTokenResponse(tokenResponse) {
1667
1677
  }
1668
1678
 
1669
1679
  // src/client/version-check.ts
1670
- var SDK_VERSION = "5.31.0";
1680
+ var SDK_VERSION = "5.35.0";
1671
1681
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
1672
1682
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
1673
1683
  function sdkHeaders() {
@@ -1,34 +1,34 @@
1
1
  'use strict';
2
2
 
3
- var chunkX7SWYOG3_cjs = require('../chunk-X7SWYOG3.cjs');
3
+ var chunkWG2YHUGO_cjs = require('../chunk-WG2YHUGO.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
8
8
  enumerable: true,
9
- get: function () { return chunkX7SWYOG3_cjs.FFIDAnnouncementBadge; }
9
+ get: function () { return chunkWG2YHUGO_cjs.FFIDAnnouncementBadge; }
10
10
  });
11
11
  Object.defineProperty(exports, "FFIDAnnouncementList", {
12
12
  enumerable: true,
13
- get: function () { return chunkX7SWYOG3_cjs.FFIDAnnouncementList; }
13
+ get: function () { return chunkWG2YHUGO_cjs.FFIDAnnouncementList; }
14
14
  });
15
15
  Object.defineProperty(exports, "FFIDInquiryForm", {
16
16
  enumerable: true,
17
- get: function () { return chunkX7SWYOG3_cjs.FFIDInquiryForm; }
17
+ get: function () { return chunkWG2YHUGO_cjs.FFIDInquiryForm; }
18
18
  });
19
19
  Object.defineProperty(exports, "FFIDLoginButton", {
20
20
  enumerable: true,
21
- get: function () { return chunkX7SWYOG3_cjs.FFIDLoginButton; }
21
+ get: function () { return chunkWG2YHUGO_cjs.FFIDLoginButton; }
22
22
  });
23
23
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
24
24
  enumerable: true,
25
- get: function () { return chunkX7SWYOG3_cjs.FFIDOrganizationSwitcher; }
25
+ get: function () { return chunkWG2YHUGO_cjs.FFIDOrganizationSwitcher; }
26
26
  });
27
27
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
28
28
  enumerable: true,
29
- get: function () { return chunkX7SWYOG3_cjs.FFIDSubscriptionBadge; }
29
+ get: function () { return chunkWG2YHUGO_cjs.FFIDSubscriptionBadge; }
30
30
  });
31
31
  Object.defineProperty(exports, "FFIDUserMenu", {
32
32
  enumerable: true,
33
- get: function () { return chunkX7SWYOG3_cjs.FFIDUserMenu; }
33
+ get: function () { return chunkWG2YHUGO_cjs.FFIDUserMenu; }
34
34
  });
@@ -1,3 +1,3 @@
1
- export { H as FFIDAnnouncementBadge, an as FFIDAnnouncementBadgeClassNames, ao as FFIDAnnouncementBadgeProps, I as FFIDAnnouncementList, ap as FFIDAnnouncementListClassNames, aq as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormLegalLayout, Y as FFIDInquiryFormOrganization, Z as FFIDInquiryFormPlaceholderContext, _ as FFIDInquiryFormPrefill, $ as FFIDInquiryFormProps, a0 as FFIDInquiryFormSubmitData, a1 as FFIDInquiryFormSubmitResult, a3 as FFIDLoginButton, ar as FFIDLoginButtonProps, a5 as FFIDOrganizationSwitcher, as as FFIDOrganizationSwitcherClassNames, at as FFIDOrganizationSwitcherProps, ab as FFIDSubscriptionBadge, au as FFIDSubscriptionBadgeClassNames, av as FFIDSubscriptionBadgeProps, ac as FFIDUserMenu, aw as FFIDUserMenuClassNames, ax as FFIDUserMenuProps } from '../index-BY_GDJm6.cjs';
1
+ export { H as FFIDAnnouncementBadge, an as FFIDAnnouncementBadgeClassNames, ao as FFIDAnnouncementBadgeProps, I as FFIDAnnouncementList, ap as FFIDAnnouncementListClassNames, aq as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormLegalLayout, Y as FFIDInquiryFormOrganization, Z as FFIDInquiryFormPlaceholderContext, _ as FFIDInquiryFormPrefill, $ as FFIDInquiryFormProps, a0 as FFIDInquiryFormSubmitData, a1 as FFIDInquiryFormSubmitResult, a3 as FFIDLoginButton, ar as FFIDLoginButtonProps, a5 as FFIDOrganizationSwitcher, as as FFIDOrganizationSwitcherClassNames, at as FFIDOrganizationSwitcherProps, ab as FFIDSubscriptionBadge, au as FFIDSubscriptionBadgeClassNames, av as FFIDSubscriptionBadgeProps, ac as FFIDUserMenu, aw as FFIDUserMenuClassNames, ax as FFIDUserMenuProps } from '../index-BMKhKhzF.cjs';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1,3 +1,3 @@
1
- export { H as FFIDAnnouncementBadge, an as FFIDAnnouncementBadgeClassNames, ao as FFIDAnnouncementBadgeProps, I as FFIDAnnouncementList, ap as FFIDAnnouncementListClassNames, aq as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormLegalLayout, Y as FFIDInquiryFormOrganization, Z as FFIDInquiryFormPlaceholderContext, _ as FFIDInquiryFormPrefill, $ as FFIDInquiryFormProps, a0 as FFIDInquiryFormSubmitData, a1 as FFIDInquiryFormSubmitResult, a3 as FFIDLoginButton, ar as FFIDLoginButtonProps, a5 as FFIDOrganizationSwitcher, as as FFIDOrganizationSwitcherClassNames, at as FFIDOrganizationSwitcherProps, ab as FFIDSubscriptionBadge, au as FFIDSubscriptionBadgeClassNames, av as FFIDSubscriptionBadgeProps, ac as FFIDUserMenu, aw as FFIDUserMenuClassNames, ax as FFIDUserMenuProps } from '../index-BY_GDJm6.js';
1
+ export { H as FFIDAnnouncementBadge, an as FFIDAnnouncementBadgeClassNames, ao as FFIDAnnouncementBadgeProps, I as FFIDAnnouncementList, ap as FFIDAnnouncementListClassNames, aq as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormLegalLayout, Y as FFIDInquiryFormOrganization, Z as FFIDInquiryFormPlaceholderContext, _ as FFIDInquiryFormPrefill, $ as FFIDInquiryFormProps, a0 as FFIDInquiryFormSubmitData, a1 as FFIDInquiryFormSubmitResult, a3 as FFIDLoginButton, ar as FFIDLoginButtonProps, a5 as FFIDOrganizationSwitcher, as as FFIDOrganizationSwitcherClassNames, at as FFIDOrganizationSwitcherProps, ab as FFIDSubscriptionBadge, au as FFIDSubscriptionBadgeClassNames, av as FFIDSubscriptionBadgeProps, ac as FFIDUserMenu, aw as FFIDUserMenuClassNames, ax as FFIDUserMenuProps } from '../index-BMKhKhzF.js';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1 +1 @@
1
- export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-5HDQ2KPC.js';
1
+ export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-53IBN7NO.js';
@@ -11,13 +11,16 @@ declare const DEFAULT_API_BASE_URL = "https://id.feelflow.net";
11
11
  *
12
12
  * Coverage:
13
13
  * - `openid email profile` — OIDC standard claims (always required)
14
- * - `subscription:read` — `/api/v1/subscriptions/ext/*` 系(プラン / 課金 gate)
14
+ * - `subscription:read` — `/api/v1/subscriptions/ext/*` 系の **読み取り**
15
+ * (`check` / `plans` / 契約詳細・シート参照。プラン / 課金 gate)
15
16
  * - `legal:read` — `/api/v1/legal/ext` 系(利用規約・同意状態)
16
17
  *
17
18
  * Add feature-specific scopes on top of this when needed (browser SDK):
18
19
  * - `analytics:read` for `/api/v1/ext/analytics/config`
19
20
  * - `organization:read` / `organization:write` for `/api/v1/organizations/ext/members`
20
- * - `subscription:write` for self-serve plan changes
21
+ * - `subscription:write` for every subscription **mutation** — `subscribe()`
22
+ * (`POST /ext/subscribe`), plan changes, and cancellation. `subscription:read`
23
+ * does NOT cover these despite the shared `/ext/*` prefix (FFID #4744)
21
24
  * - `legal:write` for recording user consent
22
25
  * - `profile:read` / `profile:write` for profile UI
23
26
  * - `organization:api-key` for org-level API key issuance
@@ -11,13 +11,16 @@ declare const DEFAULT_API_BASE_URL = "https://id.feelflow.net";
11
11
  *
12
12
  * Coverage:
13
13
  * - `openid email profile` — OIDC standard claims (always required)
14
- * - `subscription:read` — `/api/v1/subscriptions/ext/*` 系(プラン / 課金 gate)
14
+ * - `subscription:read` — `/api/v1/subscriptions/ext/*` 系の **読み取り**
15
+ * (`check` / `plans` / 契約詳細・シート参照。プラン / 課金 gate)
15
16
  * - `legal:read` — `/api/v1/legal/ext` 系(利用規約・同意状態)
16
17
  *
17
18
  * Add feature-specific scopes on top of this when needed (browser SDK):
18
19
  * - `analytics:read` for `/api/v1/ext/analytics/config`
19
20
  * - `organization:read` / `organization:write` for `/api/v1/organizations/ext/members`
20
- * - `subscription:write` for self-serve plan changes
21
+ * - `subscription:write` for every subscription **mutation** — `subscribe()`
22
+ * (`POST /ext/subscribe`), plan changes, and cancellation. `subscription:read`
23
+ * does NOT cover these despite the shared `/ext/*` prefix (FFID #4744)
21
24
  * - `legal:write` for recording user consent
22
25
  * - `profile:read` / `profile:write` for profile UI
23
26
  * - `organization:api-key` for org-level API key issuance
@@ -860,10 +860,31 @@ interface FFIDProvisionUserDryRun {
860
860
  * response. This mirrors how `FFIDApiResponse` is narrowed on `data` / `error`.
861
861
  */
862
862
  type FFIDProvisionUserResponse = FFIDProvisionUserOutcome | FFIDProvisionUserDryRun;
863
- /** Parameters for `sendProvisionWelcomeEmail` (#4449). */
863
+ /**
864
+ * Copy variant for `sendProvisionWelcomeEmail` (#5035).
865
+ *
866
+ * Both variants carry the same password-setup link; only the wording differs.
867
+ * Mirrors the server's `provisionEmailTemplateSchema` — the API rejects unknown
868
+ * values rather than defaulting, so a typo here surfaces as a 400 instead of
869
+ * mailing the wrong copy.
870
+ */
871
+ type FFIDProvisionEmailTemplate = 'account-provisioned' | 'password-reset';
872
+ /** Parameters for `sendProvisionWelcomeEmail` (#4449, #4470, #5035). */
864
873
  interface FFIDProvisionWelcomeEmailParams {
865
874
  /** Email of the provisioned FFID user to notify (must already exist). */
866
875
  email: string;
876
+ /**
877
+ * Which copy to send (#5035). Omit for the historical
878
+ * `account-provisioned`(「アカウント発行のお知らせ」); pass `'password-reset'`
879
+ * for an already-active member whose admin requested a reset
880
+ * (「パスワード再設定のご案内」).
881
+ */
882
+ template?: FFIDProvisionEmailTemplate;
883
+ /**
884
+ * Post-setup landing URL (#4470). Validated against the calling service's
885
+ * `allowed_redirect_uris` server-side. Omit to keep the FFID login page.
886
+ */
887
+ returnTo?: string;
867
888
  }
868
889
  /**
869
890
  * Response from `sendProvisionWelcomeEmail` (#4449).
@@ -1461,8 +1482,8 @@ declare const FFID_CATALOG_ENVIRONMENTS: readonly ["staging", "production"];
1461
1482
  type FFIDCatalogEnvironment = (typeof FFID_CATALOG_ENVIRONMENTS)[number];
1462
1483
  /**
1463
1484
  * Tax behavior of a published price.
1464
- * `inclusive`: displayed price contains Stripe-calculated tax (Personal/Pro).
1465
- * `exclusive`: tax is added on top of the base price (Team).
1485
+ * `inclusive`: displayed price contains Stripe-calculated tax.
1486
+ * `exclusive`: tax is added on top of the base price.
1466
1487
  */
1467
1488
  type FFIDTaxBehavior = 'inclusive' | 'exclusive';
1468
1489
  /**
@@ -1687,7 +1708,7 @@ interface FFIDOAuthUserInfoSubscription {
1687
1708
  memberRole: FFIDOAuthUserInfoMemberRole | null;
1688
1709
  organizationId: string | null;
1689
1710
  organizationName?: string | null | undefined;
1690
- /** Personal/organization split (#4531). `undefined` = older FFID predates the field; treat as unknown, don't guess. */
1711
+ /** Personal/organization split (#4531). `undefined` (older FFID predates the field) and `null` (introspect path; current FFID always sends non-null) are equally "unknown" — never fold either into `'organization'`. */
1691
1712
  accountType?: FFIDAccountType | null | undefined;
1692
1713
  hasSeatAssignment: boolean | null;
1693
1714
  /**
@@ -1787,7 +1808,7 @@ interface FFIDOAuthUserInfo {
1787
1808
  * are normalized from `subscription.member_role` when available.
1788
1809
  */
1789
1810
  organizationMemberRole?: FFIDOAuthUserInfoMemberRole | null | undefined;
1790
- /** Personal/organization split (#4531). Mirrors `organizationMemberRole` (same undefined/fallback rules). */
1811
+ /** Personal/organization split (#4531). Same precedence as `organizationMemberRole` (top-level first, then `subscription` derivation), but unlike it this can be `undefined` even with `subscription` present — `subscription.account_type` is itself optional (pre-#4531 FFID), whereas `subscription.member_role` is always populated and only degrades to `null`. */
1791
1812
  organizationAccountType?: FFIDAccountType | null | undefined;
1792
1813
  /**
1793
1814
  * Subscription summary for `(user, service, organization)`. Branch on
@@ -860,10 +860,31 @@ interface FFIDProvisionUserDryRun {
860
860
  * response. This mirrors how `FFIDApiResponse` is narrowed on `data` / `error`.
861
861
  */
862
862
  type FFIDProvisionUserResponse = FFIDProvisionUserOutcome | FFIDProvisionUserDryRun;
863
- /** Parameters for `sendProvisionWelcomeEmail` (#4449). */
863
+ /**
864
+ * Copy variant for `sendProvisionWelcomeEmail` (#5035).
865
+ *
866
+ * Both variants carry the same password-setup link; only the wording differs.
867
+ * Mirrors the server's `provisionEmailTemplateSchema` — the API rejects unknown
868
+ * values rather than defaulting, so a typo here surfaces as a 400 instead of
869
+ * mailing the wrong copy.
870
+ */
871
+ type FFIDProvisionEmailTemplate = 'account-provisioned' | 'password-reset';
872
+ /** Parameters for `sendProvisionWelcomeEmail` (#4449, #4470, #5035). */
864
873
  interface FFIDProvisionWelcomeEmailParams {
865
874
  /** Email of the provisioned FFID user to notify (must already exist). */
866
875
  email: string;
876
+ /**
877
+ * Which copy to send (#5035). Omit for the historical
878
+ * `account-provisioned`(「アカウント発行のお知らせ」); pass `'password-reset'`
879
+ * for an already-active member whose admin requested a reset
880
+ * (「パスワード再設定のご案内」).
881
+ */
882
+ template?: FFIDProvisionEmailTemplate;
883
+ /**
884
+ * Post-setup landing URL (#4470). Validated against the calling service's
885
+ * `allowed_redirect_uris` server-side. Omit to keep the FFID login page.
886
+ */
887
+ returnTo?: string;
867
888
  }
868
889
  /**
869
890
  * Response from `sendProvisionWelcomeEmail` (#4449).
@@ -1461,8 +1482,8 @@ declare const FFID_CATALOG_ENVIRONMENTS: readonly ["staging", "production"];
1461
1482
  type FFIDCatalogEnvironment = (typeof FFID_CATALOG_ENVIRONMENTS)[number];
1462
1483
  /**
1463
1484
  * Tax behavior of a published price.
1464
- * `inclusive`: displayed price contains Stripe-calculated tax (Personal/Pro).
1465
- * `exclusive`: tax is added on top of the base price (Team).
1485
+ * `inclusive`: displayed price contains Stripe-calculated tax.
1486
+ * `exclusive`: tax is added on top of the base price.
1466
1487
  */
1467
1488
  type FFIDTaxBehavior = 'inclusive' | 'exclusive';
1468
1489
  /**
@@ -1687,7 +1708,7 @@ interface FFIDOAuthUserInfoSubscription {
1687
1708
  memberRole: FFIDOAuthUserInfoMemberRole | null;
1688
1709
  organizationId: string | null;
1689
1710
  organizationName?: string | null | undefined;
1690
- /** Personal/organization split (#4531). `undefined` = older FFID predates the field; treat as unknown, don't guess. */
1711
+ /** Personal/organization split (#4531). `undefined` (older FFID predates the field) and `null` (introspect path; current FFID always sends non-null) are equally "unknown" — never fold either into `'organization'`. */
1691
1712
  accountType?: FFIDAccountType | null | undefined;
1692
1713
  hasSeatAssignment: boolean | null;
1693
1714
  /**
@@ -1787,7 +1808,7 @@ interface FFIDOAuthUserInfo {
1787
1808
  * are normalized from `subscription.member_role` when available.
1788
1809
  */
1789
1810
  organizationMemberRole?: FFIDOAuthUserInfoMemberRole | null | undefined;
1790
- /** Personal/organization split (#4531). Mirrors `organizationMemberRole` (same undefined/fallback rules). */
1811
+ /** Personal/organization split (#4531). Same precedence as `organizationMemberRole` (top-level first, then `subscription` derivation), but unlike it this can be `undefined` even with `subscription` present — `subscription.account_type` is itself optional (pre-#4531 FFID), whereas `subscription.member_role` is always populated and only degrades to `null`. */
1791
1812
  organizationAccountType?: FFIDAccountType | null | undefined;
1792
1813
  /**
1793
1814
  * Subscription summary for `(user, service, organization)`. Branch on
@@ -342,7 +342,7 @@ interface FFIDOAuthUserInfoSubscription {
342
342
  memberRole: FFIDOAuthUserInfoMemberRole | null;
343
343
  organizationId: string | null;
344
344
  organizationName?: string | null | undefined;
345
- /** Personal/organization split (#4531). `undefined` = older FFID predates the field; treat as unknown, don't guess. */
345
+ /** Personal/organization split (#4531). `undefined` (older FFID predates the field) and `null` (introspect path; current FFID always sends non-null) are equally "unknown" — never fold either into `'organization'`. */
346
346
  accountType?: FFIDAccountType | null | undefined;
347
347
  hasSeatAssignment: boolean | null;
348
348
  /**
@@ -442,7 +442,7 @@ interface FFIDOAuthUserInfo {
442
442
  * are normalized from `subscription.member_role` when available.
443
443
  */
444
444
  organizationMemberRole?: FFIDOAuthUserInfoMemberRole | null | undefined;
445
- /** Personal/organization split (#4531). Mirrors `organizationMemberRole` (same undefined/fallback rules). */
445
+ /** Personal/organization split (#4531). Same precedence as `organizationMemberRole` (top-level first, then `subscription` derivation), but unlike it this can be `undefined` even with `subscription` present — `subscription.account_type` is itself optional (pre-#4531 FFID), whereas `subscription.member_role` is always populated and only degrades to `null`. */
446
446
  organizationAccountType?: FFIDAccountType | null | undefined;
447
447
  /**
448
448
  * Subscription summary for `(user, service, organization)`. Branch on
@@ -342,7 +342,7 @@ interface FFIDOAuthUserInfoSubscription {
342
342
  memberRole: FFIDOAuthUserInfoMemberRole | null;
343
343
  organizationId: string | null;
344
344
  organizationName?: string | null | undefined;
345
- /** Personal/organization split (#4531). `undefined` = older FFID predates the field; treat as unknown, don't guess. */
345
+ /** Personal/organization split (#4531). `undefined` (older FFID predates the field) and `null` (introspect path; current FFID always sends non-null) are equally "unknown" — never fold either into `'organization'`. */
346
346
  accountType?: FFIDAccountType | null | undefined;
347
347
  hasSeatAssignment: boolean | null;
348
348
  /**
@@ -442,7 +442,7 @@ interface FFIDOAuthUserInfo {
442
442
  * are normalized from `subscription.member_role` when available.
443
443
  */
444
444
  organizationMemberRole?: FFIDOAuthUserInfoMemberRole | null | undefined;
445
- /** Personal/organization split (#4531). Mirrors `organizationMemberRole` (same undefined/fallback rules). */
445
+ /** Personal/organization split (#4531). Same precedence as `organizationMemberRole` (top-level first, then `subscription` derivation), but unlike it this can be `undefined` even with `subscription` present — `subscription.account_type` is itself optional (pre-#4531 FFID), whereas `subscription.member_role` is always populated and only degrades to `null`. */
446
446
  organizationAccountType?: FFIDAccountType | null | undefined;
447
447
  /**
448
448
  * Subscription summary for `(user, service, organization)`. Branch on