@objectstack/rest 4.0.3 → 4.0.5
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 +94 -17
- package/dist/index.cjs +722 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +133 -2
- package/dist/index.d.ts +133 -2
- package/dist/index.js +712 -60
- package/dist/index.js.map +1 -1
- package/package.json +32 -6
- package/.turbo/turbo-build.log +0 -22
- package/CHANGELOG.md +0 -473
- package/src/index.ts +0 -12
- package/src/rest-api-plugin.ts +0 -72
- package/src/rest-server.ts +0 -691
- package/src/rest.test.ts +0 -672
- package/src/route-manager.ts +0 -308
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -10
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IHttpServer, RouteHandler, Plugin } from '@objectstack/core';
|
|
2
|
-
import
|
|
2
|
+
import * as System from '@objectstack/spec/system';
|
|
3
|
+
import * as Shared from '@objectstack/spec/shared';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
import { ObjectStackProtocol, RestServerConfig } from '@objectstack/spec/api';
|
|
5
6
|
|
|
@@ -154,6 +155,17 @@ declare class RouteGroupBuilder {
|
|
|
154
155
|
private resolvePath;
|
|
155
156
|
}
|
|
156
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Structural subset of `KernelManager` that RestServer needs in order to
|
|
160
|
+
* resolve a per-project protocol at request time. Typed locally to avoid
|
|
161
|
+
* an @objectstack/runtime → @objectstack/rest → @objectstack/runtime
|
|
162
|
+
* package cycle.
|
|
163
|
+
*/
|
|
164
|
+
interface RestKernelManager {
|
|
165
|
+
getOrCreate(projectId: string): Promise<{
|
|
166
|
+
getServiceAsync<T = unknown>(name: string): Promise<T>;
|
|
167
|
+
}>;
|
|
168
|
+
}
|
|
157
169
|
/**
|
|
158
170
|
* RestServer
|
|
159
171
|
*
|
|
@@ -181,11 +193,113 @@ declare class RouteGroupBuilder {
|
|
|
181
193
|
*
|
|
182
194
|
* restServer.registerRoutes();
|
|
183
195
|
*/
|
|
196
|
+
/**
|
|
197
|
+
* Minimal env registry shape consumed by the REST server for hostname →
|
|
198
|
+
* projectId resolution and `X-Project-Id` header validation on unscoped
|
|
199
|
+
* routes. Mirrors the surface of `EnvironmentDriverRegistry` defined in
|
|
200
|
+
* `@objectstack/service-cloud`.
|
|
201
|
+
*/
|
|
202
|
+
interface RestEnvRegistry {
|
|
203
|
+
resolveByHostname(hostname: string): Promise<{
|
|
204
|
+
projectId: string;
|
|
205
|
+
} | null | undefined>;
|
|
206
|
+
/**
|
|
207
|
+
* Look up a project by id. Returns a truthy value (typically an
|
|
208
|
+
* `IDataDriver`) when the project exists and is bound, `null` when
|
|
209
|
+
* unknown. The REST server only uses the truthiness; it does not
|
|
210
|
+
* touch the driver itself (the actual driver is loaded later via
|
|
211
|
+
* `KernelManager.getOrCreate(projectId)`).
|
|
212
|
+
*/
|
|
213
|
+
resolveById?(projectId: string): Promise<unknown | null>;
|
|
214
|
+
}
|
|
184
215
|
declare class RestServer {
|
|
185
216
|
private protocol;
|
|
186
217
|
private config;
|
|
187
218
|
private routeManager;
|
|
188
|
-
|
|
219
|
+
private kernelManager?;
|
|
220
|
+
private envRegistry?;
|
|
221
|
+
private defaultProjectIdProvider?;
|
|
222
|
+
private authServiceProvider?;
|
|
223
|
+
private objectQLProvider?;
|
|
224
|
+
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>);
|
|
225
|
+
/**
|
|
226
|
+
* Resolve the protocol for a given request. When `projectId` is present
|
|
227
|
+
* and a KernelManager is wired, fetch the per-project kernel's
|
|
228
|
+
* `protocol` service so metadata / data / UI reads hit the project's
|
|
229
|
+
* own registry and datastore.
|
|
230
|
+
*
|
|
231
|
+
* When `projectId` is absent on an unscoped route and an `envRegistry`
|
|
232
|
+
* is wired (runtime mode), the resolution chain is:
|
|
233
|
+
* 1. Hostname → projectId (`envRegistry.resolveByHostname`)
|
|
234
|
+
* 2. `X-Project-Id` header → projectId (`envRegistry.resolveById`)
|
|
235
|
+
* 3. Default-project fallback (`defaultProjectIdProvider`, set by
|
|
236
|
+
* `createSingleProjectPlugin`)
|
|
237
|
+
* 4. Control-plane protocol captured at boot.
|
|
238
|
+
*
|
|
239
|
+
* Special case: `projectId === 'platform'` is a reserved virtual id used
|
|
240
|
+
* by Studio to address the control plane through the regular project
|
|
241
|
+
* URL shape (`/projects/platform/...`). It is NOT a row in the projects
|
|
242
|
+
* table, so we must never call `KernelManager.getOrCreate('platform')`.
|
|
243
|
+
* Instead, return the control-plane protocol directly. This lets Studio
|
|
244
|
+
* (and any other client) speak a single, uniform URL family without
|
|
245
|
+
* duplicating route logic for the platform surface.
|
|
246
|
+
*/
|
|
247
|
+
private resolveProtocol;
|
|
248
|
+
/**
|
|
249
|
+
* Resolve the i18n service for the request's project (or control plane
|
|
250
|
+
* when no project id is in scope). Returns `undefined` when no service is
|
|
251
|
+
* registered, so callers can short-circuit and skip translation rather
|
|
252
|
+
* than failing.
|
|
253
|
+
*
|
|
254
|
+
* Mirrors `resolveProtocol`'s lookup chain: explicit `projectId` from the
|
|
255
|
+
* route → kernel-managed `i18n` service. Control-plane / unscoped
|
|
256
|
+
* requests intentionally return `undefined` because the platform kernel
|
|
257
|
+
* does not own per-app translation bundles.
|
|
258
|
+
*/
|
|
259
|
+
private resolveI18nService;
|
|
260
|
+
/**
|
|
261
|
+
* Resolve the request's execution context (RBAC/RLS/FLS) by looking up
|
|
262
|
+
* the better-auth session via the project's `auth` service. Returns
|
|
263
|
+
* `undefined` for anonymous requests so callers can pass `context` as-is
|
|
264
|
+
* to the protocol layer (the SecurityPlugin treats undefined as anon).
|
|
265
|
+
*/
|
|
266
|
+
private resolveExecCtx;
|
|
267
|
+
/**
|
|
268
|
+
* Build a `TranslationBundle` (`Record<locale, TranslationData>`) from an
|
|
269
|
+
* `II18nService` instance. Returns `undefined` when no locales are
|
|
270
|
+
* registered so callers can avoid translation work.
|
|
271
|
+
*/
|
|
272
|
+
private buildTranslationBundle;
|
|
273
|
+
/**
|
|
274
|
+
* Parse the highest-priority locale from an `Accept-Language` header.
|
|
275
|
+
* Falls back to a `?locale=` query parameter, then to the i18n service's
|
|
276
|
+
* default locale. Returns `undefined` when no preference is expressed
|
|
277
|
+
* (callers will then return untranslated metadata).
|
|
278
|
+
*/
|
|
279
|
+
private extractLocale;
|
|
280
|
+
/**
|
|
281
|
+
* Translate a single metadata document (view or action) when an i18n
|
|
282
|
+
* service is registered for the request's project and the requested
|
|
283
|
+
* locale yields a match. Falls through unchanged for unsupported types
|
|
284
|
+
* or missing translations.
|
|
285
|
+
*/
|
|
286
|
+
private translateMetaItem;
|
|
287
|
+
/**
|
|
288
|
+
* Translate a list of metadata documents using `translateMetaItem`.
|
|
289
|
+
*/
|
|
290
|
+
private translateMetaItems;
|
|
291
|
+
/**
|
|
292
|
+
* Pull the request hostname (without port) from a Node-style `req` or
|
|
293
|
+
* a Fetch-style request wrapper. Returns undefined when no Host header
|
|
294
|
+
* is available.
|
|
295
|
+
*/
|
|
296
|
+
private extractHostname;
|
|
297
|
+
/**
|
|
298
|
+
* Pull the `X-Project-Id` header from a Node- or Fetch-style request.
|
|
299
|
+
* Header names are case-insensitive; we probe both casings to cover
|
|
300
|
+
* adapters that don't normalize headers (e.g. raw Node http).
|
|
301
|
+
*/
|
|
302
|
+
private extractProjectIdHeader;
|
|
189
303
|
/**
|
|
190
304
|
* Normalize configuration with defaults
|
|
191
305
|
*/
|
|
@@ -194,8 +308,19 @@ declare class RestServer {
|
|
|
194
308
|
* Get the full API base path
|
|
195
309
|
*/
|
|
196
310
|
private getApiBasePath;
|
|
311
|
+
/**
|
|
312
|
+
* Get the project-scoped base path for a given unscoped base.
|
|
313
|
+
* Example: `/api/v1` → `/api/v1/projects/:projectId`.
|
|
314
|
+
*/
|
|
315
|
+
private getScopedBasePath;
|
|
197
316
|
/**
|
|
198
317
|
* Register all REST API routes
|
|
318
|
+
*
|
|
319
|
+
* When `enableProjectScoping` is true, routes are registered under
|
|
320
|
+
* `/api/v1/projects/:projectId/...`. The `projectResolution` strategy
|
|
321
|
+
* controls whether unscoped legacy routes remain available:
|
|
322
|
+
* - `required` → only scoped routes registered.
|
|
323
|
+
* - `optional` / `auto` → both scoped and unscoped routes registered.
|
|
199
324
|
*/
|
|
200
325
|
registerRoutes(): void;
|
|
201
326
|
/**
|
|
@@ -231,6 +356,12 @@ declare class RestServer {
|
|
|
231
356
|
interface RestApiPluginConfig {
|
|
232
357
|
serverServiceName?: string;
|
|
233
358
|
protocolServiceName?: string;
|
|
359
|
+
/**
|
|
360
|
+
* Optional override for the kernel-manager service name. When the service
|
|
361
|
+
* is registered (by @objectstack/runtime's MultiProjectPlugin), scoped
|
|
362
|
+
* routes resolve per-project protocols at request time.
|
|
363
|
+
*/
|
|
364
|
+
kernelManagerServiceName?: string;
|
|
234
365
|
api?: RestServerConfig;
|
|
235
366
|
}
|
|
236
367
|
/**
|