@objectstack/rest 4.0.5 → 4.1.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/index.d.cts CHANGED
@@ -221,7 +221,13 @@ declare class RestServer {
221
221
  private defaultProjectIdProvider?;
222
222
  private authServiceProvider?;
223
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>);
224
+ private emailServiceProvider?;
225
+ private sharingServiceProvider?;
226
+ private reportsServiceProvider?;
227
+ private approvalsServiceProvider?;
228
+ private sharingRulesServiceProvider?;
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>);
225
231
  /**
226
232
  * Resolve the protocol for a given request. When `projectId` is present
227
233
  * and a KernelManager is wired, fetch the per-project kernel's
@@ -257,6 +263,16 @@ declare class RestServer {
257
263
  * does not own per-app translation bundles.
258
264
  */
259
265
  private resolveI18nService;
266
+ /**
267
+ * Reject anonymous requests with HTTP 401 when `api.requireAuth` is set.
268
+ * Returns `true` if the response was sent and the caller should stop
269
+ * processing. Returns `false` to continue.
270
+ *
271
+ * The check is intentionally narrow: only `context?.userId` counts as
272
+ * "authenticated". `isSystem` flags are never set on inbound HTTP
273
+ * requests (they're internal-only), so they cannot bypass this gate.
274
+ */
275
+ private enforceAuth;
260
276
  /**
261
277
  * Resolve the request's execution context (RBAC/RLS/FLS) by looking up
262
278
  * the better-auth session via the project's `auth` service. Returns
@@ -339,6 +355,142 @@ declare class RestServer {
339
355
  * Register CRUD endpoints for data operations
340
356
  */
341
357
  private registerCrudEndpoints;
