@fonderie/client 1.2.0 → 1.4.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.
@@ -49,6 +49,7 @@ new FonderieClient(opts: IFonderieClientOptions): FonderieClient
49
49
  .webhooks: WebhooksClient
50
50
  .customers: CustomersClient
51
51
  .media: MediaClient
52
+ .config: ConfigClient
52
53
  .setAccessToken(token: string | undefined): void
53
54
  .clearCache(): void
54
55
  .setWorkspaceId(workspaceId: string | undefined): void
@@ -1592,4 +1593,39 @@ type SubscriberType = 'user' | 'workspace';
1592
1593
  function isMfaRequired(result: ILoginResult | IMfaRequiredResult): result is IMfaRequiredResult
1593
1594
 
1594
1595
  function describeLocation(loc: IRequestLocationDTO | null | undefined, countryName?: ((code: string) => string | undefined) | undefined): string | null
1596
+
1597
+ const CONFIG_KEY_PATTERN: RegExp
1598
+
1599
+ const CONFIG_VALUE_TYPES: readonly ConfigValueType[]
1600
+
1601
+ function castConfigValue(type: ConfigValueType, raw: string): CastResult
1602
+
1603
+ function configKeyProblem(key: string): string | null
1604
+
1605
+ function configValueType(value: unknown): ConfigValueType
1606
+
1607
+ function formatConfigValue(value: unknown, type?: ConfigValueType): string
1608
+
1609
+ type CastResult = {
1610
+ ok: true;
1611
+ value: unknown;
1612
+ } | {
1613
+ ok: false;
1614
+ error: string;
1615
+ };
1616
+
1617
+ type ConfigValueType = 'string' | 'number' | 'boolean' | 'json';
1618
+
1619
+ new ConfigClient(http: HttpClient, tokens: TokenStore): ConfigClient
1620
+ .load(): Promise<IRemoteConfigState>
1621
+ .get<T>(key: string, fallback: T): T
1622
+ .snapshot(): IRemoteConfigState
1623
+ .subscribe(listener: Listener): () => void
1624
+
1625
+ interface IRemoteConfigState {
1626
+ values: Readonly<Record<string, unknown>>;
1627
+ loadedAt: Date | null;
1628
+ isLoading: boolean;
1629
+ error: unknown;
1630
+ }
1595
1631
  ```
package/dist/index.cjs CHANGED
@@ -27,7 +27,10 @@ __export(index_exports, {
27
27
  AuthClient: () => AuthClient,
28
28
  BillingAdminClient: () => BillingAdminClient,
29
29
  BillingClient: () => BillingClient,
30
+ CONFIG_KEY_PATTERN: () => CONFIG_KEY_PATTERN,
31
+ CONFIG_VALUE_TYPES: () => CONFIG_VALUE_TYPES,
30
32
  ConfigAdminClient: () => ConfigAdminClient,
33
+ ConfigClient: () => ConfigClient,
31
34
  CourierAdminClient: () => CourierAdminClient,
32
35
  CustomersClient: () => CustomersClient,
33
36
  FonderieApiError: () => FonderieApiError,
@@ -35,8 +38,12 @@ __export(index_exports, {
35
38
  MediaClient: () => MediaClient,
36
39
  WebhooksClient: () => WebhooksClient,
37
40
  WorkspacesClient: () => WorkspacesClient,
41
+ castConfigValue: () => castConfigValue,
42
+ configKeyProblem: () => configKeyProblem,
43
+ configValueType: () => configValueType,
38
44
  createMemoryCache: () => createMemoryCache,
39
45
  describeLocation: () => describeLocation,
46
+ formatConfigValue: () => formatConfigValue,
40
47
  isMfaRequired: () => isMfaRequired
41
48
  });
42
49
  module.exports = __toCommonJS(index_exports);
@@ -1139,6 +1146,62 @@ var MediaClient = class {
1139
1146
  }
1140
1147
  };
1141
1148
 
1149
+ // src/modules/config.ts
1150
+ var ConfigClient = class {
1151
+ constructor(http, tokens) {
1152
+ this.http = http;
1153
+ this.tokens = tokens;
1154
+ }
1155
+ http;
1156
+ tokens;
1157
+ state = { values: {}, loadedAt: null, isLoading: false, error: null };
1158
+ listeners = /* @__PURE__ */ new Set();
1159
+ inFlight = null;
1160
+ // GET /config/public — fetch the public values into the shared snapshot.
1161
+ // Concurrent calls share one request. A failure keeps the previous values
1162
+ // (a flag that was on does not flicker off because one refresh failed) and
1163
+ // is reported in `error`; before any success, reads fall back to defaults.
1164
+ load() {
1165
+ if (this.inFlight) return this.inFlight;
1166
+ this.set({ isLoading: true });
1167
+ this.inFlight = this.http.request({
1168
+ method: "GET",
1169
+ path: "/config/public",
1170
+ // Sent when signed in, so per-user values can be served later
1171
+ // without a client change; the route itself is public.
1172
+ token: this.tokens.get()
1173
+ }).then(
1174
+ (res) => this.set({ values: res.result?.values ?? {}, loadedAt: /* @__PURE__ */ new Date(), isLoading: false, error: null }),
1175
+ (error) => this.set({ isLoading: false, error })
1176
+ ).finally(() => {
1177
+ this.inFlight = null;
1178
+ });
1179
+ return this.inFlight;
1180
+ }
1181
+ // One value, with the fallback used when the key is absent — before the
1182
+ // first load, when it failed, or when the server does not expose the key.
1183
+ // Pick the SAFE fallback (feature off), not the optimistic one.
1184
+ get(key, fallback) {
1185
+ return Object.hasOwn(this.state.values, key) ? this.state.values[key] : fallback;
1186
+ }
1187
+ // The whole current state (values, loadedAt, isLoading, error).
1188
+ snapshot() {
1189
+ return this.state;
1190
+ }
1191
+ // Called with the new state after every change; returns an unsubscribe.
1192
+ subscribe(listener) {
1193
+ this.listeners.add(listener);
1194
+ return () => {
1195
+ this.listeners.delete(listener);
1196
+ };
1197
+ }
1198
+ set(patch) {
1199
+ this.state = { ...this.state, ...patch };
1200
+ for (const listener of this.listeners) listener(this.state);
1201
+ return this.state;
1202
+ }
1203
+ };
1204
+
1142
1205
  // src/modules/webhooks.ts
1143
1206
  var WebhooksClient = class {
1144
1207
  constructor(http, tokens) {
@@ -1475,6 +1538,8 @@ var FonderieClient = class {
1475
1538
  webhooks;
1476
1539
  customers;
1477
1540
  media;
1541
+ /** Public remote config: flags and settings the server exposes to frontends. */
1542
+ config;
1478
1543
  http;
1479
1544
  tokens;
1480
1545
  workspaceId;
@@ -1498,6 +1563,7 @@ var FonderieClient = class {
1498
1563
  this.webhooks = new WebhooksClient(this.http, this.tokens);
1499
1564
  this.customers = new CustomersClient(this.http, this.tokens);
1500
1565
  this.media = new MediaClient(this.http, this.tokens);
1566
+ this.config = new ConfigClient(this.http, this.tokens);
1501
1567
  if (opts.workspaceId !== void 0) this.setWorkspaceId(opts.workspaceId);
1502
1568
  }
1503
1569
  // Single-flight refresh: POST /auth/refresh with the app-supplied refresh
@@ -2124,6 +2190,46 @@ function describeLocation(loc, countryName) {
2124
2190
  );
2125
2191
  return parts.length > 0 ? parts.join(", ") : null;
2126
2192
  }
2193
+
2194
+ // src/config-value.ts
2195
+ var CONFIG_VALUE_TYPES = ["string", "number", "boolean", "json"];
2196
+ function configValueType(value) {
2197
+ if (typeof value === "string") return "string";
2198
+ if (typeof value === "number") return "number";
2199
+ if (typeof value === "boolean") return "boolean";
2200
+ return "json";
2201
+ }
2202
+ function formatConfigValue(value, type = configValueType(value)) {
2203
+ if (type === "string") return typeof value === "string" ? value : String(value ?? "");
2204
+ if (type === "number") return typeof value === "number" ? String(value) : "";
2205
+ if (type === "boolean") return value === true ? "true" : "false";
2206
+ return JSON.stringify(value ?? null, null, 2);
2207
+ }
2208
+ function castConfigValue(type, raw) {
2209
+ if (type === "string") return { ok: true, value: raw };
2210
+ const t = raw.trim();
2211
+ if (type === "number") {
2212
+ if (t === "") return { ok: false, error: "Enter a number." };
2213
+ const n = Number(t);
2214
+ return Number.isFinite(n) ? { ok: true, value: n } : { ok: false, error: `"${raw}" is not a number.` };
2215
+ }
2216
+ if (type === "boolean") {
2217
+ const v = t.toLowerCase();
2218
+ if (["true", "1", "yes", "on"].includes(v)) return { ok: true, value: true };
2219
+ if (["false", "0", "no", "off"].includes(v)) return { ok: true, value: false };
2220
+ return { ok: false, error: "Use true or false." };
2221
+ }
2222
+ try {
2223
+ return { ok: true, value: JSON.parse(t) };
2224
+ } catch (err) {
2225
+ return { ok: false, error: `Not valid JSON: ${err.message}` };
2226
+ }
2227
+ }
2228
+ var CONFIG_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9._-]{0,127}$/;
2229
+ function configKeyProblem(key) {
2230
+ if (!key) return "Enter a key.";
2231
+ return CONFIG_KEY_PATTERN.test(key) ? null : 'Start with a letter; use letters, digits, ".", "_" or "-" (max 128).';
2232
+ }
2127
2233
  // Annotate the CommonJS export names for ESM import in node:
2128
2234
  0 && (module.exports = {
2129
2235
  AdminClient,
@@ -2133,7 +2239,10 @@ function describeLocation(loc, countryName) {
2133
2239
  AuthClient,
2134
2240
  BillingAdminClient,
2135
2241
  BillingClient,
2242
+ CONFIG_KEY_PATTERN,
2243
+ CONFIG_VALUE_TYPES,
2136
2244
  ConfigAdminClient,
2245
+ ConfigClient,
2137
2246
  CourierAdminClient,
2138
2247
  CustomersClient,
2139
2248
  FonderieApiError,
@@ -2141,8 +2250,12 @@ function describeLocation(loc, countryName) {
2141
2250
  MediaClient,
2142
2251
  WebhooksClient,
2143
2252
  WorkspacesClient,
2253
+ castConfigValue,
2254
+ configKeyProblem,
2255
+ configValueType,
2144
2256
  createMemoryCache,
2145
2257
  describeLocation,
2258
+ formatConfigValue,
2146
2259
  isMfaRequired
2147
2260
  });
2148
2261
  //# sourceMappingURL=index.cjs.map