@objectstack/rest 5.1.0 → 6.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/dist/index.d.cts CHANGED
@@ -162,7 +162,7 @@ declare class RouteGroupBuilder {
162
162
  * package cycle.
163
163
  */
164
164
  interface RestKernelManager {
165
- getOrCreate(projectId: string): Promise<{
165
+ getOrCreate(environmentId: string): Promise<{
166
166
  getServiceAsync<T = unknown>(name: string): Promise<T>;
167
167
  }>;
168
168
  }
@@ -195,22 +195,22 @@ interface RestKernelManager {
195
195
  */
196
196
  /**
197
197
  * Minimal env registry shape consumed by the REST server for hostname →
198
- * projectId resolution and `X-Project-Id` header validation on unscoped
198
+ * environmentId resolution and `X-Environment-Id` header validation on unscoped
199
199
  * routes. Mirrors the surface of `EnvironmentDriverRegistry` defined in
200
200
  * `@objectstack/service-cloud`.
201
201
  */
202
202
  interface RestEnvRegistry {
203
203
  resolveByHostname(hostname: string): Promise<{
204
- projectId: string;
204
+ environmentId: string;
205
205
  } | null | undefined>;
206
206
  /**
207
207
  * Look up a project by id. Returns a truthy value (typically an
208
208
  * `IDataDriver`) when the project exists and is bound, `null` when
209
209
  * unknown. The REST server only uses the truthiness; it does not
210
210
  * touch the driver itself (the actual driver is loaded later via
211
- * `KernelManager.getOrCreate(projectId)`).
211
+ * `KernelManager.getOrCreate(environmentId)`).
212
212
  */
213
- resolveById?(projectId: string): Promise<unknown | null>;
213
+ resolveById?(environmentId: string): Promise<unknown | null>;
214
214
  }
215
215
  declare class RestServer {
216
216
  private protocol;
@@ -218,7 +218,7 @@ declare class RestServer {
218
218
  private routeManager;
219
219
  private kernelManager?;
220
220
  private envRegistry?;
221
- private defaultProjectIdProvider?;
221
+ private defaultEnvironmentIdProvider?;
222
222
  private authServiceProvider?;
223
223
  private objectQLProvider?;
224
224
  private emailServiceProvider?;
@@ -227,22 +227,22 @@ declare class RestServer {
227
227
  private approvalsServiceProvider?;
228
228
  private sharingRulesServiceProvider?;
229
229
  private i18nServiceProvider?;
230
- constructor(server: IHttpServer, protocol: ObjectStackProtocol, config?: RestServerConfig, kernelManager?: RestKernelManager, envRegistry?: RestEnvRegistry, defaultProjectIdProvider?: () => string | undefined, authServiceProvider?: (projectId?: string) => Promise<any | undefined>, objectQLProvider?: (projectId?: string) => Promise<any | undefined>, emailServiceProvider?: (projectId?: string) => Promise<any | undefined>, sharingServiceProvider?: (projectId?: string) => Promise<any | undefined>, reportsServiceProvider?: (projectId?: string) => Promise<any | undefined>, approvalsServiceProvider?: (projectId?: string) => Promise<any | undefined>, sharingRulesServiceProvider?: (projectId?: string) => Promise<any | undefined>, i18nServiceProvider?: (projectId?: string) => Promise<any | undefined>);
230
+ constructor(server: IHttpServer, protocol: ObjectStackProtocol, config?: RestServerConfig, kernelManager?: RestKernelManager, envRegistry?: RestEnvRegistry, defaultEnvironmentIdProvider?: () => string | undefined, authServiceProvider?: (environmentId?: string) => Promise<any | undefined>, objectQLProvider?: (environmentId?: string) => Promise<any | undefined>, emailServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingServiceProvider?: (environmentId?: string) => Promise<any | undefined>, reportsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, approvalsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingRulesServiceProvider?: (environmentId?: string) => Promise<any | undefined>, i18nServiceProvider?: (environmentId?: string) => Promise<any | undefined>);
231
231
  /**
232
- * Resolve the protocol for a given request. When `projectId` is present
232
+ * Resolve the protocol for a given request. When `environmentId` is present
233
233
  * and a KernelManager is wired, fetch the per-project kernel's
234
234
  * `protocol` service so metadata / data / UI reads hit the project's
235
235
  * own registry and datastore.
236
236
  *
237
- * When `projectId` is absent on an unscoped route and an `envRegistry`
237
+ * When `environmentId` is absent on an unscoped route and an `envRegistry`
238
238
  * is wired (runtime mode), the resolution chain is:
239
- * 1. Hostname → projectId (`envRegistry.resolveByHostname`)
240
- * 2. `X-Project-Id` header → projectId (`envRegistry.resolveById`)
241
- * 3. Default-project fallback (`defaultProjectIdProvider`, set by
242
- * `createSingleProjectPlugin`)
239
+ * 1. Hostname → environmentId (`envRegistry.resolveByHostname`)
240
+ * 2. `X-Environment-Id` header → environmentId (`envRegistry.resolveById`)
241
+ * 3. Default-project fallback (`defaultEnvironmentIdProvider`, set by
242
+ * `createSingleEnvironmentPlugin`)
243
243
  * 4. Control-plane protocol captured at boot.
244
244
  *
245
- * Special case: `projectId === 'platform'` is a reserved virtual id used
245
+ * Special case: `environmentId === 'platform'` is a reserved virtual id used
246
246
  * by Studio to address the control plane through the regular project
247
247
  * URL shape (`/projects/platform/...`). It is NOT a row in the projects
248
248
  * table, so we must never call `KernelManager.getOrCreate('platform')`.
@@ -257,7 +257,7 @@ declare class RestServer {
257
257
  * registered, so callers can short-circuit and skip translation rather
258
258
  * than failing.
259
259
  *
260
- * Mirrors `resolveProtocol`'s lookup chain: explicit `projectId` from the
260
+ * Mirrors `resolveProtocol`'s lookup chain: explicit `environmentId` from the
261
261
  * route → kernel-managed `i18n` service. Control-plane / unscoped
262
262
  * requests intentionally return `undefined` because the platform kernel
263
263
  * does not own per-app translation bundles.
@@ -311,7 +311,7 @@ declare class RestServer {
311
311
  */
312
312
  private extractHostname;
313
313
  /**
314
- * Pull the `X-Project-Id` header from a Node- or Fetch-style request.
314
+ * Pull the `X-Environment-Id` header from a Node- or Fetch-style request.
315
315
  * Header names are case-insensitive; we probe both casings to cover
316
316
  * adapters that don't normalize headers (e.g. raw Node http).
317
317
  */
@@ -326,14 +326,14 @@ declare class RestServer {
326
326
  private getApiBasePath;
327
327
  /**
328
328
  * Get the project-scoped base path for a given unscoped base.
329
- * Example: `/api/v1` → `/api/v1/projects/:projectId`.
329
+ * Example: `/api/v1` → `/api/v1/environments/:environmentId`.
330
330
  */
331
331
  private getScopedBasePath;
332
332
  /**
333
333
  * Register all REST API routes
334
334
  *
335
335
  * When `enableProjectScoping` is true, routes are registered under
336
- * `/api/v1/projects/:projectId/...`. The `projectResolution` strategy
336
+ * `/api/v1/environments/:environmentId/...`. The `projectResolution` strategy
337
337
  * controls whether unscoped legacy routes remain available:
338
338
  * - `required` → only scoped routes registered.
339
339
  * - `optional` / `auto` → both scoped and unscoped routes registered.
@@ -343,6 +343,31 @@ declare class RestServer {
343
343
  * Register discovery endpoints
344
344
  */
345
345
  private registerDiscoveryEndpoints;
346
+ /**
347
+ * Register OpenAPI 3.1 spec + interactive docs viewer.
348
+ *
349
+ * GET <basePath>/openapi.json → enriched OpenAPI document
350
+ * GET <basePath>/docs → Scalar-rendered HTML (CDN, no dep)
351
+ *
352
+ * Enrichment at request time:
353
+ * - servers[0].url — derived from the request's Host header
354
+ * - paths — `{object}` placeholders expanded into
355
+ * one concrete path per registered object
356
+ * from the protocol's discovery metadata
357
+ *
358
+ * The base spec is loaded lazily from @objectstack/spec/openapi.json
359
+ * (shipped pre-generated by spec's build pipeline) so we don't pay
360
+ * the cost of regenerating on every request, and a missing or
361
+ * malformed file degrades to a stub instead of crashing.
362
+ */
363
+ private registerOpenApiEndpoints;
364
+ /**
365
+ * Lazily load the OpenAPI spec JSON shipped by @objectstack/spec.
366
+ * Cached after first read. Resilient to missing files / parse errors
367
+ * so a degraded environment still boots.
368
+ */
369
+ private _openApiSpecCache;
370
+ private loadOpenApiSpec;
346
371
  /**
347
372
  * Register metadata endpoints
348
373
  */
@@ -511,7 +536,7 @@ interface RestApiPluginConfig {
511
536
  /**
512
537
  * Optional override for the kernel-manager service name. When the service
513
538
  * is registered (by @objectstack/runtime's MultiProjectPlugin), scoped
514
- * routes resolve per-project protocols at request time.
539
+ * routes resolve per-environment protocols at request time.
515
540
  */
516
541
  kernelManagerServiceName?: string;
517
542
  api?: RestServerConfig;
package/dist/index.d.ts CHANGED
@@ -162,7 +162,7 @@ declare class RouteGroupBuilder {
162
162
  * package cycle.
163
163
  */
164
164
  interface RestKernelManager {
165
- getOrCreate(projectId: string): Promise<{
165
+ getOrCreate(environmentId: string): Promise<{
166
166
  getServiceAsync<T = unknown>(name: string): Promise<T>;
167
167
  }>;
168
168
  }
@@ -195,22 +195,22 @@ interface RestKernelManager {
195
195
  */
196
196
  /**
197
197
  * Minimal env registry shape consumed by the REST server for hostname →
198
- * projectId resolution and `X-Project-Id` header validation on unscoped
198
+ * environmentId resolution and `X-Environment-Id` header validation on unscoped
199
199
  * routes. Mirrors the surface of `EnvironmentDriverRegistry` defined in
200
200
  * `@objectstack/service-cloud`.
201
201
  */
202
202
  interface RestEnvRegistry {
203
203
  resolveByHostname(hostname: string): Promise<{
204
- projectId: string;
204
+ environmentId: string;
205
205
  } | null | undefined>;
206
206
  /**
207
207
  * Look up a project by id. Returns a truthy value (typically an
208
208
  * `IDataDriver`) when the project exists and is bound, `null` when
209
209
  * unknown. The REST server only uses the truthiness; it does not
210
210
  * touch the driver itself (the actual driver is loaded later via
211
- * `KernelManager.getOrCreate(projectId)`).
211
+ * `KernelManager.getOrCreate(environmentId)`).
212
212
  */
213
- resolveById?(projectId: string): Promise<unknown | null>;
213
+ resolveById?(environmentId: string): Promise<unknown | null>;
214
214
  }
215
215
  declare class RestServer {
216
216
  private protocol;
@@ -218,7 +218,7 @@ declare class RestServer {
218
218
  private routeManager;
219
219
  private kernelManager?;
220
220
  private envRegistry?;
221
- private defaultProjectIdProvider?;
221
+ private defaultEnvironmentIdProvider?;
222
222
  private authServiceProvider?;
223
223
  private objectQLProvider?;
224
224
  private emailServiceProvider?;
@@ -227,22 +227,22 @@ declare class RestServer {
227
227
  private approvalsServiceProvider?;
228
228
  private sharingRulesServiceProvider?;
229
229
  private i18nServiceProvider?;
230
- constructor(server: IHttpServer, protocol: ObjectStackProtocol, config?: RestServerConfig, kernelManager?: RestKernelManager, envRegistry?: RestEnvRegistry, defaultProjectIdProvider?: () => string | undefined, authServiceProvider?: (projectId?: string) => Promise<any | undefined>, objectQLProvider?: (projectId?: string) => Promise<any | undefined>, emailServiceProvider?: (projectId?: string) => Promise<any | undefined>, sharingServiceProvider?: (projectId?: string) => Promise<any | undefined>, reportsServiceProvider?: (projectId?: string) => Promise<any | undefined>, approvalsServiceProvider?: (projectId?: string) => Promise<any | undefined>, sharingRulesServiceProvider?: (projectId?: string) => Promise<any | undefined>, i18nServiceProvider?: (projectId?: string) => Promise<any | undefined>);
230
+ constructor(server: IHttpServer, protocol: ObjectStackProtocol, config?: RestServerConfig, kernelManager?: RestKernelManager, envRegistry?: RestEnvRegistry, defaultEnvironmentIdProvider?: () => string | undefined, authServiceProvider?: (environmentId?: string) => Promise<any | undefined>, objectQLProvider?: (environmentId?: string) => Promise<any | undefined>, emailServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingServiceProvider?: (environmentId?: string) => Promise<any | undefined>, reportsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, approvalsServiceProvider?: (environmentId?: string) => Promise<any | undefined>, sharingRulesServiceProvider?: (environmentId?: string) => Promise<any | undefined>, i18nServiceProvider?: (environmentId?: string) => Promise<any | undefined>);
231
231
  /**
232
- * Resolve the protocol for a given request. When `projectId` is present
232
+ * Resolve the protocol for a given request. When `environmentId` is present
233
233
  * and a KernelManager is wired, fetch the per-project kernel's
234
234
  * `protocol` service so metadata / data / UI reads hit the project's
235
235
  * own registry and datastore.
236
236
  *
237
- * When `projectId` is absent on an unscoped route and an `envRegistry`
237
+ * When `environmentId` is absent on an unscoped route and an `envRegistry`
238
238
  * is wired (runtime mode), the resolution chain is:
239
- * 1. Hostname → projectId (`envRegistry.resolveByHostname`)
240
- * 2. `X-Project-Id` header → projectId (`envRegistry.resolveById`)
241
- * 3. Default-project fallback (`defaultProjectIdProvider`, set by
242
- * `createSingleProjectPlugin`)
239
+ * 1. Hostname → environmentId (`envRegistry.resolveByHostname`)
240
+ * 2. `X-Environment-Id` header → environmentId (`envRegistry.resolveById`)
241
+ * 3. Default-project fallback (`defaultEnvironmentIdProvider`, set by
242
+ * `createSingleEnvironmentPlugin`)
243
243
  * 4. Control-plane protocol captured at boot.
244
244
  *
245
- * Special case: `projectId === 'platform'` is a reserved virtual id used
245
+ * Special case: `environmentId === 'platform'` is a reserved virtual id used
246
246
  * by Studio to address the control plane through the regular project
247
247
  * URL shape (`/projects/platform/...`). It is NOT a row in the projects
248
248
  * table, so we must never call `KernelManager.getOrCreate('platform')`.
@@ -257,7 +257,7 @@ declare class RestServer {
257
257
  * registered, so callers can short-circuit and skip translation rather
258
258
  * than failing.
259
259
  *
260
- * Mirrors `resolveProtocol`'s lookup chain: explicit `projectId` from the
260
+ * Mirrors `resolveProtocol`'s lookup chain: explicit `environmentId` from the
261
261
  * route → kernel-managed `i18n` service. Control-plane / unscoped
262
262
  * requests intentionally return `undefined` because the platform kernel
263
263
  * does not own per-app translation bundles.
@@ -311,7 +311,7 @@ declare class RestServer {
311
311
  */
312
312
  private extractHostname;
313
313
  /**
314
- * Pull the `X-Project-Id` header from a Node- or Fetch-style request.
314
+ * Pull the `X-Environment-Id` header from a Node- or Fetch-style request.
315
315
  * Header names are case-insensitive; we probe both casings to cover
316
316
  * adapters that don't normalize headers (e.g. raw Node http).
317
317
  */
@@ -326,14 +326,14 @@ declare class RestServer {
326
326
  private getApiBasePath;
327
327
  /**
328
328
  * Get the project-scoped base path for a given unscoped base.
329
- * Example: `/api/v1` → `/api/v1/projects/:projectId`.
329
+ * Example: `/api/v1` → `/api/v1/environments/:environmentId`.
330
330
  */
331
331
  private getScopedBasePath;
332
332
  /**
333
333
  * Register all REST API routes
334
334
  *
335
335
  * When `enableProjectScoping` is true, routes are registered under
336
- * `/api/v1/projects/:projectId/...`. The `projectResolution` strategy
336
+ * `/api/v1/environments/:environmentId/...`. The `projectResolution` strategy
337
337
  * controls whether unscoped legacy routes remain available:
338
338
  * - `required` → only scoped routes registered.
339
339
  * - `optional` / `auto` → both scoped and unscoped routes registered.
@@ -343,6 +343,31 @@ declare class RestServer {
343
343
  * Register discovery endpoints
344
344
  */
345
345
  private registerDiscoveryEndpoints;
346
+ /**
347
+ * Register OpenAPI 3.1 spec + interactive docs viewer.
348
+ *
349
+ * GET <basePath>/openapi.json → enriched OpenAPI document
350
+ * GET <basePath>/docs → Scalar-rendered HTML (CDN, no dep)
351
+ *
352
+ * Enrichment at request time:
353
+ * - servers[0].url — derived from the request's Host header
354
+ * - paths — `{object}` placeholders expanded into
355
+ * one concrete path per registered object
356
+ * from the protocol's discovery metadata
357
+ *
358
+ * The base spec is loaded lazily from @objectstack/spec/openapi.json
359
+ * (shipped pre-generated by spec's build pipeline) so we don't pay
360
+ * the cost of regenerating on every request, and a missing or
361
+ * malformed file degrades to a stub instead of crashing.
362
+ */
363
+ private registerOpenApiEndpoints;
364
+ /**
365
+ * Lazily load the OpenAPI spec JSON shipped by @objectstack/spec.
366
+ * Cached after first read. Resilient to missing files / parse errors
367
+ * so a degraded environment still boots.
368
+ */
369
+ private _openApiSpecCache;
370
+ private loadOpenApiSpec;
346
371
  /**
347
372
  * Register metadata endpoints
348
373
  */
@@ -511,7 +536,7 @@ interface RestApiPluginConfig {
511
536
  /**
512
537
  * Optional override for the kernel-manager service name. When the service
513
538
  * is registered (by @objectstack/runtime's MultiProjectPlugin), scoped
514
- * routes resolve per-project protocols at request time.
539
+ * routes resolve per-environment protocols at request time.
515
540
  */
516
541
  kernelManagerServiceName?: string;
517
542
  api?: RestServerConfig;