358
+ /**
359
+ * Register object-specific action endpoints that don't fit the
360
+ * generic CRUD shape. These are domain operations (Salesforce
361
+ * convertLead, etc.) where the protocol implementation does its own
362
+ * multi-record orchestration and we just need a thin HTTP route.
363
+ *
364
+ * POST {basePath}/data/lead/:id/convert — M10.6 lead conversion.
365
+ */
366
+ private registerDataActionEndpoints;
367
+ /**
368
+ * Register global cross-object search endpoint (M10.5).
369
+ * GET {basePath}/search?q=acme&objects=lead,account&limit=20&perObject=5
370
+ */
371
+ private registerSearchEndpoints;
372
+ /**
373
+ * Register email endpoints (M11.B1 / M10.7).
374
+ *
375
+ * POST {basePath}/email/send — send a transactional email via the
376
+ * `IEmailService` provider registered by EmailServicePlugin. Returns
377
+ * 501 when no provider is wired so deployments without email
378
+ * configured fail cleanly.
379
+ *
380
+ * Request body:
381
+ * {
382
+ * to: "a@b.com" | ["a@b.com", { name, address }],
383
+ * from?: ..., cc?: ..., bcc?: ..., replyTo?: ...,
384
+ * subject: string,
385
+ * text?: string, html?: string, // at least one required
386
+ * attachments?: [{ filename, content, contentType?, cid? }],
387
+ * headers?: { [name]: value },
388
+ * relatedObject?: string, relatedId?: string,
389
+ * }
390
+ */
391
+ private registerEmailEndpoints;
392
+ /**
393
+ * Register public (anonymous) form endpoints.
394
+ *
395
+ * Public forms are opt-in: a `FormView` becomes accessible to anonymous
396
+ * visitors only when `sharing.allowAnonymous === true` AND a
397
+ * `sharing.publicLink` slug is configured. Two routes are registered:
398
+ *
399
+ * GET {basePath}/forms/:slug → resolved form spec
400
+ * POST {basePath}/forms/:slug/submit → INSERT record (no auth required)
401
+ *
402
+ * Both routes bypass `enforceAuth` even when `requireAuth=true` on the
403
+ * deployment (e.g. ObjectOS multi-tenant). Security is delegated to the
404
+ * `guest_portal` permission set carried on the execution context — the
405
+ * SecurityPlugin enforces INSERT-only access to the target object. If
406
+ * the deployment hasn't registered a `guest_portal` profile, the
407
+ * security middleware falls open with `permissions: []` (no userId),
408
+ * matching the existing anonymous-access semantics; deployers must
409
+ * keep `requireAuth=true` deployments paired with a `guest_portal`
410
+ * profile (the CRM example does this) to enforce the INSERT-only
411
+ * contract.
412
+ *
413
+ * The matched FormView's parent ViewSchema is found by scanning
414
+ * `protocol.getMetaItems({ type: 'view' })`. For each entry we inspect
415
+ * `form.sharing` and every entry in `formViews`; the first FormView
416
+ * whose `sharing.publicLink` matches `/forms/:slug` (or just `:slug`)
417
+ * wins. The response carries the matched form view under `form` and
418
+ * the inferred target object, matching what the frontend's
419
+ * `mapViewSpecToEmbeddableConfig` expects.
420
+ */
421
+ private registerFormEndpoints;
422
+ /**
423
+ * Register record-level sharing endpoints (M11.C17).
424
+ *
425
+ * Surfaces `ISharingService` over HTTP so the UI can list, create
426
+ * and revoke per-record grants without going through ObjectQL. The
427
+ * three routes mirror the share-management drawer in Salesforce /
428
+ * ServiceNow:
429
+ *
430
+ * GET {basePath}/data/:object/:id/shares
431
+ * POST {basePath}/data/:object/:id/shares
432
+ * DELETE {basePath}/data/:object/:id/shares/:shareId
433
+ *
434
+ * All three resolve via `sharingServiceProvider`; routes return 501
435
+ * when no sharing service is configured so a deployment without the
436
+ * `@objectstack/plugin-sharing` plugin fails cleanly.
437
+ */
438
+ private registerSharingEndpoints;
439
+ /**
440
+ * Register sharing-rule endpoints (M10.17). Mirrors the existing
441
+ * sharing endpoints but operates on `sys_sharing_rule` rows.
442
+ *
443
+ * GET {basePath}/sharing/rules?object=&activeOnly=
444
+ * POST {basePath}/sharing/rules
445
+ * GET {basePath}/sharing/rules/:idOrName
446
+ * DELETE {basePath}/sharing/rules/:idOrName
447
+ * POST {basePath}/sharing/rules/:idOrName/evaluate
448
+ *
449
+ * Returns 501 when no sharing-rule service is configured.
450
+ */
451
+ private registerSharingRuleEndpoints;
452
+ /**
453
+ * Register saved-report + scheduled-digest endpoints (M11.C16).
454
+ *
455
+ * Surfaces `IReportService` over HTTP so the UI can build,
456
+ * run, and schedule reports without dropping to ObjectQL. Routes
457
+ * live at the top of the API surface (alongside `/approvals` and
458
+ * `/sharing`) — reports are a tenant-wide capability, not a record
459
+ * on a specific CRUD object:
460
+ *
461
+ * GET {basePath}/reports?object=&ownerId=
462
+ * POST {basePath}/reports
463
+ * GET {basePath}/reports/:id
464
+ * DELETE {basePath}/reports/:id
465
+ * POST {basePath}/reports/:id/run
466
+ * POST {basePath}/reports/:id/schedule
467
+ * GET {basePath}/reports/:id/schedules
468
+ * DELETE {basePath}/reports/schedules/:scheduleId
469
+ *
470
+ * All routes return 501 when `reportsServiceProvider` is unset so
471
+ * a deployment without `@objectstack/plugin-reports` fails cleanly.
472
+ */
473
+ private registerReportsEndpoints;
474
+ /**
475
+ * Register approval engine endpoints.
476
+ *
477
+ * Routes (all under {basePath}/approvals):
478
+ * GET /processes — list approval processes
479
+ * POST /processes — upsert (defineProcess)
480
+ * GET /processes/:id — get by id or name
481
+ * DELETE /processes/:id — delete process
482
+ * POST /requests — submit
483
+ * GET /requests — list (filters: status, object, recordId, approverId, submitterId)
484
+ * GET /requests/:id — get request
485
+ * POST /requests/:id/approve — approve current step
486
+ * POST /requests/:id/reject — reject current step
487
+ * POST /requests/:id/recall — recall (submitter only)
488
+ * GET /requests/:id/actions — audit trail
489
+ *
490
+ * Returns 501 when `approvalsServiceProvider` is unset so deployments
491
+ * without `@objectstack/plugin-approvals` fail cleanly.
492
+ */
493
+ private registerApprovalsEndpoints;
342
494
  /**
343
495
  * Register batch operation endpoints
344
496
  */
