@classytic/arc-next 0.15.0 → 0.15.1

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/dist/api.d.ts CHANGED
@@ -114,9 +114,33 @@ interface BaseApiConfig {
114
114
  }
115
115
  declare class BaseApi<TDoc = Record<string, unknown>, TCreate = Partial<TDoc>, TUpdate = Partial<TDoc>> {
116
116
  readonly entity: string;
117
- readonly config: Required<Omit<BaseApiConfig, "client">>;
118
- readonly baseUrl: string;
117
+ /**
118
+ * `basePath` stays OPTIONAL here — it holds the explicit per-instance
119
+ * override and nothing else. The resolved value is {@link basePath}, which is
120
+ * computed per read; baking it in would defeat the whole point (see below).
121
+ */
122
+ readonly config: Required<Omit<BaseApiConfig, "client" | "basePath">> & Pick<BaseApiConfig, "basePath">;
119
123
  private readonly requestFn;
124
+ /**
125
+ * Per-instance override → the deployment's declared prefix → `/api/v1`.
126
+ *
127
+ * The middle step is what lets a package construct its own API internally and
128
+ * still land on a host mounted elsewhere. Without it the only options were
129
+ * "every consumer passes `basePath`" — impossible for an instance a package
130
+ * owns — or "every host mounts at `/api/v1`".
131
+ *
132
+ * ## Why this is a GETTER and not resolved in the constructor
133
+ *
134
+ * `configureClient()` runs inside a `"use client"` provider, which is LATER
135
+ * than module evaluation. A package's API instance is created at import time,
136
+ * so a constructor-time read would capture `/api/v1` before the deployment
137
+ * ever declared `/api`, and the fallback would win permanently — producing a
138
+ * 404 that renders as an empty list, which is the failure this was meant to
139
+ * fix. An explicit `basePath` is unaffected either way.
140
+ */
141
+ get basePath(): string;
142
+ /** `{basePath}/{entity}` — the resource root every request is built from. */
143
+ get baseUrl(): string;
120
144
  constructor(entity: string, config?: BaseApiConfig);
121
145
  /** Merge per-instance headers into request options */
122
146
  private withHeaders;
package/dist/api.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createQueryString, handleApiRequest } from "./client.js";
1
+ import { createQueryString, getBasePath, handleApiRequest } from "./client.js";
2
2
  import { STANDARD_RESERVED_PARAMS } from "@classytic/repo-core/query-parser";
3
3
 
4
4
  //#region src/api.ts
@@ -9,15 +9,43 @@ for (const verb of [
9
9
  ]) if (!STANDARD_RESERVED_PARAMS.has(verb)) throw new Error(`[arc-next] dispatch verb '${verb}' is not in repo-core STANDARD_RESERVED_PARAMS`);