package/dist/index.d.ts CHANGED
@@ -221,7 +221,13 @@ declare class RestServer {
221
221
  private defaultProjectIdProvider?;
222
222
  private authServiceProvider?;
223
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>);
224
+ private emailServiceProvider?;
225
+ private sharingServiceProvider?;
226
+ private reportsServiceProvider?;
227
+ private approvalsServiceProvider?;
228
+ private sharingRulesServiceProvider?;
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>);
225
231
  /**
226
232
  * Resolve the protocol for a given request. When `projectId` is present
227
233
  * and a KernelManager is wired, fetch the per-project kernel's
@@ -257,6 +263,16 @@ declare class RestServer {
257
263
  * does not own per-app translation bundles.
258
264
  */
259
265
  private resolveI18nService;
266
+ /**
267
+ * Reject anonymous requests with HTTP 401 when `api.requireAuth` is set.
268
+ * Returns `true` if the response was sent and the caller should stop
269
+ * processing. Returns `false` to continue.
270
+ *
271
+ * The check is intentionally narrow: only `context?.userId` counts as
272
+ * "authenticated". `isSystem` flags are never set on inbound HTTP
273
+ * requests (they're internal-only), so they cannot bypass this gate.
274
+ */
275
+ private enforceAuth;
260
276
  /**
261
277
  * Resolve the request's execution context (RBAC/RLS/FLS) by looking up
262
278
  * the better-auth session via the project's `auth` service. Returns
@@ -339,6 +355,142 @@ declare class RestServer {
339
355
  * Register CRUD endpoints for data operations
340
356
  */
341
357
  private registerCrudEndpoints;