10
10
  var BaseApi = class {
11
11
  entity;
12
+ /**
13
+ * `basePath` stays OPTIONAL here — it holds the explicit per-instance
14
+ * override and nothing else. The resolved value is {@link basePath}, which is
15
+ * computed per read; baking it in would defeat the whole point (see below).
16
+ */
12
17
  config;
13
- baseUrl;
14
18
  requestFn;
19
+ /**
20
+ * Per-instance override → the deployment's declared prefix → `/api/v1`.
21
+ *
22
+ * The middle step is what lets a package construct its own API internally and
23
+ * still land on a host mounted elsewhere. Without it the only options were
24
+ * "every consumer passes `basePath`" — impossible for an instance a package
25
+ * owns — or "every host mounts at `/api/v1`".
26
+ *
27
+ * ## Why this is a GETTER and not resolved in the constructor
28
+ *
29
+ * `configureClient()` runs inside a `"use client"` provider, which is LATER
30
+ * than module evaluation. A package's API instance is created at import time,
31
+ * so a constructor-time read would capture `/api/v1` before the deployment
32
+ * ever declared `/api`, and the fallback would win permanently — producing a
33
+ * 404 that renders as an empty list, which is the failure this was meant to
34
+ * fix. An explicit `basePath` is unaffected either way.
35
+ */
36
+ get basePath() {
37
+ return this.config.basePath ?? getBasePath() ?? "/api/v1";
38
+ }
39
+ /** `{basePath}/{entity}` — the resource root every request is built from. */
40
+ get baseUrl() {
41
+ return `${this.basePath}/${this.entity}`;
42
+ }
15
43
  constructor(entity, config = {}) {
16
44
  this.entity = entity;
17
45
  const client = config.client;
18
46
  this.requestFn = typeof client === "function" ? (method, endpoint, options) => client().request(method, endpoint, options) : client?.request ?? handleApiRequest;
19
47
  this.config = {
20
- basePath: config.basePath ?? "/api/v1",
48
+ ...config.basePath !== void 0 ? { basePath: config.basePath } : {},
21
49
  defaultParams: {
22
50
  limit: 10,
23
51
  page: 1,
@@ -26,7 +54,6 @@ var BaseApi = class {
26
54
  cache: config.cache ?? "no-store",
27
55
  headers: { ...config.headers || {} }
28
56
  };
29
- this.baseUrl = `${this.config.basePath}/${this.entity}`;
30
57
  }
31
58
  /** Merge per-instance headers into request options */
32
59
  withHeaders(options) {
package/dist/client.d.ts CHANGED
@@ -278,6 +278,23 @@ interface ClientEncryptionConfig {
278
278
  }
279
279
  interface ClientConfig {
280
280
  baseUrl: string;
281
+ /**
282
+ * Route prefix every API is mounted under, when it is not `/api/v1`.
283
+ *
284
+ * `BaseApi` defaults each instance to `/api/v1` and takes a per-instance
285
+ * `basePath` override. That covers an app's OWN api classes and nothing else:
286
+ * a package that constructs its own API internally — erp-shell's permission
287
+ * `platformApi`, an SDK preset — has no seam to be told, so on a host mounted
288
+ * anywhere but `/api/v1` it silently requests a URL that does not exist and
289
+ * the feature reads as "no data" rather than as a misconfiguration.
290
+ *
291
+ * Setting it here makes the mount point a property of the DEPLOYMENT, stated
292
+ * once, which is what it actually is. A per-instance `basePath` still wins,
293
+ * so nothing that already passes one changes.
294
+ *
295
+ * @example configureClient({ baseUrl, basePath: '/api' }) // host mounts at /api
296
+ */
297
+ basePath?: string;
281
298
  internalApiKey?: string;
282
299
  defaultHeaders?: Record<string, string>;
283
300
  /**
@@ -457,6 +474,15 @@ declare function configureClient(config: ClientConfig): void;
457
474
  declare function getAuthMode(): "bearer" | "cookie" | "header";
458
475
  /** Get the configured base URL. Returns empty string if not configured. */
459
476
  declare function getBaseUrl(): string;
477
+ /**
478
+ * The deployment's route prefix, or `null` when it has not declared one.
479
+ *
480
+ * `null` rather than the `/api/v1` default on purpose: the default belongs to
481
+ * `BaseApi`, which is the one place that should own it. Returning it here would
482
+ * put the same literal in two files, and the next person to change one would
483
+ * have no way to know about the other.
484
+ */
485
+ declare function getBasePath(): string | null;
460
486
  /** Whether auto-idempotency is enabled on the global client. */
461
487
  declare function isAutoIdempotency(): boolean;
462
488
  /**
@@ -1016,4 +1042,4 @@ declare const arc: {
1016
1042
  delete: <T = unknown>(path: string, opts?: ArcFetchOptions) => Promise<T>;
1017
1043
  };
1018
1044
  //#endregion
1019
- export { AfterResponseContext, AfterResponseInterceptor, ApiRequestOptions, ArcApiError, ArcApiErrorOptions, ArcClient, ArcClientConfig, ArcErrorCode, ArcFetchOptions, AuthConfig, AuthErrorContext, AuthErrorHandler, BeforeRequestContext, BeforeRequestInterceptor, BlobResponse, ClientConfig, ClientEncryptionConfig, HttpMethod, KNOWN_ARC_ERROR_CODES, NextFetchOptions, QuotaDetails, RetryConfig, ServerClientConfig, StreamUrlProtocol, TextResponse, TierRequirement, ToastHandler, UseRouterHook, _getAuthErrorHandler, _isAuthRecoverable, _resetArcFetchClient, _resetAuthRecovery, _resetAuthWarnings, _resolveRefreshedToken, _runAuthRecovery, arc, arcAuthHeaders, arcFetch, buildStreamUrl, configureAuth, configureClient, createAuthAwareClient, createAuthRefreshHandler, createClient, createQueryString, createServerClient, getAuthContext, getAuthMode, getBaseUrl, getClientAuthContext, getQuotaDetails, getTierRequirement, handleApiRequest, hasGlobalStaticAuth, isAbortError, isArcApiError, isArcErrorCode, isAutoIdempotency, isDuplicateKeyError, isOrgContextRequiredError, isQuotaExceeded, isTierRequiredError, isValidationError };
1045
+ export { AfterResponseContext, AfterResponseInterceptor, ApiRequestOptions, ArcApiError, ArcApiErrorOptions, ArcClient, ArcClientConfig, ArcErrorCode, ArcFetchOptions, AuthConfig, AuthErrorContext, AuthErrorHandler, BeforeRequestContext, BeforeRequestInterceptor, BlobResponse, ClientConfig, ClientEncryptionConfig, HttpMethod, KNOWN_ARC_ERROR_CODES, NextFetchOptions, QuotaDetails, RetryConfig, ServerClientConfig, StreamUrlProtocol, TextResponse, TierRequirement, ToastHandler, UseRouterHook, _getAuthErrorHandler, _isAuthRecoverable, _resetArcFetchClient, _resetAuthRecovery, _resetAuthWarnings, _resolveRefreshedToken, _runAuthRecovery, arc, arcAuthHeaders, arcFetch, buildStreamUrl, configureAuth, configureClient, createAuthAwareClient, createAuthRefreshHandler, createClient, createQueryString, createServerClient, getAuthContext, getAuthMode, getBasePath, getBaseUrl, getClientAuthContext, getQuotaDetails, getTierRequirement, handleApiRequest, hasGlobalStaticAuth, isAbortError, isArcApiError, isArcErrorCode, isAutoIdempotency, isDuplicateKeyError, isOrgContextRequiredError, isQuotaExceeded, isTierRequiredError, isValidationError };
package/dist/client.js CHANGED
@@ -337,6 +337,17 @@ function getAuthMode() {
337
337
  function getBaseUrl() {
338
338
  return clientConfig?.baseUrl ?? "";
339
339
  }
340
+ /**
341
+ * The deployment's route prefix, or `null` when it has not declared one.
342
+ *
343
+ * `null` rather than the `/api/v1` default on purpose: the default belongs to
344
+ * `BaseApi`, which is the one place that should own it. Returning it here would
345
+ * put the same literal in two files, and the next person to change one would
346
+ * have no way to know about the other.
347
+ */
348
+ function getBasePath() {
349
+ return clientConfig?.basePath ?? null;
350
+ }
340
351
  /** Whether auto-idempotency is enabled on the global client. */
341
352
  function isAutoIdempotency() {
342
353
  return clientConfig?.autoIdempotency ?? false;
@@ -1276,4 +1287,4 @@ const arc = {
1276
1287
  };
1277
1288
 
1278
1289
  //#endregion
1279
- export { ArcApiError, KNOWN_ARC_ERROR_CODES, _getAuthErrorHandler, _isAuthRecoverable, _resetArcFetchClient, _resetAuthRecovery, _resetAuthWarnings, _resolveRefreshedToken, _runAuthRecovery, arc, arcAuthHeaders, arcFetch, buildStreamUrl, configureAuth, configureClient, createAuthAwareClient, createAuthRefreshHandler, createClient, createQueryString, createServerClient, getAuthContext, getAuthMode, getBaseUrl, getClientAuthContext, getQuotaDetails, getTierRequirement, handleApiRequest, hasGlobalStaticAuth, isAbortError, isArcApiError, isArcErrorCode, isAutoIdempotency, isDuplicateKeyError, isOrgContextRequiredError, isQuotaExceeded, isTierRequiredError, isValidationError };
1290
+ export { ArcApiError, KNOWN_ARC_ERROR_CODES, _getAuthErrorHandler, _isAuthRecoverable, _resetArcFetchClient, _resetAuthRecovery, _resetAuthWarnings, _resolveRefreshedToken, _runAuthRecovery, arc, arcAuthHeaders, arcFetch, buildStreamUrl, configureAuth, configureClient, createAuthAwareClient, createAuthRefreshHandler, createClient, createQueryString, createServerClient, getAuthContext, getAuthMode, getBasePath, getBaseUrl, getClientAuthContext, getQuotaDetails, getTierRequirement, handleApiRequest, hasGlobalStaticAuth, isAbortError, isArcApiError, isArcErrorCode, isAutoIdempotency, isDuplicateKeyError, isOrgContextRequiredError, isQuotaExceeded, isTierRequiredError, isValidationError };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/arc-next",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "description": "React + TanStack Query SDK for Arc resources",
5
5
  "type": "module",
6
6
  "sideEffects": false,