358
+ /**
359
+ * Register object-specific action endpoints that don't fit the
360
+ * generic CRUD shape. These are domain operations (Salesforce
361
+ * convertLead, etc.) where the protocol implementation does its own
362
+ * multi-record orchestration and we just need a thin HTTP route.
363
+ *
364
+ * POST {basePath}/data/lead/:id/convert — M10.6 lead conversion.
365
+ */
366
+ private registerDataActionEndpoints;
367
+ /**
368
+ * Register global cross-object search endpoint (M10.5).
369
+ * GET {basePath}/search?q=acme&objects=lead,account&limit=20&perObject=5
370
+ */
371
+ private registerSearchEndpoints;
372
+ /**
373
+ * Register email endpoints (M11.B1 / M10.7).
374
+ *
375
+ * POST {basePath}/email/send — send a transactional email via the
376
+ * `IEmailService` provider registered by EmailServicePlugin. Returns
377
+ * 501 when no provider is wired so deployments without email
378
+ * configured fail cleanly.
379
+ *
380
+ * Request body:
381
+ * {
382
+ * to: "a@b.com" | ["a@b.com", { name, address }],
383
+ * from?: ..., cc?: ..., bcc?: ..., replyTo?: ...,
384
+ * subject: string,
385
+ * text?: string, html?: string, // at least one required
386
+ * attachments?: [{ filename, content, contentType?, cid? }],
387
+ * headers?: { [name]: value },
388
+ * relatedObject?: string, relatedId?: string,
389
+ * }
390
+ */
391
+ private registerEmailEndpoints;
392
+ /**
393
+ * Register public (anonymous) form endpoints.
394
+ *
395
+ * Public forms are opt-in: a `FormView` becomes accessible to anonymous
396
+ * visitors only when `sharing.allowAnonymous === true` AND a
397
+ * `sharing.publicLink` slug is configured. Two routes are registered:
398
+ *
399
+ * GET {basePath}/forms/:slug → resolved form spec
400
+ * POST {basePath}/forms/:slug/submit → INSERT record (no auth required)
401
+ *
402
+ * Both routes bypass `enforceAuth` even when `requireAuth=true` on the
403
+ * deployment (e.g. ObjectOS multi-tenant). Security is delegated to the
404
+ * `guest_portal` permission set carried on the execution context — the
405
+ * SecurityPlugin enforces INSERT-only access to the target object. If
406
+ * the deployment hasn't registered a `guest_portal` profile, the
407
+ * security middleware falls open with `permissions: []` (no userId),
408
+ * matching the existing anonymous-access semantics; deployers must
409
+ * keep `requireAuth=true` deployments paired with a `guest_portal`
410
+ * profile (the CRM example does this) to enforce the INSERT-only
411
+ * contract.
412
+ *
413
+ * The matched FormView's parent ViewSchema is found by scanning
414
+ * `protocol.getMetaItems({ type: 'view' })`. For each entry we inspect
415
+ * `form.sharing` and every entry in `formViews`; the first FormView
416
+ * whose `sharing.publicLink` matches `/forms/:slug` (or just `:slug`)
417
+ * wins. The response carries the matched form view under `form` and
418
+ * the inferred target object, matching what the frontend's
419
+ * `mapViewSpecToEmbeddableConfig` expects.
420
+ */
421
+ private registerFormEndpoints;
422
+ /**
423
+ * Register record-level sharing endpoints (M11.C17).
424
+ *
425
+ * Surfaces `ISharingService` over HTTP so the UI can list, create
426
+ * and revoke per-record grants without going through ObjectQL. The
427
+ * three routes mirror the share-management drawer in Salesforce /
428
+ * ServiceNow:
429
+ *
430
+ * GET {basePath}/data/:object/:id/shares
431
+ * POST {basePath}/data/:object/:id/shares
432
+ * DELETE {basePath}/data/:object/:id/shares/:shareId
433
+ *
434
+ * All three resolve via `sharingServiceProvider`; routes return 501
435
+ * when no sharing service is configured so a deployment without the
436
+ * `@objectstack/plugin-sharing` plugin fails cleanly.
437
+ */
438
+ private registerSharingEndpoints;
439
+ /**
440
+ * Register sharing-rule endpoints (M10.17). Mirrors the existing
441
+ * sharing endpoints but operates on `sys_sharing_rule` rows.
442
+ *
443
+ * GET {basePath}/sharing/rules?object=&activeOnly=
444
+ * POST {basePath}/sharing/rules
445
+ * GET {basePath}/sharing/rules/:idOrName
446
+ * DELETE {basePath}/sharing/rules/:idOrName
447
+ * POST {basePath}/sharing/rules/:idOrName/evaluate
448
+ *
449
+ * Returns 501 when no sharing-rule service is configured.
450
+ */
451
+ private registerSharingRuleEndpoints;
452
+ /**
453
+ * Register saved-report + scheduled-digest endpoints (M11.C16).
454
+ *
455
+ * Surfaces `IReportService` over HTTP so the UI can build,
456
+ * run, and schedule reports without dropping to ObjectQL. Routes
457
+ * live at the top of the API surface (alongside `/approvals` and
458
+ * `/sharing`) — reports are a tenant-wide capability, not a record
459
+ * on a specific CRUD object:
460
+ *
461
+ * GET {basePath}/reports?object=&ownerId=
462
+ * POST {basePath}/reports
463
+ * GET {basePath}/reports/:id
464
+ * DELETE {basePath}/reports/:id
465
+ * POST {basePath}/reports/:id/run
466
+ * POST {basePath}/reports/:id/schedule
467
+ * GET {basePath}/reports/:id/schedules
468
+ * DELETE {basePath}/reports/schedules/:scheduleId
469
+ *
470
+ * All routes return 501 when `reportsServiceProvider` is unset so
471
+ * a deployment without `@objectstack/plugin-reports` fails cleanly.
472
+ */
473
+ private registerReportsEndpoints;
474
+ /**
475
+ * Register approval engine endpoints.
476
+ *
477
+ * Routes (all under {basePath}/approvals):
478
+ * GET /processes — list approval processes
479
+ * POST /processes — upsert (defineProcess)
480
+ * GET /processes/:id — get by id or name
481
+ * DELETE /processes/:id — delete process
482
+ * POST /requests — submit
483
+ * GET /requests — list (filters: status, object, recordId, approverId, submitterId)
484
+ * GET /requests/:id — get request
485
+ * POST /requests/:id/approve — approve current step
486
+ * POST /requests/:id/reject — reject current step
487
+ * POST /requests/:id/recall — recall (submitter only)
488
+ * GET /requests/:id/actions — audit trail
489
+ *
490
+ * Returns 501 when `approvalsServiceProvider` is unset so deployments
491
+ * without `@objectstack/plugin-approvals` fail cleanly.
492
+ */
493
+ private registerApprovalsEndpoints;
342
494
  /**
343
495
  * Register batch operation endpoints
344
496
  */