diff --git a/.changeset/config.json b/.changeset/config.json index 8b4212a295..a970114509 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -40,7 +40,6 @@ "@objectstack/plugin-email", "@objectstack/plugin-hono-server", "@objectstack/mcp", - "@objectstack/plugin-msw", "@objectstack/plugin-org-scoping", "@objectstack/plugin-reports", "@objectstack/plugin-security", @@ -52,13 +51,7 @@ "@objectstack/connector-mcp", "@objectstack/connector-rest", "@objectstack/connector-slack", - "@objectstack/express", - "@objectstack/fastify", "@objectstack/hono", - "@objectstack/nestjs", - "@objectstack/nextjs", - "@objectstack/nuxt", - "@objectstack/sveltekit", "@objectstack/service-analytics", "@objectstack/service-automation", "@objectstack/service-cache", diff --git a/.changeset/v11-remove-unused-adapters.md b/.changeset/v11-remove-unused-adapters.md new file mode 100644 index 0000000000..4bdb4e745f --- /dev/null +++ b/.changeset/v11-remove-unused-adapters.md @@ -0,0 +1,23 @@ +--- +"@objectstack/client": major +"@objectstack/runtime": patch +--- + +Remove the unused HTTP framework adapters and the MSW plugin — the open edition ships the **Hono** adapter only. + +The `express` / `fastify` / `nextjs` / `nestjs` / `nuxt` / `sveltekit` adapters and +`@objectstack/plugin-msw` had **zero internal consumers** and were not dogfooded — +pure release/maintenance surface (and an untested-integration liability). They are +removed; `@objectstack/hono` (the adapter actually used, via `@objectstack/client`) +is kept. + +- Deleted packages: `@objectstack/express`, `@objectstack/fastify`, + `@objectstack/nextjs`, `@objectstack/nestjs`, `@objectstack/nuxt`, + `@objectstack/sveltekit`, `@objectstack/plugin-msw` (fixed group 73 → 66). +- `@objectstack/client`: dropped the `plugin-msw` / `msw` dev usage (MSW test removed). +- `HttpDispatcher` (the dispatch engine) is now used only by the Hono adapter + + the internal dispatcher-plugin, so its misleading `@deprecated → createDispatcherPlugin` + note (createDispatcherPlugin is a kernel plugin, not a drop-in) is corrected. + +Anyone needing another framework adapter can build one on the public +`HttpDispatcher` / `createDispatcherPlugin` API or maintain it out-of-tree. diff --git a/content/docs/references/ai/solution-blueprint.mdx b/content/docs/references/ai/solution-blueprint.mdx index e70591fd78..2fd1d00bf5 100644 --- a/content/docs/references/ai/solution-blueprint.mdx +++ b/content/docs/references/ai/solution-blueprint.mdx @@ -34,8 +34,8 @@ batch-draft. This is the safety valve for low-specificity input. ## TypeScript Usage ```typescript -import { BlueprintApp, BlueprintDashboard, BlueprintField, BlueprintNavItem, BlueprintObject, BlueprintSeed, BlueprintView, SolutionBlueprint, SolutionBlueprintStrict } from '@objectstack/spec/ai'; -import type { BlueprintApp, BlueprintDashboard, BlueprintField, BlueprintNavItem, BlueprintObject, BlueprintSeed, BlueprintView, SolutionBlueprint, SolutionBlueprintStrict } from '@objectstack/spec/ai'; +import { BlueprintApp, BlueprintDashboard, BlueprintField, BlueprintNavItem, BlueprintObject, BlueprintSeed, BlueprintView, BlueprintWidgetCondition, SolutionBlueprint, SolutionBlueprintStrict } from '@objectstack/spec/ai'; +import type { BlueprintApp, BlueprintDashboard, BlueprintField, BlueprintNavItem, BlueprintObject, BlueprintSeed, BlueprintView, BlueprintWidgetCondition, SolutionBlueprint, SolutionBlueprintStrict } from '@objectstack/spec/ai'; // Validate data const result = BlueprintApp.parse(data); @@ -78,7 +78,7 @@ const result = BlueprintApp.parse(data); | :--- | :--- | :--- | :--- | | **name** | `string` | ✅ | Field machine name (snake_case) | | **label** | `string` | optional | Human-readable field label | -| **type** | `Enum<'text' \| 'textarea' \| 'email' \| 'url' \| 'phone' \| 'password' \| 'secret' \| 'markdown' \| 'html' \| 'richtext' \| 'number' \| 'currency' \| 'percent' \| 'date' \| 'datetime' \| 'time' \| 'boolean' \| 'toggle' \| 'select' \| 'multiselect' \| 'radio' \| 'checkboxes' \| 'lookup' \| 'master_detail' \| 'tree' \| 'image' \| 'file' \| 'avatar' \| 'video' \| 'audio' \| 'formula' \| 'summary' \| 'autonumber' \| 'composite' \| 'repeater' \| 'record' \| 'location' \| 'address' \| 'code' \| 'json' \| 'color' \| 'rating' \| 'slider' \| 'signature' \| 'qrcode' \| 'progress' \| 'tags' \| 'vector'>` | ✅ | Field data type | +| **type** | `Enum<'text' \| 'textarea' \| 'email' \| 'url' \| 'phone' \| 'password' \| 'secret' \| 'markdown' \| 'html' \| 'richtext' \| 'number' \| 'currency' \| 'percent' \| 'date' \| 'datetime' \| 'time' \| 'boolean' \| 'toggle' \| 'select' \| 'multiselect' \| 'radio' \| 'checkboxes' \| 'lookup' \| 'master_detail' \| 'tree' \| 'user' \| 'image' \| 'file' \| 'avatar' \| 'video' \| 'audio' \| 'formula' \| 'summary' \| 'autonumber' \| 'composite' \| 'repeater' \| 'record' \| 'location' \| 'address' \| 'code' \| 'json' \| 'color' \| 'rating' \| 'slider' \| 'signature' \| 'qrcode' \| 'progress' \| 'tags' \| 'vector'>` | ✅ | Field data type | | **required** | `boolean` | optional | Whether the field is required | | **reference** | `string` | optional | Target object name for lookup / master_detail relationship fields | | **options** | `Object[]` | optional | Choices for select / multiselect / radio fields | @@ -140,6 +140,19 @@ const result = BlueprintApp.parse(data); | **groupBy** | `string` | optional | REQUIRED for kanban views: the select/status field whose options become the board columns (e.g. "stage", "status"). Without it a kanban renders as a plain list. | +--- + +## BlueprintWidgetCondition + +### Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **field** | `string` | ✅ | Field on the widget object to filter by (e.g. "stock_quantity", "status") | +| **op** | `Enum<'lt' \| 'lte' \| 'gt' \| 'gte' \| 'eq' \| 'ne'>` | ✅ | Comparison operator | +| **value** | `number \| string \| boolean` | ✅ | Comparison value (e.g. 10, "open") | + + --- ## SolutionBlueprint diff --git a/content/docs/references/api/auth-endpoints.mdx b/content/docs/references/api/auth-endpoints.mdx index 0ed2ec50e5..0976978246 100644 --- a/content/docs/references/api/auth-endpoints.mdx +++ b/content/docs/references/api/auth-endpoints.mdx @@ -61,6 +61,7 @@ const result = AuthEndpoint.parse(data); | **passkeys** | `boolean` | ✅ | Passkey/WebAuthn support enabled | | **magicLink** | `boolean` | ✅ | Magic link login enabled | | **organization** | `boolean` | ✅ | Multi-tenant organization support enabled | +| **ssoEnforced** | `boolean` | optional | SSO-only login enforced: the UI hides the local password form + self-registration (a break-glass "use a password" link remains) | --- diff --git a/content/docs/references/automation/execution.mdx b/content/docs/references/automation/execution.mdx index 705a7c1d12..34e1a43664 100644 --- a/content/docs/references/automation/execution.mdx +++ b/content/docs/references/automation/execution.mdx @@ -141,7 +141,7 @@ const result = Checkpoint.parse(data); | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | | **nodeId** | `string` | ✅ | Node ID that was executed | -| **nodeType** | `string` | ✅ | Node action type (e.g., "decision", "http_request") | +| **nodeType** | `string` | ✅ | Node action type (e.g., "decision", "http") | | **nodeLabel** | `string` | optional | Human-readable node label | | **status** | `Enum<'success' \| 'failure' \| 'skipped'>` | ✅ | Step execution result | | **startedAt** | `string` | ✅ | When the step started | diff --git a/content/docs/references/automation/flow.mdx b/content/docs/references/automation/flow.mdx index c814c76029..443ab3f4a6 100644 --- a/content/docs/references/automation/flow.mdx +++ b/content/docs/references/automation/flow.mdx @@ -74,7 +74,6 @@ const result = FlowNode.parse(data); * `delete_record` * `get_record` * `http` -* `http_request` * `notify` * `script` * `screen` diff --git a/content/docs/references/data/object.mdx b/content/docs/references/data/object.mdx index 32ed1caea5..d1b8afc858 100644 --- a/content/docs/references/data/object.mdx +++ b/content/docs/references/data/object.mdx @@ -14,8 +14,8 @@ API Operations Enum ## TypeScript Usage ```typescript -import { ApiMethod, Index, ObjectCapabilities, ObjectExternalBinding, ObjectOwnershipEnum, PartitioningConfig, SoftDeleteConfig, TenancyConfig, VersioningConfig } from '@objectstack/spec/data'; -import type { ApiMethod, Index, ObjectCapabilities, ObjectExternalBinding, ObjectOwnershipEnum, PartitioningConfig, SoftDeleteConfig, TenancyConfig, VersioningConfig } from '@objectstack/spec/data'; +import { ApiMethod, Index, ObjectAccessConfig, ObjectCapabilities, ObjectExternalBinding, ObjectOwnershipEnum, PartitioningConfig, SoftDeleteConfig, TenancyConfig, VersioningConfig } from '@objectstack/spec/data'; +import type { ApiMethod, Index, ObjectAccessConfig, ObjectCapabilities, ObjectExternalBinding, ObjectOwnershipEnum, PartitioningConfig, SoftDeleteConfig, TenancyConfig, VersioningConfig } from '@objectstack/spec/data'; // Validate data const result = ApiMethod.parse(data); @@ -58,6 +58,17 @@ const result = ApiMethod.parse(data); | **partial** | `string` | optional | Partial index condition (SQL WHERE clause for conditional indexes) | +--- + +## ObjectAccessConfig + +### Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **default** | `Enum<'public' \| 'private'>` | ✅ | Default exposure posture: public (covered by wildcard grants) | private (needs explicit grant; exempt from wildcard RLS). | + + --- ## ObjectCapabilities diff --git a/content/docs/references/security/index.mdx b/content/docs/references/security/index.mdx index a1fcd56c6d..4b94ae0a29 100644 --- a/content/docs/references/security/index.mdx +++ b/content/docs/references/security/index.mdx @@ -7,7 +7,6 @@ This section contains all protocol schemas for the security layer of ObjectStack - diff --git a/content/docs/references/security/meta.json b/content/docs/references/security/meta.json index e5d72448cc..1e70554bfd 100644 --- a/content/docs/references/security/meta.json +++ b/content/docs/references/security/meta.json @@ -2,7 +2,6 @@ "title": "Security Protocol", "pages": [ "permission", - "policy", "rls", "sharing", "territory" diff --git a/content/docs/references/security/policy.mdx b/content/docs/references/security/policy.mdx deleted file mode 100644 index ea6ac2b805..0000000000 --- a/content/docs/references/security/policy.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: Policy -description: Policy protocol schemas ---- - -{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs go in content/docs/guides/. */} - -Password Complexity Policy - - -**Source:** `packages/spec/src/security/policy.zod.ts` - - -## TypeScript Usage - -```typescript -import { AuditPolicy, NetworkPolicy, PasswordPolicy, Policy, SessionPolicy } from '@objectstack/spec/security'; -import type { AuditPolicy, NetworkPolicy, PasswordPolicy, Policy, SessionPolicy } from '@objectstack/spec/security'; - -// Validate data -const result = AuditPolicy.parse(data); -``` - ---- - -## AuditPolicy - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **logRetentionDays** | `number` | ✅ | [EXPERIMENTAL — not enforced] Days to retain audit logs | -| **sensitiveFields** | `string[]` | ✅ | [EXPERIMENTAL — not enforced] Fields to redact in logs (e.g. password, ssn) | -| **captureRead** | `boolean` | ✅ | [EXPERIMENTAL — not enforced] Log read access (High volume!) | - - ---- - -## NetworkPolicy - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **trustedRanges** | `string[]` | ✅ | [EXPERIMENTAL — not enforced] CIDR ranges allowed to access (e.g. 10.0.0.0/8) | -| **blockUnknown** | `boolean` | ✅ | [EXPERIMENTAL — not enforced] Block all IPs not in trusted ranges | -| **vpnRequired** | `boolean` | ✅ | [EXPERIMENTAL — not enforced] Require VPN to access | - - ---- - -## PasswordPolicy - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **minLength** | `number` | ✅ | [EXPERIMENTAL — not enforced] Minimum password length | -| **requireUppercase** | `boolean` | ✅ | [EXPERIMENTAL — not enforced] Require an uppercase letter | -| **requireLowercase** | `boolean` | ✅ | [EXPERIMENTAL — not enforced] Require a lowercase letter | -| **requireNumbers** | `boolean` | ✅ | [EXPERIMENTAL — not enforced] Require a number | -| **requireSymbols** | `boolean` | ✅ | [EXPERIMENTAL — not enforced] Require a symbol | -| **expirationDays** | `number` | optional | [EXPERIMENTAL — not enforced] Force password change every X days | -| **historyCount** | `number` | ✅ | [EXPERIMENTAL — not enforced] Prevent reusing last X passwords | - - ---- - -## Policy - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | [EXPERIMENTAL — not enforced] Policy Name | -| **password** | `Object` | optional | | -| **network** | `Object` | optional | | -| **session** | `Object` | optional | | -| **audit** | `Object` | optional | | -| **isDefault** | `boolean` | ✅ | [EXPERIMENTAL — not enforced] Apply to all users by default | -| **assignedProfiles** | `string[]` | optional | [EXPERIMENTAL — not enforced] Apply to specific profiles | - - ---- - -## SessionPolicy - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **idleTimeout** | `number` | ✅ | [EXPERIMENTAL — not enforced] Minutes before idle session logout | -| **absoluteTimeout** | `number` | ✅ | [EXPERIMENTAL — not enforced] Max session duration (minutes) | -| **forceMfa** | `boolean` | ✅ | [EXPERIMENTAL — not enforced] Require 2FA for all users | - - ---- - diff --git a/content/docs/references/studio/flow-builder.mdx b/content/docs/references/studio/flow-builder.mdx index f7eafc559b..fce617ddd8 100644 --- a/content/docs/references/studio/flow-builder.mdx +++ b/content/docs/references/studio/flow-builder.mdx @@ -17,7 +17,7 @@ within ObjectStack Studio. Covers: - **Canvas Node**: Position, size, and rendering hints for each node on canvas -- **Canvas Edge**: Visual properties for sequence flows (normal, default, fault) +- **Canvas Edge**: Visual properties for sequence flows (normal, default, fault, back-edge) - **Flow Builder Config**: Canvas settings, palette, minimap, and toolbar @@ -101,7 +101,7 @@ Canvas layout and visual data for a flow edge | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | | **edgeId** | `string` | ✅ | Corresponding FlowEdge.id | -| **style** | `Enum<'solid' \| 'dashed' \| 'dotted' \| 'bold'>` | ✅ | Line style | +| **style** | `Enum<'solid' \| 'dashed' \| 'dotted' \| 'bold' \| 'back'>` | ✅ | Line style | | **color** | `string` | ✅ | Edge line color | | **labelPosition** | `number` | ✅ | Position of the condition label along the edge | | **waypoints** | `Object[]` | optional | Manual waypoints for edge routing | @@ -120,6 +120,7 @@ Edge line style * `dashed` * `dotted` * `bold` +* `back` --- diff --git a/content/docs/references/studio/index.mdx b/content/docs/references/studio/index.mdx index 2627e1a216..fb99699ce4 100644 --- a/content/docs/references/studio/index.mdx +++ b/content/docs/references/studio/index.mdx @@ -8,6 +8,5 @@ This section contains all protocol schemas for the studio layer of ObjectStack. - diff --git a/content/docs/references/studio/meta.json b/content/docs/references/studio/meta.json index 8aaa5a3f36..7dee856224 100644 --- a/content/docs/references/studio/meta.json +++ b/content/docs/references/studio/meta.json @@ -4,7 +4,6 @@ "action", "flow-builder", "object-designer", - "page-builder", "plugin" ] } \ No newline at end of file diff --git a/content/docs/references/studio/page-builder.mdx b/content/docs/references/studio/page-builder.mdx deleted file mode 100644 index 5c39a219a4..0000000000 --- a/content/docs/references/studio/page-builder.mdx +++ /dev/null @@ -1,109 +0,0 @@ ---- -title: Page Builder -description: Page Builder protocol schemas ---- - -{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs go in content/docs/guides/. */} - -@module studio/page-builder - -Studio Page Builder Protocol - -Defines the specification for the drag-and-drop Page Builder UI. - -The builder allows visual composition of blank pages by placing - -elements on a grid canvas with snapping, alignment, and layer ordering. - - -**Source:** `packages/spec/src/studio/page-builder.zod.ts` - - -## TypeScript Usage - -```typescript -import { CanvasSnapSettings, CanvasZoomSettings, ElementPaletteItem, InterfaceBuilderConfig, PageBuilderConfig } from '@objectstack/spec/studio'; -import type { CanvasSnapSettings, CanvasZoomSettings, ElementPaletteItem, InterfaceBuilderConfig, PageBuilderConfig } from '@objectstack/spec/studio'; - -// Validate data -const result = CanvasSnapSettings.parse(data); -``` - ---- - -## CanvasSnapSettings - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **enabled** | `boolean` | ✅ | Enable snap-to-grid | -| **gridSize** | `integer` | ✅ | Snap grid size in pixels | -| **showGrid** | `boolean` | ✅ | Show grid overlay on canvas | -| **showGuides** | `boolean` | ✅ | Show alignment guides when dragging | - - ---- - -## CanvasZoomSettings - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **min** | `number` | ✅ | Minimum zoom level | -| **max** | `number` | ✅ | Maximum zoom level | -| **default** | `number` | ✅ | Default zoom level | -| **step** | `number` | ✅ | Zoom step increment | - - ---- - -## ElementPaletteItem - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **type** | `string` | ✅ | Component type (e.g. "element:button", "element:text") | -| **label** | `string` | ✅ | Display label in palette | -| **icon** | `string` | optional | Icon name for palette display | -| **category** | `Enum<'content' \| 'interactive' \| 'data' \| 'layout'>` | ✅ | Palette category grouping | -| **defaultWidth** | `integer` | ✅ | Default width in grid columns | -| **defaultHeight** | `integer` | ✅ | Default height in grid rows | - - ---- - -## InterfaceBuilderConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **snap** | `Object` | optional | Canvas snap settings | -| **zoom** | `Object` | optional | Canvas zoom settings | -| **palette** | `Object[]` | optional | Custom element palette (defaults to all registered elements) | -| **showLayerPanel** | `boolean` | ✅ | Show layer ordering panel | -| **showPropertyPanel** | `boolean` | ✅ | Show property inspector panel | -| **undoLimit** | `integer` | ✅ | Maximum undo history steps | - - ---- - -## PageBuilderConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **snap** | `Object` | optional | Canvas snap settings | -| **zoom** | `Object` | optional | Canvas zoom settings | -| **palette** | `Object[]` | optional | Custom element palette (defaults to all registered elements) | -| **showLayerPanel** | `boolean` | ✅ | Show layer ordering panel | -| **showPropertyPanel** | `boolean` | ✅ | Show property inspector panel | -| **undoLimit** | `integer` | ✅ | Maximum undo history steps | - - ---- - diff --git a/content/docs/references/system/auth-config.mdx b/content/docs/references/system/auth-config.mdx index 71f34a82ac..57f2d8241c 100644 --- a/content/docs/references/system/auth-config.mdx +++ b/content/docs/references/system/auth-config.mdx @@ -62,6 +62,7 @@ Advanced / low-level Better-Auth options | **emailAndPassword** | `Object` | optional | Email and password authentication options forwarded to better-auth | | **emailVerification** | `Object` | optional | Email verification options forwarded to better-auth | | **advanced** | `Object` | optional | Advanced / low-level Better-Auth options | +| **ssoOnlyMode** | `boolean` | optional | SSO-only login: hide the local password form + self-registration (the break-glass password endpoint stays enabled) | | **mutualTls** | `Object` | optional | Mutual TLS (mTLS) configuration | @@ -76,6 +77,7 @@ Advanced / low-level Better-Auth options | **organization** | `boolean` | ✅ | Enable Organization/Teams support (frontend AuthProvider expects this enabled) | | **twoFactor** | `boolean` | ✅ | Enable 2FA | | **passkeys** | `boolean` | ✅ | Enable Passkey support | +| **passwordRejectBreached** | `boolean` | ✅ | Reject passwords found in the Have I Been Pwned breach corpus (enables better-auth's haveibeenpwned plugin) | | **magicLink** | `boolean` | ✅ | Enable Magic Link login | | **oidcProvider** | `boolean` | ✅ | Enable the OpenID Connect provider plugin (acts as an OIDC IdP) | | **deviceAuthorization** | `boolean` | ✅ | Enable RFC 8628 Device Authorization Grant (CLI / TV-style login) | diff --git a/content/docs/references/ui/action.mdx b/content/docs/references/ui/action.mdx index 7d1043c84b..ca09b79647 100644 --- a/content/docs/references/ui/action.mdx +++ b/content/docs/references/ui/action.mdx @@ -98,7 +98,7 @@ const result = ActionAi.parse(data); | **field** | `string` | optional | Snake case identifier (lowercase with underscores only) | | **objectOverride** | `string` | optional | Snake case identifier (lowercase with underscores only) | | **label** | `string` | optional | Display label (plain string; i18n keys are auto-generated by the framework) | -| **type** | `Enum<'text' \| 'textarea' \| 'email' \| 'url' \| 'phone' \| 'password' \| 'secret' \| 'markdown' \| 'html' \| 'richtext' \| 'number' \| 'currency' \| 'percent' \| 'date' \| 'datetime' \| 'time' \| 'boolean' \| 'toggle' \| 'select' \| 'multiselect' \| 'radio' \| 'checkboxes' \| 'lookup' \| 'master_detail' \| 'tree' \| 'image' \| 'file' \| 'avatar' \| 'video' \| 'audio' \| 'formula' \| 'summary' \| 'autonumber' \| 'composite' \| 'repeater' \| 'record' \| 'location' \| 'address' \| 'code' \| 'json' \| 'color' \| 'rating' \| 'slider' \| 'signature' \| 'qrcode' \| 'progress' \| 'tags' \| 'vector'>` | optional | | +| **type** | `Enum<'text' \| 'textarea' \| 'email' \| 'url' \| 'phone' \| 'password' \| 'secret' \| 'markdown' \| 'html' \| 'richtext' \| 'number' \| 'currency' \| 'percent' \| 'date' \| 'datetime' \| 'time' \| 'boolean' \| 'toggle' \| 'select' \| 'multiselect' \| 'radio' \| 'checkboxes' \| 'lookup' \| 'master_detail' \| 'tree' \| 'user' \| 'image' \| 'file' \| 'avatar' \| 'video' \| 'audio' \| 'formula' \| 'summary' \| 'autonumber' \| 'composite' \| 'repeater' \| 'record' \| 'location' \| 'address' \| 'code' \| 'json' \| 'color' \| 'rating' \| 'slider' \| 'signature' \| 'qrcode' \| 'progress' \| 'tags' \| 'vector'>` | optional | | | **required** | `boolean` | ✅ | | | **options** | `Object[]` | optional | | | **placeholder** | `string` | optional | | diff --git a/content/docs/references/ui/chart.mdx b/content/docs/references/ui/chart.mdx index fa86df49ba..9653c391d5 100644 --- a/content/docs/references/ui/chart.mdx +++ b/content/docs/references/ui/chart.mdx @@ -76,7 +76,7 @@ const result = ChartAnnotation.parse(data); | **xAxis** | `Object` | optional | X-Axis configuration | | **yAxis** | `Object[]` | optional | Y-Axis configuration (support dual axis) | | **series** | `Object[]` | optional | Defined series configuration | -| **colors** | `string[]` | optional | Color palette | +| **colors** | `string[] \| Record` | optional | Color palette (string[]) or value→color map (`{ value: color }`) | | **height** | `number` | optional | Fixed height in pixels | | **showLegend** | `boolean` | ✅ | Display legend | | **showDataLabels** | `boolean` | ✅ | Display data labels | diff --git a/content/docs/references/ui/component.mdx b/content/docs/references/ui/component.mdx index 03e9807722..279d759121 100644 --- a/content/docs/references/ui/component.mdx +++ b/content/docs/references/ui/component.mdx @@ -14,8 +14,8 @@ Empty Properties Schema ## TypeScript Usage ```typescript -import { AIChatWindowProps, ElementButtonProps, ElementFilterProps, ElementImageProps, ElementMetadataViewerProps, ElementNumberProps, ElementRecordPickerProps, ElementTextProps, PageAccordionProps, PageCardProps, PageHeaderProps, PageTabsProps, RecordActivityProps, RecordChatterProps, RecordDetailsProps, RecordHighlightsField, RecordHighlightsProps, RecordPathProps, RecordRelatedListProps } from '@objectstack/spec/ui'; -import type { AIChatWindowProps, ElementButtonProps, ElementFilterProps, ElementImageProps, ElementMetadataViewerProps, ElementNumberProps, ElementRecordPickerProps, ElementTextProps, PageAccordionProps, PageCardProps, PageHeaderProps, PageTabsProps, RecordActivityProps, RecordChatterProps, RecordDetailsProps, RecordHighlightsField, RecordHighlightsProps, RecordPathProps, RecordRelatedListProps } from '@objectstack/spec/ui'; +import { AIChatWindowProps, ElementButtonProps, ElementFilterProps, ElementImageProps, ElementMetadataViewerProps, ElementNumberProps, ElementRecordPickerProps, ElementTextInputProps, ElementTextProps, PageAccordionProps, PageCardProps, PageHeaderProps, PageTabsProps, RecordActivityProps, RecordChatterProps, RecordDetailsProps, RecordHighlightsField, RecordHighlightsProps, RecordPathProps, RecordRelatedListProps } from '@objectstack/spec/ui'; +import type { AIChatWindowProps, ElementButtonProps, ElementFilterProps, ElementImageProps, ElementMetadataViewerProps, ElementNumberProps, ElementRecordPickerProps, ElementTextInputProps, ElementTextProps, PageAccordionProps, PageCardProps, PageHeaderProps, PageTabsProps, RecordActivityProps, RecordChatterProps, RecordDetailsProps, RecordHighlightsField, RecordHighlightsProps, RecordPathProps, RecordRelatedListProps } from '@objectstack/spec/ui'; // Validate data const result = AIChatWindowProps.parse(data); @@ -135,6 +135,25 @@ const result = AIChatWindowProps.parse(data); | **aria** | `Object` | optional | ARIA accessibility attributes | +--- + +## ElementTextInputProps + +### Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **inputType** | `Enum<'text' \| 'email' \| 'number' \| 'tel' \| 'url' \| 'password'>` | ✅ | Native input type — drives keyboard/validation affordance and how the bound value is coerced (number → numeric). | +| **label** | `string` | optional | Field label shown above the input | +| **placeholder** | `string` | optional | Placeholder text shown when empty | +| **defaultValue** | `string \| number` | optional | Initial value; seeds the bound page variable on mount | +| **required** | `boolean` | ✅ | Mark the field as required | +| **disabled** | `boolean` | ✅ | Disable the input | +| **description** | `string` | optional | Helper text shown below the input | +| **targetVariable** | `string` | optional | Page variable this input writes to. Declarative hint; the live binding resolves via the variable whose `source` equals this component id (see PageVariableSchema). | +| **aria** | `Object` | optional | ARIA accessibility attributes | + + --- ## ElementTextProps @@ -334,6 +353,7 @@ Type: `string` | **title** | `string` | optional | Custom title for the related list | | **showViewAll** | `boolean` | ✅ | Show "View All" link to see all related records | | **actions** | `string[]` | optional | Action IDs available for related records | +| **add** | `Object` | optional | Add-existing-via-picker config (generic m2m/junction assignment). | | **aria** | `Object` | optional | ARIA accessibility attributes | diff --git a/content/docs/references/ui/dashboard.mdx b/content/docs/references/ui/dashboard.mdx index f15290b786..2a9095db14 100644 --- a/content/docs/references/ui/dashboard.mdx +++ b/content/docs/references/ui/dashboard.mdx @@ -106,7 +106,7 @@ Dashboard header action | **dataset** | `string` | ✅ | Dataset name to bind (ADR-0021) | | **dimensions** | `string[]` | optional | Dimension names — X/group/split | | **values** | `string[]` | ✅ | Measure names — Y (at least one) | -| **layout** | `Object` | ✅ | Grid layout position | +| **layout** | `Object` | optional | Grid layout position (auto-flowed when omitted) | | **options** | `any` | optional | Widget specific configuration | | **suppressWarnings** | `string[]` | optional | Build diagnostic rule ids suppressed on this widget | | **responsive** | `Object` | optional | Responsive layout configuration | diff --git a/content/docs/references/ui/dataset.mdx b/content/docs/references/ui/dataset.mdx index 4b7954c990..c3731c0d44 100644 --- a/content/docs/references/ui/dataset.mdx +++ b/content/docs/references/ui/dataset.mdx @@ -65,7 +65,7 @@ const result = Dataset.parse(data); | **label** | `string` | ✅ | Dataset label | | **description** | `string` | optional | Display label (plain string; i18n keys are auto-generated by the framework) | | **object** | `string` | ✅ | Base object name | -| **include** | `string[]` | optional | Relationship names to join (derived from object graph) | +| **include** | `string[]` | optional | Relationship names/paths to join (derived from object graph; max 3 hops) | | **filter** | `[__schema0](./__schema0)` | optional | Intrinsic dataset scope filter | | **dimensions** | `Object[]` | ✅ | Groupable axes | | **measures** | `Object[]` | ✅ | Aggregatable values | @@ -89,7 +89,7 @@ const result = Dataset.parse(data); | :--- | :--- | :--- | :--- | | **name** | `string` | ✅ | Dimension name — referenced by presentations | | **label** | `string` | optional | Display label (plain string; i18n keys are auto-generated by the framework) | -| **field** | `string` | ✅ | Base field or `relationship.field` path | +| **field** | `string` | ✅ | Base field, or `relationship[.relationship].field` path | | **type** | `Enum<'string' \| 'number' \| 'date' \| 'boolean' \| 'lookup'>` | optional | | | **dateGranularity** | `Enum<'day' \| 'week' \| 'month' \| 'quarter' \| 'year'>` | optional | | @@ -104,7 +104,7 @@ const result = Dataset.parse(data); | :--- | :--- | :--- | :--- | | **name** | `string` | ✅ | Measure name — e.g. "revenue"; defined once | | **label** | `string` | optional | Display label (plain string; i18n keys are auto-generated by the framework) | -| **aggregate** | `Enum<'count' \| 'sum' \| 'avg' \| 'min' \| 'max' \| 'count_distinct' \| 'array_agg' \| 'string_agg'>` | ✅ | Aggregation (sum/avg/count/...) | +| **aggregate** | `Enum<'count' \| 'sum' \| 'avg' \| 'min' \| 'max' \| 'count_distinct' \| 'array_agg' \| 'string_agg'>` | optional | Aggregation (sum/avg/count/...); omit when `derived` is set | | **field** | `string` | optional | Aggregated field; optional for count(*) | | **filter** | `[__schema0](./__schema0)` | optional | | | **format** | `string` | optional | | diff --git a/content/docs/references/ui/page.mdx b/content/docs/references/ui/page.mdx index cdce2d1b59..c1ef342941 100644 --- a/content/docs/references/ui/page.mdx +++ b/content/docs/references/ui/page.mdx @@ -16,42 +16,13 @@ A named region in the template where components are dropped. ## TypeScript Usage ```typescript -import { BlankPageLayout, BlankPageLayoutItem, ElementDataSource, InterfacePageConfig, PageComponentType, PageType, PageVariable, RecordReviewConfig } from '@objectstack/spec/ui'; -import type { BlankPageLayout, BlankPageLayoutItem, ElementDataSource, InterfacePageConfig, PageComponentType, PageType, PageVariable, RecordReviewConfig } from '@objectstack/spec/ui'; +import { ElementDataSource, InterfacePageConfig, PageComponentType, PageType, PageVariable } from '@objectstack/spec/ui'; +import type { ElementDataSource, InterfacePageConfig, PageComponentType, PageType, PageVariable } from '@objectstack/spec/ui'; // Validate data -const result = BlankPageLayout.parse(data); +const result = ElementDataSource.parse(data); ``` ---- - -## BlankPageLayout - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **columns** | `integer` | ✅ | Number of grid columns | -| **rowHeight** | `integer` | ✅ | Height of each grid row in pixels | -| **gap** | `integer` | ✅ | Gap between grid items in pixels | -| **items** | `Object[]` | ✅ | Positioned components on the canvas | - - ---- - -## BlankPageLayoutItem - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **componentId** | `string` | ✅ | Reference to a PageComponent.id in the page | -| **x** | `integer` | ✅ | Grid column position (0-based) | -| **y** | `integer` | ✅ | Grid row position (0-based) | -| **width** | `integer` | ✅ | Width in grid columns | -| **height** | `integer` | ✅ | Height in grid rows | - - --- ## ElementDataSource @@ -132,13 +103,14 @@ Interface-level page configuration (Airtable parity) * `element:filter` * `element:form` * `element:record_picker` +* `element:text_input` --- ## PageType -Page type — the page KIND (platform or interface). Visualizations of a list page live in interfaceConfig, not here. +Page type — the page KIND. Only types with a dedicated renderer are authorizable; visualizations of a list page live in interfaceConfig, not here. ### Allowed Values @@ -147,12 +119,6 @@ Page type — the page KIND (platform or interface). Visualizations of a list pa * `app` * `utility` * `list` -* `dashboard` -* `form` -* `record_detail` -* `record_review` -* `overview` -* `blank` --- @@ -163,27 +129,10 @@ Page type — the page KIND (platform or interface). Visualizations of a list pa | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | -| **name** | `string` | ✅ | Variable name | +| **name** | `string` | ✅ | Variable name. Exposed to expressions as `page.`. | | **type** | `Enum<'string' \| 'number' \| 'boolean' \| 'object' \| 'array' \| 'record_id'>` | ✅ | | -| **defaultValue** | `any` | optional | | -| **source** | `string` | optional | Component ID that writes to this variable | - - ---- - -## RecordReviewConfig - -### Properties - -| Property | Type | Required | Description | -| :--- | :--- | :--- | :--- | -| **object** | `string` | ✅ | Target object for review | -| **filter** | `[__schema0](./__schema0)` | optional | Filter criteria for review queue | -| **sort** | `Object[]` | optional | Sort order for review queue | -| **displayFields** | `string[]` | optional | Fields to display on the review page | -| **actions** | `Object[]` | ✅ | Review actions | -| **navigation** | `Enum<'sequential' \| 'random' \| 'filtered'>` | ✅ | Record navigation mode | -| **showProgress** | `boolean` | ✅ | Show review progress indicator | +| **defaultValue** | `any` | optional | Initial value. Defaults to a type-appropriate empty value when omitted. | +| **source** | `string` | optional | Component id that writes this variable (e.g. an element:record_picker whose `id` matches). | --- diff --git a/content/docs/references/ui/report.mdx b/content/docs/references/ui/report.mdx index 25e7671d2f..73dd784409 100644 --- a/content/docs/references/ui/report.mdx +++ b/content/docs/references/ui/report.mdx @@ -88,7 +88,7 @@ const result = JoinedReportBlock.parse(data); | **xAxis** | `string` | ✅ | Grouping field for X-Axis | | **yAxis** | `string` | ✅ | Summary field for Y-Axis | | **series** | `Object[]` | optional | Defined series configuration | -| **colors** | `string[]` | optional | Color palette | +| **colors** | `string[] \| Record` | optional | Color palette (string[]) or value→color map (`{ value: color }`) | | **height** | `number` | optional | Fixed height in pixels | | **showLegend** | `boolean` | ✅ | Display legend | | **showDataLabels** | `boolean` | ✅ | Display data labels | diff --git a/content/docs/references/ui/responsive.mdx b/content/docs/references/ui/responsive.mdx index 2e7524bbdc..93e133351e 100644 --- a/content/docs/references/ui/responsive.mdx +++ b/content/docs/references/ui/responsive.mdx @@ -16,8 +16,8 @@ Matches the breakpoint names defined in theme.zod.ts BreakpointsSchema. ## TypeScript Usage ```typescript -import { BreakpointColumnMap, BreakpointName, BreakpointOrderMap, PerformanceConfig, ResponsiveConfig } from '@objectstack/spec/ui'; -import type { BreakpointColumnMap, BreakpointName, BreakpointOrderMap, PerformanceConfig, ResponsiveConfig } from '@objectstack/spec/ui'; +import { BreakpointColumnMap, BreakpointName, BreakpointOrderMap, PerformanceConfig, ResponsiveConfig, ResponsiveStyles, StyleMap } from '@objectstack/spec/ui'; +import type { BreakpointColumnMap, BreakpointName, BreakpointOrderMap, PerformanceConfig, ResponsiveConfig, ResponsiveStyles, StyleMap } from '@objectstack/spec/ui'; // Validate data const result = BreakpointColumnMap.parse(data); @@ -109,3 +109,22 @@ Responsive layout configuration --- +## ResponsiveStyles + +Per-breakpoint scoped style maps (ADR-0065) + +### Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **large** | `Record` | optional | Unconditional base (desktop-first) | +| **medium** | `Record` | optional | Applied at ≤ medium breakpoint | +| **small** | `Record` | optional | Applied at ≤ small breakpoint | +| **xsmall** | `Record` | optional | Applied at ≤ xsmall breakpoint | + + +--- + + +--- + diff --git a/packages/adapters/express/CHANGELOG.md b/packages/adapters/express/CHANGELOG.md deleted file mode 100644 index 38c772150b..0000000000 --- a/packages/adapters/express/CHANGELOG.md +++ /dev/null @@ -1,473 +0,0 @@ -# @objectstack/express - -## 11.0.0 - -### Patch Changes - -- Updated dependencies [4d99a5c] -- Updated dependencies [61d441f] -- Updated dependencies [c224e18] -- Updated dependencies [6c4fbd9] -- Updated dependencies [aa33b02] - - @objectstack/runtime@11.0.0 - -## 10.3.0 - -### Patch Changes - -- Updated dependencies [8cf4f7c] -- Updated dependencies [f2063f3] - - @objectstack/runtime@10.3.0 - -## 10.2.0 - -### Patch Changes - -- @objectstack/runtime@10.2.0 - -## 10.1.0 - -### Patch Changes - -- Updated dependencies [ac79f16] -- Updated dependencies [94d2161] - - @objectstack/runtime@10.1.0 - -## 10.0.0 - -### Patch Changes - -- Updated dependencies [e16f2a8] -- Updated dependencies [47d978a] -- Updated dependencies [220ce5b] - - @objectstack/runtime@10.0.0 - -## 9.11.0 - -### Patch Changes - -- Updated dependencies [2afb612] - - @objectstack/runtime@9.11.0 - -## 9.10.0 - -### Patch Changes - -- Updated dependencies [1f88fd9] -- Updated dependencies [e2b5324] - - @objectstack/runtime@9.10.0 - -## 9.9.1 - -### Patch Changes - -- @objectstack/runtime@9.9.1 - -## 9.9.0 - -### Patch Changes - -- Updated dependencies [11af299] -- Updated dependencies [83fd318] -- Updated dependencies [9afeb2d] - - @objectstack/runtime@9.9.0 - -## 9.8.0 - -### Patch Changes - -- @objectstack/runtime@9.8.0 - -## 9.7.0 - -### Patch Changes - -- @objectstack/runtime@9.7.0 - -## 9.6.0 - -### Patch Changes - -- Updated dependencies [71578f2] - - @objectstack/runtime@9.6.0 - -## 9.5.1 - -### Patch Changes - -- @objectstack/runtime@9.5.1 - -## 9.5.0 - -### Patch Changes - -- @objectstack/runtime@9.5.0 - -## 9.4.0 - -### Patch Changes - -- Updated dependencies [0856476] - - @objectstack/runtime@9.4.0 - -## 9.3.0 - -### Patch Changes - -- Updated dependencies [1ada658] - - @objectstack/runtime@9.3.0 - -## 9.2.0 - -### Patch Changes - -- @objectstack/runtime@9.2.0 - -## 9.1.0 - -### Patch Changes - -- @objectstack/runtime@9.1.0 - -## 9.0.1 - -### Patch Changes - -- @objectstack/runtime@9.0.1 - -## 9.0.0 - -### Patch Changes - -- @objectstack/runtime@9.0.0 - -## 8.0.1 - -### Patch Changes - -- @objectstack/runtime@8.0.1 - -## 8.0.0 - -### Patch Changes - -- Updated dependencies [f68be58] -- Updated dependencies [bc0d85b] -- Updated dependencies [2537e28] -- Updated dependencies [0ec7717] -- Updated dependencies [c262301] - - @objectstack/runtime@8.0.0 - -## 7.9.0 - -### Patch Changes - -- Updated dependencies [ac1fc4c] -- Updated dependencies [ac1fc4c] - - @objectstack/runtime@7.9.0 - -## 7.8.0 - -### Patch Changes - -- Updated dependencies [a75823a] -- Updated dependencies [4fbb86a] -- Updated dependencies [e631f1e] -- Updated dependencies [424ab26] - - @objectstack/runtime@7.8.0 - -## 7.7.0 - -### Patch Changes - -- @objectstack/runtime@7.7.0 - -## 7.6.0 - -### Patch Changes - -- 3377e38: fix(release): stop the fixed-group major cascade caused by internal `@objectstack/*` peerDependencies. - - These packages declared workspace peerDependencies on other framework packages - in the changesets `fixed` group. Inside a fixed group, changesets rewrites those - peer ranges on every release and treats a peer-range change as breaking → major, - which cascaded to **all 69 packages → 8.0.0** on _any_ minor changeset. Required - internal peers are now regular `dependencies`; optional ones move to - `devDependencies` (kept for in-workspace tests, no longer a published peer edge). - Releases now bump correctly (patch/minor) instead of a spurious major. - -- Updated dependencies [8e539cc] - - @objectstack/runtime@7.6.0 - -## 7.5.0 - -## 7.4.1 - -## 7.4.0 - -## 7.3.0 - -## 7.2.1 - -## 7.2.0 - -## 7.1.0 - -## 7.0.0 - -### Patch Changes - -- Updated dependencies [dc72172] -- Updated dependencies [3a630b6] - - @objectstack/runtime@7.0.0 - -## 6.9.0 - -## 6.8.1 - -## 6.8.0 - -## 6.7.1 - -## 6.7.0 - -## 6.6.0 - -## 6.5.1 - -## 6.5.0 - -## 6.4.0 - -## 6.3.0 - -## 6.2.0 - -## 6.1.1 - -## 6.1.0 - -## 6.0.0 - -### Patch Changes - -- Updated dependencies [944f187] - - @objectstack/runtime@6.0.0 - -## 5.2.0 - -## 5.1.0 - -## 5.0.0 - -### Patch Changes - -- Updated dependencies [5e9dcb4] -- Updated dependencies [96ad4df] -- Updated dependencies [df18ae9] - - @objectstack/runtime@5.0.0 - -## 4.2.0 - -## 4.1.1 - -## 4.1.0 - -## 4.0.5 - -### Patch Changes - -- 15e0df6: chore: unify all package versions to a single patch release - -## 4.0.4 - -### Patch Changes - -- @objectstack/runtime@4.0.4 - -## 4.0.3 - -### Patch Changes - -- @objectstack/runtime@4.0.3 - -## 4.0.2 - -### Patch Changes - -- @objectstack/runtime@4.0.2 - -## 4.0.0 - -### Patch Changes - -- f08ffc3: Fix discovery API endpoint routing and protocol consistency. - - **Discovery route standardization:** - - - All adapters (Express, Fastify, Hono, NestJS, Next.js, Nuxt, SvelteKit) now mount the discovery endpoint at `{prefix}/discovery` instead of `{prefix}` root. - - `.well-known/objectstack` redirects now point to `{prefix}/discovery`. - - Client `connect()` fallback URL changed from `/api/v1` to `/api/v1/discovery`. - - Runtime dispatcher handles both `/discovery` (standard) and `/` (legacy) for backward compatibility. - - **Schema & route alignment:** - - - Added `storage` (service: `file-storage`) and `feed` (service: `data`) routes to `DEFAULT_DISPATCHER_ROUTES`. - - Added `feed` and `discovery` fields to `ApiRoutesSchema`. - - Unified `GetDiscoveryResponseSchema` with `DiscoverySchema` as single source of truth. - - Client `getRoute('feed')` fallback updated from `/api/v1/data` to `/api/v1/feed`. - - **Type safety:** - - - Extracted `ApiRouteType` from `ApiRoutes` keys for type-safe client route resolution. - - Removed `as any` type casting in client route access. - -- Updated dependencies [f08ffc3] -- Updated dependencies [e0b0a78] - - @objectstack/runtime@4.0.0 - -## 3.3.1 - -### Patch Changes - -- @objectstack/runtime@3.3.1 - -## 3.3.0 - -### Patch Changes - -- @objectstack/runtime@3.3.0 - -## 3.2.9 - -### Patch Changes - -- @objectstack/runtime@3.2.9 - -## 3.2.8 - -### Patch Changes - -- @objectstack/runtime@3.2.8 - -## 3.2.8 - -### Patch Changes - -- fix: unified catch-all dispatch pattern — `createExpressRouter()` now delegates all non-framework-specific routes to `HttpDispatcher.dispatch()`, automatically supporting packages, analytics, automation, i18n, ui, openapi, custom endpoints, and any future routes -- Only auth (service check), storage (file upload), GraphQL (raw result), and discovery (response wrapper) remain as explicit routes - -## 3.2.7 - -### Patch Changes - -- @objectstack/runtime@3.2.7 - -## 3.2.6 - -### Patch Changes - -- @objectstack/runtime@3.2.6 - -## 3.2.5 - -### Patch Changes - -- @objectstack/runtime@3.2.5 - -## 3.2.4 - -### Patch Changes - -- @objectstack/runtime@3.2.4 - -## 3.2.3 - -### Patch Changes - -- @objectstack/runtime@3.2.3 - -## 3.2.2 - -### Patch Changes - -- @objectstack/runtime@3.2.2 - -## 3.2.1 - -### Patch Changes - -- @objectstack/runtime@3.2.1 - -## 3.2.0 - -### Patch Changes - -- @objectstack/runtime@3.2.0 - -## 3.1.1 - -### Patch Changes - -- @objectstack/runtime@3.1.1 - -## 3.1.0 - -### Patch Changes - -- @objectstack/runtime@3.1.0 - -## 3.0.11 - -### Patch Changes - -- @objectstack/runtime@3.0.11 - -## 3.0.10 - -### Patch Changes - -- @objectstack/runtime@3.0.10 - -## 3.0.9 - -### Patch Changes - -- @objectstack/runtime@3.0.9 - -## 3.0.8 - -### Patch Changes - -- @objectstack/runtime@3.0.8 - -## 3.0.7 - -### Patch Changes - -- @objectstack/runtime@3.0.7 - -## 3.0.6 - -### Patch Changes - -- @objectstack/runtime@3.0.6 - -## 3.0.5 - -### Patch Changes - -- @objectstack/runtime@3.0.5 - -## 3.0.4 - -### Patch Changes - -- @objectstack/runtime@3.0.4 - -## 3.0.3 - -### Patch Changes - -- Updated dependencies [c7267f6] - - @objectstack/runtime@3.0.3 diff --git a/packages/adapters/express/LICENSE b/packages/adapters/express/LICENSE deleted file mode 100644 index 16bc23f404..0000000000 --- a/packages/adapters/express/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute - must include a readable copy of the attribution notices - contained within such NOTICE file, excluding those notices - that do not pertain to any part of the Derivative Works, - in at least one of the following places: within a NOTICE - text file distributed as part of the Derivative Works; within - the Source form or documentation, if provided along with - the Derivative Works; or, within a display generated by the - Derivative Works, if and wherever such third-party notices - normally appear. The contents of the NOTICE file are for - informational purposes only and do not modify the License. - You may add Your own attribution notices within Derivative - Works that You distribute, alongside or as an addendum to - the NOTICE text from the Work, provided that such additional - attribution notices cannot be construed as modifying the - License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2026 ObjectStack - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/adapters/express/README.md b/packages/adapters/express/README.md deleted file mode 100644 index 3a51933aaf..0000000000 --- a/packages/adapters/express/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# @objectstack/express - -> Express adapter for ObjectStack — mounts the auto-generated REST API and route dispatcher onto an Express app. - -[![npm](https://img.shields.io/npm/v/@objectstack/express.svg)](https://www.npmjs.com/package/@objectstack/express) -[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) - -## Overview - -Wraps `HttpDispatcher` from `@objectstack/runtime` as an Express `Router` (or request middleware). All ObjectStack routes — CRUD, batch, metadata, discovery, auth, storage, GraphQL — are handled by a catch-all that delegates to the dispatcher, so new protocol routes work automatically without adapter updates. - -## Installation - -```bash -pnpm add @objectstack/express express -``` - -## Quick Start - -```typescript -import express from 'express'; -import { createExpressRouter } from '@objectstack/express'; -import { kernel } from './my-kernel'; - -const app = express(); -app.use(express.json()); -app.use('/api', createExpressRouter({ kernel })); -app.listen(3000); -``` - -### Middleware mode - -```typescript -import { objectStackMiddleware } from '@objectstack/express'; - -app.use(objectStackMiddleware(kernel)); -``` - -## Key Exports - -| Export | Kind | Description | -|:---|:---|:---| -| `createExpressRouter(options)` | function | Returns a mounted `Router` with all ObjectStack dispatchers. | -| `objectStackMiddleware(kernel)` | function | Per-request middleware (use for custom routing/prefix). | -| `ExpressAdapterOptions` | interface | `{ kernel: ObjectKernel, prefix?: string }`. | - -## Configuration - -| Option | Type | Default | Notes | -|:---|:---|:---|:---| -| `kernel` | `ObjectKernel` | — | Bootstrapped kernel from `@objectstack/core`. | -| `prefix` | `string` | `'/api'` | Base path for the mounted router. | - -## Middleware order - -`express.json()` (or an equivalent body parser) MUST be registered **before** the ObjectStack router. Auth cookies require `cookie-parser` if you use cookie sessions. The adapter sets response headers directly; do not wrap with compression middleware that rewrites JSON bodies before the router. - -## When to use - -- ✅ Adding ObjectStack to an existing Express app. -- ✅ Node.js deployments without edge runtime constraints. - -## When not to use - -- ❌ Edge / Workers — use [`@objectstack/hono`](../hono) instead. -- ❌ Next.js App Router — use [`@objectstack/nextjs`](../nextjs). - -## Related Packages - -- [`@objectstack/runtime`](../../runtime) — provides `HttpDispatcher`. -- [`@objectstack/rest`](../../rest) — route registration used by the dispatcher. -- [`@objectstack/core`](../../core) — kernel. - -## Links - -- 📖 Docs: -- 📚 API Reference: - -## License - -Apache-2.0 © ObjectStack diff --git a/packages/adapters/express/package.json b/packages/adapters/express/package.json deleted file mode 100644 index 8f9fe4eae0..0000000000 --- a/packages/adapters/express/package.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "@objectstack/express", - "version": "11.0.0", - "license": "Apache-2.0", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "require": "./dist/index.js" - } - }, - "scripts": { - "build": "tsup --config ../../../tsup.config.ts", - "test": "vitest run", - "test:watch": "vitest" - }, - "peerDependencies": { - "express": "^5.1.0" - }, - "devDependencies": { - "@objectstack/runtime": "workspace:*", - "@types/express": "^5.0.6", - "express": "^5.2.1", - "typescript": "^6.0.3", - "vitest": "^4.1.9" - }, - "description": "Express adapter for ObjectStack — mounts the generated REST API onto an Express app.", - "keywords": [ - "objectstack", - "express", - "adapter", - "rest", - "node" - ], - "author": "ObjectStack", - "repository": { - "type": "git", - "url": "https://github.com/objectstack-ai/framework.git", - "directory": "packages/adapters/express" - }, - "homepage": "https://objectstack.ai/docs", - "bugs": "https://github.com/objectstack-ai/framework/issues", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist", - "README.md" - ], - "engines": { - "node": ">=18.0.0" - }, - "dependencies": { - "@objectstack/runtime": "workspace:^" - } -} diff --git a/packages/adapters/express/src/__mocks__/runtime.ts b/packages/adapters/express/src/__mocks__/runtime.ts deleted file mode 100644 index 7961fd466c..0000000000 --- a/packages/adapters/express/src/__mocks__/runtime.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Stub for @objectstack/runtime - replaced by vi.mock in tests -export const HttpDispatcher = class {}; -export type ObjectKernel = any; -export type HttpDispatcherResult = any; diff --git a/packages/adapters/express/src/express.test.ts b/packages/adapters/express/src/express.test.ts deleted file mode 100644 index 0fc86574d1..0000000000 --- a/packages/adapters/express/src/express.test.ts +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { describe, it, expect, vi, beforeEach } from 'vitest'; - -// Mock dispatcher instance -const mockDispatcher = { - getDiscoveryInfo: vi.fn().mockReturnValue({ version: '1.0', endpoints: [] }), - handleAuth: vi.fn().mockResolvedValue({ handled: true, response: { body: { ok: true }, status: 200 } }), - handleGraphQL: vi.fn().mockResolvedValue({ data: {} }), - handleStorage: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }), - dispatch: vi.fn().mockResolvedValue({ handled: true, response: { body: { success: true }, status: 200 } }), -}; - -vi.mock('@objectstack/runtime', () => { - return { - HttpDispatcher: function HttpDispatcher() { - return mockDispatcher; - }, - }; -}); - -import { createExpressRouter, objectStackMiddleware } from './index'; - -const mockKernel = { name: 'test-kernel' } as any; - -function createMockRes() { - const res: any = { - _status: 200, - _body: null, - _headers: {} as Record, - _redirectUrl: null as string | null, - status(code: number) { res._status = code; return res; }, - json(body: any) { res._body = body; return res; }, - send(body: any) { res._body = body; return res; }, - set(k: string, v: string) { res._headers[k] = v; return res; }, - redirect(url: string) { res._redirectUrl = url; return res; }, - }; - return res; -} - -function createMockReq(overrides: any = {}) { - return { - method: 'GET', - path: '/', - url: '/', - originalUrl: '/', - body: {}, - query: {}, - headers: {}, - params: {}, - protocol: 'http', - get: (key: string) => key === 'host' ? 'localhost' : undefined, - ...overrides, - }; -} - -describe('createExpressRouter', () => { - let router: any; - - beforeEach(() => { - vi.clearAllMocks(); - router = createExpressRouter({ kernel: mockKernel }); - }); - - it('returns a router with registered routes', () => { - expect(router).toBeDefined(); - expect(router.stack).toBeDefined(); - expect(router.stack.length).toBeGreaterThan(0); - }); -}); - -describe('objectStackMiddleware', () => { - it('attaches kernel to request', () => { - const middleware = objectStackMiddleware(mockKernel); - const req: any = {}; - const res = createMockRes(); - const next = vi.fn(); - - middleware(req, res, next); - - expect(req.objectStack).toBe(mockKernel); - expect(next).toHaveBeenCalled(); - }); -}); diff --git a/packages/adapters/express/src/index.ts b/packages/adapters/express/src/index.ts deleted file mode 100644 index afc5d46055..0000000000 --- a/packages/adapters/express/src/index.ts +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { type Router, type Request, type Response, type NextFunction, Router as createRouter } from 'express'; -import { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime'; - -export interface ExpressAdapterOptions { - kernel: ObjectKernel; - prefix?: string; -} - -/** - * Auth service interface with handleRequest method - */ -interface AuthService { - handleRequest(request: globalThis.Request): Promise; -} - -/** - * Creates an Express Router with all ObjectStack route dispatchers. - * - * Only routes that need framework-specific handling (auth service, storage - * file upload, GraphQL raw result, discovery wrapper) are registered explicitly. - * All other routes are handled by a catch-all that delegates to - * `HttpDispatcher.dispatch()`, making the adapter automatically support - * new routes added to the dispatcher. - * - * @example - * ```ts - * import express from 'express'; - * import { createExpressRouter } from '@objectstack/express'; - * - * const app = express(); - * app.use('/api', createExpressRouter({ kernel })); - * app.listen(3000); - * ``` - */ -export function createExpressRouter(options: ExpressAdapterOptions): Router { - const router = createRouter(); - const dispatcher = new HttpDispatcher(options.kernel); - const prefix = options.prefix || '/api'; - - const sendResult = (result: HttpDispatcherResult, res: Response) => { - if (result.handled) { - if (result.response) { - if (result.response.headers) { - Object.entries(result.response.headers).forEach(([k, v]) => res.set(k, v as string)); - } - return res.status(result.response.status).json(result.response.body); - } - if (result.result) { - const response = result.result; - if (response.type === 'redirect' && response.url) { - return res.redirect(response.url); - } - if (response.type === 'stream' && response.stream) { - if (response.headers) { - Object.entries(response.headers).forEach(([k, v]) => res.set(k, v as string)); - } - response.stream.pipe(res); - return; - } - return res.status(200).json(response); - } - } - return res.status(404).json({ success: false, error: { message: 'Not Found', code: 404 } }); - }; - - const errorResponse = (err: any, res: Response) => { - return res.status(err.statusCode || 500).json({ - success: false, - error: { - message: err.message || 'Internal Server Error', - code: err.statusCode || 500, - }, - }); - }; - - // ─── Explicit routes (framework-specific handling required) ──────────────── - - // --- Discovery --- - router.get('/discovery', async (_req: Request, res: Response) => { - res.json({ data: await dispatcher.getDiscoveryInfo(prefix) }); - }); - - // --- Auth (needs auth service integration) --- - router.all('/auth/{*path}', async (req: Request, res: Response) => { - try { - const path = (req.params as any).path; - const method = req.method; - - // Try AuthPlugin service first (prefer async to support factory-based services) - let authService: AuthService | null = null; - try { - if (typeof options.kernel.getServiceAsync === 'function') { - authService = await options.kernel.getServiceAsync('auth'); - } else if (typeof options.kernel.getService === 'function') { - authService = options.kernel.getService('auth'); - } - } catch { - // Service not registered — fall through to dispatcher - authService = null; - } - - if (authService && typeof authService.handleRequest === 'function') { - const protocol = req.protocol || 'http'; - const host = req.get?.('host') || req.headers?.host || 'localhost'; - const url = `${protocol}://${host}${req.originalUrl || req.url}`; - const headers = new Headers(); - if (req.headers) { - Object.entries(req.headers).forEach(([k, v]) => { - if (typeof v === 'string') headers.set(k, v); - else if (Array.isArray(v)) headers.set(k, v.join(', ')); - }); - } - const init: RequestInit = { method, headers }; - if (method !== 'GET' && method !== 'HEAD' && req.body) { - init.body = JSON.stringify(req.body); - if (!headers.has('content-type')) { - headers.set('content-type', 'application/json'); - } - } - const webRequest = new Request(url, init); - const response = await authService.handleRequest(webRequest); - res.status(response.status); - response.headers.forEach((v: string, k: string) => res.set(k, v)); - const text = await response.text(); - return res.send(text); - } - - // Fallback to dispatcher - const body = method === 'GET' || method === 'HEAD' ? {} : req.body || {}; - const result = await dispatcher.handleAuth(path, method, body, { request: req, response: res }); - return sendResult(result, res); - } catch (err: any) { - return errorResponse(err, res); - } - }); - - // --- GraphQL (returns raw result, not HttpDispatcherResult) --- - router.post('/graphql', async (req: Request, res: Response) => { - try { - const result = await dispatcher.handleGraphQL(req.body, { request: req }); - return res.json(result); - } catch (err: any) { - return errorResponse(err, res); - } - }); - - // --- Storage (needs file upload handling) --- - router.all('/storage/{*path}', async (req: Request, res: Response) => { - try { - const subPath = '/' + (req.params as any).path; - const method = req.method; - const file = (req as any).file || (req as any).files?.file; - const result = await dispatcher.handleStorage(subPath, method, file, { request: req }); - return sendResult(result, res); - } catch (err: any) { - return errorResponse(err, res); - } - }); - - // ─── Catch-all: delegate to dispatcher.dispatch() ───────────────────────── - // Handles meta, data, packages, analytics, automation, i18n, ui, openapi, - // custom API endpoints, and any future routes added to HttpDispatcher. - router.all('/{*path}', async (req: Request, res: Response) => { - try { - const subPath = '/' + (req.params as any).path; - const method = req.method; - const body = (method === 'POST' || method === 'PUT' || method === 'PATCH') ? req.body : undefined; - const result = await dispatcher.dispatch(method, subPath, body, req.query, { request: req, response: res }, prefix); - return sendResult(result, res); - } catch (err: any) { - return errorResponse(err, res); - } - }); - - return router; -} - -/** - * Middleware that attaches the ObjectStack kernel to the request. - */ -export function objectStackMiddleware(kernel: ObjectKernel) { - return (req: Request, _res: Response, next: NextFunction) => { - (req as any).objectStack = kernel; - next(); - }; -} diff --git a/packages/adapters/express/tsconfig.json b/packages/adapters/express/tsconfig.json deleted file mode 100644 index e8dfb8f584..0000000000 --- a/packages/adapters/express/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": "../../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "esModuleInterop": true, - "skipLibCheck": true, - "lib": [ - "ES2020", - "DOM", - "DOM.Iterable" - ], - "types": [ - "node" - ] - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "dist" - ] -} diff --git a/packages/adapters/express/vitest.config.ts b/packages/adapters/express/vitest.config.ts deleted file mode 100644 index e2051df210..0000000000 --- a/packages/adapters/express/vitest.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { defineConfig } from 'vitest/config'; -import path from 'node:path'; - -export default defineConfig({ - test: { - globals: true, - environment: 'node', - }, - resolve: { - alias: { - '@objectstack/runtime': path.resolve(__dirname, 'src/__mocks__/runtime.ts'), - }, - }, -}); diff --git a/packages/adapters/fastify/CHANGELOG.md b/packages/adapters/fastify/CHANGELOG.md deleted file mode 100644 index 930ebf6774..0000000000 --- a/packages/adapters/fastify/CHANGELOG.md +++ /dev/null @@ -1,473 +0,0 @@ -# @objectstack/fastify - -## 11.0.0 - -### Patch Changes - -- Updated dependencies [4d99a5c] -- Updated dependencies [61d441f] -- Updated dependencies [c224e18] -- Updated dependencies [6c4fbd9] -- Updated dependencies [aa33b02] - - @objectstack/runtime@11.0.0 - -## 10.3.0 - -### Patch Changes - -- Updated dependencies [8cf4f7c] -- Updated dependencies [f2063f3] - - @objectstack/runtime@10.3.0 - -## 10.2.0 - -### Patch Changes - -- @objectstack/runtime@10.2.0 - -## 10.1.0 - -### Patch Changes - -- Updated dependencies [ac79f16] -- Updated dependencies [94d2161] - - @objectstack/runtime@10.1.0 - -## 10.0.0 - -### Patch Changes - -- Updated dependencies [e16f2a8] -- Updated dependencies [47d978a] -- Updated dependencies [220ce5b] - - @objectstack/runtime@10.0.0 - -## 9.11.0 - -### Patch Changes - -- Updated dependencies [2afb612] - - @objectstack/runtime@9.11.0 - -## 9.10.0 - -### Patch Changes - -- Updated dependencies [1f88fd9] -- Updated dependencies [e2b5324] - - @objectstack/runtime@9.10.0 - -## 9.9.1 - -### Patch Changes - -- @objectstack/runtime@9.9.1 - -## 9.9.0 - -### Patch Changes - -- Updated dependencies [11af299] -- Updated dependencies [83fd318] -- Updated dependencies [9afeb2d] - - @objectstack/runtime@9.9.0 - -## 9.8.0 - -### Patch Changes - -- @objectstack/runtime@9.8.0 - -## 9.7.0 - -### Patch Changes - -- @objectstack/runtime@9.7.0 - -## 9.6.0 - -### Patch Changes - -- Updated dependencies [71578f2] - - @objectstack/runtime@9.6.0 - -## 9.5.1 - -### Patch Changes - -- @objectstack/runtime@9.5.1 - -## 9.5.0 - -### Patch Changes - -- @objectstack/runtime@9.5.0 - -## 9.4.0 - -### Patch Changes - -- Updated dependencies [0856476] - - @objectstack/runtime@9.4.0 - -## 9.3.0 - -### Patch Changes - -- Updated dependencies [1ada658] - - @objectstack/runtime@9.3.0 - -## 9.2.0 - -### Patch Changes - -- @objectstack/runtime@9.2.0 - -## 9.1.0 - -### Patch Changes - -- @objectstack/runtime@9.1.0 - -## 9.0.1 - -### Patch Changes - -- @objectstack/runtime@9.0.1 - -## 9.0.0 - -### Patch Changes - -- @objectstack/runtime@9.0.0 - -## 8.0.1 - -### Patch Changes - -- @objectstack/runtime@8.0.1 - -## 8.0.0 - -### Patch Changes - -- Updated dependencies [f68be58] -- Updated dependencies [bc0d85b] -- Updated dependencies [2537e28] -- Updated dependencies [0ec7717] -- Updated dependencies [c262301] - - @objectstack/runtime@8.0.0 - -## 7.9.0 - -### Patch Changes - -- Updated dependencies [ac1fc4c] -- Updated dependencies [ac1fc4c] - - @objectstack/runtime@7.9.0 - -## 7.8.0 - -### Patch Changes - -- Updated dependencies [a75823a] -- Updated dependencies [4fbb86a] -- Updated dependencies [e631f1e] -- Updated dependencies [424ab26] - - @objectstack/runtime@7.8.0 - -## 7.7.0 - -### Patch Changes - -- @objectstack/runtime@7.7.0 - -## 7.6.0 - -### Patch Changes - -- 3377e38: fix(release): stop the fixed-group major cascade caused by internal `@objectstack/*` peerDependencies. - - These packages declared workspace peerDependencies on other framework packages - in the changesets `fixed` group. Inside a fixed group, changesets rewrites those - peer ranges on every release and treats a peer-range change as breaking → major, - which cascaded to **all 69 packages → 8.0.0** on _any_ minor changeset. Required - internal peers are now regular `dependencies`; optional ones move to - `devDependencies` (kept for in-workspace tests, no longer a published peer edge). - Releases now bump correctly (patch/minor) instead of a spurious major. - -- Updated dependencies [8e539cc] - - @objectstack/runtime@7.6.0 - -## 7.5.0 - -## 7.4.1 - -## 7.4.0 - -## 7.3.0 - -## 7.2.1 - -## 7.2.0 - -## 7.1.0 - -## 7.0.0 - -### Patch Changes - -- Updated dependencies [dc72172] -- Updated dependencies [3a630b6] - - @objectstack/runtime@7.0.0 - -## 6.9.0 - -## 6.8.1 - -## 6.8.0 - -## 6.7.1 - -## 6.7.0 - -## 6.6.0 - -## 6.5.1 - -## 6.5.0 - -## 6.4.0 - -## 6.3.0 - -## 6.2.0 - -## 6.1.1 - -## 6.1.0 - -## 6.0.0 - -### Patch Changes - -- Updated dependencies [944f187] - - @objectstack/runtime@6.0.0 - -## 5.2.0 - -## 5.1.0 - -## 5.0.0 - -### Patch Changes - -- Updated dependencies [5e9dcb4] -- Updated dependencies [96ad4df] -- Updated dependencies [df18ae9] - - @objectstack/runtime@5.0.0 - -## 4.2.0 - -## 4.1.1 - -## 4.1.0 - -## 4.0.5 - -### Patch Changes - -- 15e0df6: chore: unify all package versions to a single patch release - -## 4.0.4 - -### Patch Changes - -- @objectstack/runtime@4.0.4 - -## 4.0.3 - -### Patch Changes - -- @objectstack/runtime@4.0.3 - -## 4.0.2 - -### Patch Changes - -- @objectstack/runtime@4.0.2 - -## 4.0.0 - -### Patch Changes - -- f08ffc3: Fix discovery API endpoint routing and protocol consistency. - - **Discovery route standardization:** - - - All adapters (Express, Fastify, Hono, NestJS, Next.js, Nuxt, SvelteKit) now mount the discovery endpoint at `{prefix}/discovery` instead of `{prefix}` root. - - `.well-known/objectstack` redirects now point to `{prefix}/discovery`. - - Client `connect()` fallback URL changed from `/api/v1` to `/api/v1/discovery`. - - Runtime dispatcher handles both `/discovery` (standard) and `/` (legacy) for backward compatibility. - - **Schema & route alignment:** - - - Added `storage` (service: `file-storage`) and `feed` (service: `data`) routes to `DEFAULT_DISPATCHER_ROUTES`. - - Added `feed` and `discovery` fields to `ApiRoutesSchema`. - - Unified `GetDiscoveryResponseSchema` with `DiscoverySchema` as single source of truth. - - Client `getRoute('feed')` fallback updated from `/api/v1/data` to `/api/v1/feed`. - - **Type safety:** - - - Extracted `ApiRouteType` from `ApiRoutes` keys for type-safe client route resolution. - - Removed `as any` type casting in client route access. - -- Updated dependencies [f08ffc3] -- Updated dependencies [e0b0a78] - - @objectstack/runtime@4.0.0 - -## 3.3.1 - -### Patch Changes - -- @objectstack/runtime@3.3.1 - -## 3.3.0 - -### Patch Changes - -- @objectstack/runtime@3.3.0 - -## 3.2.9 - -### Patch Changes - -- @objectstack/runtime@3.2.9 - -## 3.2.8 - -### Patch Changes - -- @objectstack/runtime@3.2.8 - -## 3.2.8 - -### Patch Changes - -- fix: unified catch-all dispatch pattern — `objectStackPlugin()` now delegates all non-framework-specific routes to `HttpDispatcher.dispatch()`, automatically supporting packages, analytics, automation, i18n, ui, openapi, custom endpoints, and any future routes -- Only auth (service check), storage (file upload), GraphQL (raw result), and discovery (response wrapper) remain as explicit routes - -## 3.2.7 - -### Patch Changes - -- @objectstack/runtime@3.2.7 - -## 3.2.6 - -### Patch Changes - -- @objectstack/runtime@3.2.6 - -## 3.2.5 - -### Patch Changes - -- @objectstack/runtime@3.2.5 - -## 3.2.4 - -### Patch Changes - -- @objectstack/runtime@3.2.4 - -## 3.2.3 - -### Patch Changes - -- @objectstack/runtime@3.2.3 - -## 3.2.2 - -### Patch Changes - -- @objectstack/runtime@3.2.2 - -## 3.2.1 - -### Patch Changes - -- @objectstack/runtime@3.2.1 - -## 3.2.0 - -### Patch Changes - -- @objectstack/runtime@3.2.0 - -## 3.1.1 - -### Patch Changes - -- @objectstack/runtime@3.1.1 - -## 3.1.0 - -### Patch Changes - -- @objectstack/runtime@3.1.0 - -## 3.0.11 - -### Patch Changes - -- @objectstack/runtime@3.0.11 - -## 3.0.10 - -### Patch Changes - -- @objectstack/runtime@3.0.10 - -## 3.0.9 - -### Patch Changes - -- @objectstack/runtime@3.0.9 - -## 3.0.8 - -### Patch Changes - -- @objectstack/runtime@3.0.8 - -## 3.0.7 - -### Patch Changes - -- @objectstack/runtime@3.0.7 - -## 3.0.6 - -### Patch Changes - -- @objectstack/runtime@3.0.6 - -## 3.0.5 - -### Patch Changes - -- @objectstack/runtime@3.0.5 - -## 3.0.4 - -### Patch Changes - -- @objectstack/runtime@3.0.4 - -## 3.0.3 - -### Patch Changes - -- Updated dependencies [c7267f6] - - @objectstack/runtime@3.0.3 diff --git a/packages/adapters/fastify/LICENSE b/packages/adapters/fastify/LICENSE deleted file mode 100644 index 16bc23f404..0000000000 --- a/packages/adapters/fastify/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute - must include a readable copy of the attribution notices - contained within such NOTICE file, excluding those notices - that do not pertain to any part of the Derivative Works, - in at least one of the following places: within a NOTICE - text file distributed as part of the Derivative Works; within - the Source form or documentation, if provided along with - the Derivative Works; or, within a display generated by the - Derivative Works, if and wherever such third-party notices - normally appear. The contents of the NOTICE file are for - informational purposes only and do not modify the License. - You may add Your own attribution notices within Derivative - Works that You distribute, alongside or as an addendum to - the NOTICE text from the Work, provided that such additional - attribution notices cannot be construed as modifying the - License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2026 ObjectStack - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/adapters/fastify/README.md b/packages/adapters/fastify/README.md deleted file mode 100644 index 44682cbede..0000000000 --- a/packages/adapters/fastify/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# @objectstack/fastify - -> Fastify plugin for ObjectStack — registers the auto-generated REST API and dispatcher as a Fastify plugin. - -[![npm](https://img.shields.io/npm/v/@objectstack/fastify.svg)](https://www.npmjs.com/package/@objectstack/fastify) -[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) - -## Overview - -Exposes ObjectStack as a standard Fastify plugin. Auth, storage, GraphQL, and discovery routes are handled explicitly; everything else is served by a catch-all delegating to `HttpDispatcher`, so protocol evolution does not require adapter changes. - -## Installation - -```bash -pnpm add @objectstack/fastify fastify -``` - -## Quick Start - -```typescript -import Fastify from 'fastify'; -import { objectStackPlugin } from '@objectstack/fastify'; -import { kernel } from './my-kernel'; - -const app = Fastify({ logger: true }); -await app.register(objectStackPlugin, { kernel, prefix: '/api' }); -await app.listen({ port: 3000 }); -``` - -### Decorator mode - -```typescript -import { objectStackDecorator } from '@objectstack/fastify'; - -app.decorate('objectstack', objectStackDecorator(kernel)); -``` - -## Key Exports - -| Export | Kind | Description | -|:---|:---|:---| -| `objectStackPlugin` | fastify plugin | Registers all dispatcher routes under `prefix`. | -| `objectStackDecorator(kernel)` | function | Returns a decorator that exposes the dispatcher on the Fastify instance. | -| `FastifyAdapterOptions` | interface | `{ kernel, prefix? }`. | - -## Configuration - -| Option | Type | Default | -|:---|:---|:---| -| `kernel` | `ObjectKernel` | — | -| `prefix` | `string` | `'/api'` | - -## Middleware order - -Register the ObjectStack plugin **after** body parsing, CORS, and authentication preprocessors. Do not enable Fastify's automatic schema validation on ObjectStack routes — the protocol does its own Zod validation. - -## When to use - -- ✅ Fastify applications needing high-throughput APIs. -- ✅ Teams already invested in Fastify's plugin ecosystem. - -## When not to use - -- ❌ Edge / serverless Workers — use [`@objectstack/hono`](../hono). - -## Related Packages - -- [`@objectstack/runtime`](../../runtime), [`@objectstack/rest`](../../rest), [`@objectstack/core`](../../core). - -## Links - -- 📖 Docs: -- 📚 API Reference: - -## License - -Apache-2.0 © ObjectStack diff --git a/packages/adapters/fastify/package.json b/packages/adapters/fastify/package.json deleted file mode 100644 index 431ed40749..0000000000 --- a/packages/adapters/fastify/package.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "name": "@objectstack/fastify", - "version": "11.0.0", - "license": "Apache-2.0", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "require": "./dist/index.js" - } - }, - "scripts": { - "build": "tsup --config ../../../tsup.config.ts", - "test": "vitest run", - "test:watch": "vitest" - }, - "peerDependencies": { - "fastify": "^5.8.5" - }, - "devDependencies": { - "@objectstack/runtime": "workspace:*", - "fastify": "^5.8.5", - "typescript": "^6.0.3", - "vitest": "^4.1.9" - }, - "description": "Fastify adapter for ObjectStack — mounts the generated REST API onto a Fastify app.", - "keywords": [ - "objectstack", - "fastify", - "adapter", - "rest", - "node" - ], - "author": "ObjectStack", - "repository": { - "type": "git", - "url": "https://github.com/objectstack-ai/framework.git", - "directory": "packages/adapters/fastify" - }, - "homepage": "https://objectstack.ai/docs", - "bugs": "https://github.com/objectstack-ai/framework/issues", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist", - "README.md" - ], - "engines": { - "node": ">=18.0.0" - }, - "dependencies": { - "@objectstack/runtime": "workspace:^" - } -} diff --git a/packages/adapters/fastify/src/__mocks__/runtime.ts b/packages/adapters/fastify/src/__mocks__/runtime.ts deleted file mode 100644 index 7961fd466c..0000000000 --- a/packages/adapters/fastify/src/__mocks__/runtime.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Stub for @objectstack/runtime - replaced by vi.mock in tests -export const HttpDispatcher = class {}; -export type ObjectKernel = any; -export type HttpDispatcherResult = any; diff --git a/packages/adapters/fastify/src/fastify.test.ts b/packages/adapters/fastify/src/fastify.test.ts deleted file mode 100644 index fecf0b68d4..0000000000 --- a/packages/adapters/fastify/src/fastify.test.ts +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { describe, it, expect, vi, beforeEach } from 'vitest'; - -// Mock dispatcher instance -const mockDispatcher = { - getDiscoveryInfo: vi.fn().mockReturnValue({ version: '1.0', endpoints: [] }), - handleAuth: vi.fn().mockResolvedValue({ handled: true, response: { body: { ok: true }, status: 200 } }), - handleGraphQL: vi.fn().mockResolvedValue({ data: {} }), - handleStorage: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }), - dispatch: vi.fn().mockResolvedValue({ handled: true, response: { body: { success: true }, status: 200 } }), -}; - -vi.mock('@objectstack/runtime', () => { - return { - HttpDispatcher: function HttpDispatcher() { - return mockDispatcher; - }, - }; -}); - -import Fastify from 'fastify'; -import { objectStackPlugin, objectStackDecorator } from './index'; - -const mockKernel = { name: 'test-kernel' } as any; - -describe('objectStackPlugin', () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - it('registers discovery route', async () => { - const app = Fastify(); - await app.register(objectStackPlugin, { kernel: mockKernel }); - await app.ready(); - - const res = await app.inject({ method: 'GET', url: '/api' }); - expect(res.statusCode).toBe(200); - const json = JSON.parse(res.payload); - expect(json.data).toBeDefined(); - expect(json.data.version).toBe('1.0'); - expect(mockDispatcher.getDiscoveryInfo).toHaveBeenCalledWith('/api'); - }); - - it('registers auth route', async () => { - const app = Fastify(); - await app.register(objectStackPlugin, { kernel: mockKernel }); - await app.ready(); - - const res = await app.inject({ - method: 'POST', - url: '/api/auth/login', - payload: { email: 'a@b.com' }, - }); - expect(res.statusCode).toBe(200); - const json = JSON.parse(res.payload); - expect(json.ok).toBe(true); - expect(mockDispatcher.handleAuth).toHaveBeenCalledWith( - 'login', - 'POST', - { email: 'a@b.com' }, - expect.objectContaining({ request: expect.anything() }), - ); - }); - - it('registers graphql route', async () => { - const app = Fastify(); - await app.register(objectStackPlugin, { kernel: mockKernel }); - await app.ready(); - - const body = { query: '{ objects { name } }' }; - const res = await app.inject({ - method: 'POST', - url: '/api/graphql', - payload: body, - }); - expect(res.statusCode).toBe(200); - const json = JSON.parse(res.payload); - expect(json.data).toBeDefined(); - expect(mockDispatcher.handleGraphQL).toHaveBeenCalledWith( - body, - expect.objectContaining({ request: expect.anything() }), - ); - }); - - it('registers metadata route', async () => { - const app = Fastify(); - await app.register(objectStackPlugin, { kernel: mockKernel }); - await app.ready(); - - const res = await app.inject({ method: 'GET', url: '/api/meta/objects' }); - expect(res.statusCode).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/objects', - undefined, - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('registers data route', async () => { - const app = Fastify(); - await app.register(objectStackPlugin, { kernel: mockKernel }); - await app.ready(); - - const res = await app.inject({ method: 'GET', url: '/api/data/account' }); - expect(res.statusCode).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/data/account', - undefined, - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('registers storage route', async () => { - const app = Fastify(); - await app.register(objectStackPlugin, { kernel: mockKernel }); - await app.ready(); - - const res = await app.inject({ method: 'GET', url: '/api/storage/files' }); - expect(res.statusCode).toBe(200); - expect(mockDispatcher.handleStorage).toHaveBeenCalledWith( - '/files', - 'GET', - undefined, - expect.objectContaining({ request: expect.anything() }), - ); - }); - - it('uses custom prefix', async () => { - const app = Fastify(); - // Pass prefix as part of options but not via Fastify's built-in prefix - await app.register(async (instance) => { - await objectStackPlugin(instance, { kernel: mockKernel, prefix: '/v2' }); - }); - await app.ready(); - - const res = await app.inject({ method: 'GET', url: '/v2' }); - expect(res.statusCode).toBe(200); - expect(mockDispatcher.getDiscoveryInfo).toHaveBeenCalledWith('/v2'); - }); - - it('returns error on exception', async () => { - mockDispatcher.dispatch.mockRejectedValueOnce( - Object.assign(new Error('Forbidden'), { statusCode: 403 }), - ); - const app = Fastify(); - await app.register(objectStackPlugin, { kernel: mockKernel }); - await app.ready(); - - const res = await app.inject({ method: 'GET', url: '/api/data/account' }); - expect(res.statusCode).toBe(403); - const json = JSON.parse(res.payload); - expect(json.success).toBe(false); - expect(json.error.message).toBe('Forbidden'); - }); -}); - -describe('objectStackDecorator', () => { - it('attaches kernel to request via hook', async () => { - const app = Fastify(); - // The decorator plugin adds an onRequest hook - const decoratorPlugin = objectStackDecorator(mockKernel); - await decoratorPlugin(app); - - let capturedKernel: any = null; - app.get('/test', async (request) => { - capturedKernel = (request as any).objectStack; - return { ok: true }; - }); - - await app.ready(); - await app.inject({ method: 'GET', url: '/test' }); - expect(capturedKernel).toBe(mockKernel); - }); -}); diff --git a/packages/adapters/fastify/src/index.ts b/packages/adapters/fastify/src/index.ts deleted file mode 100644 index 246555169b..0000000000 --- a/packages/adapters/fastify/src/index.ts +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { type FastifyInstance, type FastifyRequest, type FastifyReply } from 'fastify'; -import { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime'; - -export interface FastifyAdapterOptions { - kernel: ObjectKernel; - prefix?: string; -} - -/** - * Auth service interface with handleRequest method - */ -interface AuthService { - handleRequest(request: Request): Promise; -} - -/** - * Registers ObjectStack routes as a Fastify plugin. - * - * Only routes that need framework-specific handling (auth service, storage - * file upload, GraphQL raw result, discovery wrapper) are registered explicitly. - * All other routes are handled by a catch-all that delegates to - * `HttpDispatcher.dispatch()`, making the adapter automatically support - * new routes added to the dispatcher. - * - * @example - * ```ts - * import Fastify from 'fastify'; - * import { objectStackPlugin } from '@objectstack/fastify'; - * - * const app = Fastify(); - * app.register(objectStackPlugin, { kernel, prefix: '/api' }); - * app.listen({ port: 3000 }); - * ``` - */ -export async function objectStackPlugin(fastify: FastifyInstance, options: FastifyAdapterOptions) { - const dispatcher = new HttpDispatcher(options.kernel); - const prefix = options.prefix || '/api'; - - const sendResult = (result: HttpDispatcherResult, reply: FastifyReply) => { - if (result.handled) { - if (result.response) { - if (result.response.headers) { - Object.entries(result.response.headers).forEach(([k, v]) => reply.header(k, v as string)); - } - return reply.status(result.response.status).send(result.response.body); - } - if (result.result) { - const response = result.result; - if (response.type === 'redirect' && response.url) { - return reply.redirect(response.url); - } - if (response.type === 'stream' && response.stream) { - if (response.headers) { - Object.entries(response.headers).forEach(([k, v]) => reply.header(k, v as string)); - } - return reply.send(response.stream); - } - return reply.status(200).send(response); - } - } - return reply.status(404).send({ success: false, error: { message: 'Not Found', code: 404 } }); - }; - - const errorResponse = (err: any, reply: FastifyReply) => { - return reply.status(err.statusCode || 500).send({ - success: false, - error: { - message: err.message || 'Internal Server Error', - code: err.statusCode || 500, - }, - }); - }; - - // ─── Explicit routes (framework-specific handling required) ──────────────── - - // --- Discovery --- - fastify.get(prefix, async (_request: FastifyRequest, reply: FastifyReply) => { - return reply.send({ data: await dispatcher.getDiscoveryInfo(prefix) }); - }); - - fastify.get(`${prefix}/discovery`, async (_request: FastifyRequest, reply: FastifyReply) => { - return reply.send({ data: await dispatcher.getDiscoveryInfo(prefix) }); - }); - - // --- .well-known --- - fastify.get('/.well-known/objectstack', async (_request: FastifyRequest, reply: FastifyReply) => { - return reply.redirect(prefix); - }); - - // --- Auth (needs auth service integration) --- - fastify.all(`${prefix}/auth/*`, async (request: FastifyRequest, reply: FastifyReply) => { - try { - const path = request.url.substring(`${prefix}/auth/`.length).split('?')[0]; - const method = request.method; - - // Try AuthPlugin service first (prefer async to support factory-based services) - let authService: AuthService | null = null; - try { - if (typeof options.kernel.getServiceAsync === 'function') { - authService = await options.kernel.getServiceAsync('auth'); - } else if (typeof options.kernel.getService === 'function') { - authService = options.kernel.getService('auth'); - } - } catch { - // Service not registered — fall through to dispatcher - authService = null; - } - - if (authService && typeof authService.handleRequest === 'function') { - const protocol = request.protocol || 'http'; - const host = request.hostname || 'localhost'; - const url = `${protocol}://${host}${request.url}`; - const headers = new Headers(); - if (request.headers) { - Object.entries(request.headers).forEach(([k, v]) => { - if (typeof v === 'string') headers.set(k, v); - else if (Array.isArray(v)) headers.set(k, v.join(', ')); - }); - } - const init: RequestInit = { method, headers }; - if (method !== 'GET' && method !== 'HEAD' && request.body) { - init.body = JSON.stringify(request.body); - if (!headers.has('content-type')) { - headers.set('content-type', 'application/json'); - } - } - const webRequest = new Request(url, init); - const response = await authService.handleRequest(webRequest); - reply.status(response.status); - response.headers.forEach((v: string, k: string) => reply.header(k, v)); - const text = await response.text(); - return reply.send(text); - } - - // Fallback to dispatcher - const body = method === 'GET' || method === 'HEAD' ? {} : (request.body as any) || {}; - const result = await dispatcher.handleAuth(path, method, body, { request: request.raw }); - return sendResult(result, reply); - } catch (err: any) { - return errorResponse(err, reply); - } - }); - - // --- GraphQL (returns raw result, not HttpDispatcherResult) --- - fastify.post(`${prefix}/graphql`, async (request: FastifyRequest, reply: FastifyReply) => { - try { - const result = await dispatcher.handleGraphQL(request.body as any, { request: request.raw }); - return reply.send(result); - } catch (err: any) { - return errorResponse(err, reply); - } - }); - - // --- Storage (needs file upload handling) --- - fastify.all(`${prefix}/storage/*`, async (request: FastifyRequest, reply: FastifyReply) => { - try { - const urlPath = request.url.split('?')[0]; - const subPath = urlPath.substring(`${prefix}/storage`.length); - const method = request.method; - const file = (request as any).file || (request.body as any)?.file; - const result = await dispatcher.handleStorage(subPath, method, file, { request: request.raw }); - return sendResult(result, reply); - } catch (err: any) { - return errorResponse(err, reply); - } - }); - - // ─── Catch-all: delegate to dispatcher.dispatch() ───────────────────────── - // Handles meta, data, packages, analytics, automation, i18n, ui, openapi, - // custom API endpoints, and any future routes added to HttpDispatcher. - fastify.all(`${prefix}/*`, async (request: FastifyRequest, reply: FastifyReply) => { - try { - const urlPath = request.url.split('?')[0]; - const subPath = urlPath.substring(prefix.length); - const method = request.method; - const body = (method === 'POST' || method === 'PUT' || method === 'PATCH') ? request.body : undefined; - const result = await dispatcher.dispatch(method, subPath, body, request.query, { request: request.raw }, prefix); - return sendResult(result, reply); - } catch (err: any) { - return errorResponse(err, reply); - } - }); -} - -/** - * Fastify plugin that attaches the ObjectStack kernel to each request. - */ -export function objectStackDecorator(kernel: ObjectKernel) { - return async function (fastify: FastifyInstance) { - fastify.addHook('onRequest', async (request) => { - (request as any).objectStack = kernel; - }); - }; -} diff --git a/packages/adapters/fastify/tsconfig.json b/packages/adapters/fastify/tsconfig.json deleted file mode 100644 index e8dfb8f584..0000000000 --- a/packages/adapters/fastify/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": "../../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "esModuleInterop": true, - "skipLibCheck": true, - "lib": [ - "ES2020", - "DOM", - "DOM.Iterable" - ], - "types": [ - "node" - ] - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "dist" - ] -} diff --git a/packages/adapters/fastify/vitest.config.ts b/packages/adapters/fastify/vitest.config.ts deleted file mode 100644 index e2051df210..0000000000 --- a/packages/adapters/fastify/vitest.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { defineConfig } from 'vitest/config'; -import path from 'node:path'; - -export default defineConfig({ - test: { - globals: true, - environment: 'node', - }, - resolve: { - alias: { - '@objectstack/runtime': path.resolve(__dirname, 'src/__mocks__/runtime.ts'), - }, - }, -}); diff --git a/packages/adapters/hono/README.md b/packages/adapters/hono/README.md index 97ee4dc110..252a7e2b48 100644 --- a/packages/adapters/hono/README.md +++ b/packages/adapters/hono/README.md @@ -74,11 +74,6 @@ app.use('/api/*', objectStackMiddleware(kernel)); - ✅ Edge, serverless, and multi-runtime deployments. - ✅ Projects wanting built-in CORS with wildcard patterns. -## When not to use - -- ❌ Existing Express app — use [`@objectstack/express`](../express). -- ❌ NestJS enterprise stacks — use [`@objectstack/nestjs`](../nestjs). - ## Related Packages - [`@objectstack/plugin-hono-server`](../../plugins/plugin-hono-server) — optional plugin that hosts a Hono server inside the kernel. diff --git a/packages/adapters/nestjs/CHANGELOG.md b/packages/adapters/nestjs/CHANGELOG.md deleted file mode 100644 index 0c7592c234..0000000000 --- a/packages/adapters/nestjs/CHANGELOG.md +++ /dev/null @@ -1,656 +0,0 @@ -# @objectstack/nestjs - -## 11.0.0 - -### Patch Changes - -- Updated dependencies [4d99a5c] -- Updated dependencies [61d441f] -- Updated dependencies [c224e18] -- Updated dependencies [6c4fbd9] -- Updated dependencies [aa33b02] - - @objectstack/runtime@11.0.0 - -## 10.3.0 - -### Patch Changes - -- Updated dependencies [8cf4f7c] -- Updated dependencies [f2063f3] - - @objectstack/runtime@10.3.0 - -## 10.2.0 - -### Patch Changes - -- @objectstack/runtime@10.2.0 - -## 10.1.0 - -### Patch Changes - -- Updated dependencies [ac79f16] -- Updated dependencies [94d2161] - - @objectstack/runtime@10.1.0 - -## 10.0.0 - -### Patch Changes - -- Updated dependencies [e16f2a8] -- Updated dependencies [47d978a] -- Updated dependencies [220ce5b] - - @objectstack/runtime@10.0.0 - -## 9.11.0 - -### Patch Changes - -- Updated dependencies [2afb612] - - @objectstack/runtime@9.11.0 - -## 9.10.0 - -### Patch Changes - -- Updated dependencies [1f88fd9] -- Updated dependencies [e2b5324] - - @objectstack/runtime@9.10.0 - -## 9.9.1 - -### Patch Changes - -- @objectstack/runtime@9.9.1 - -## 9.9.0 - -### Patch Changes - -- Updated dependencies [11af299] -- Updated dependencies [83fd318] -- Updated dependencies [9afeb2d] - - @objectstack/runtime@9.9.0 - -## 9.8.0 - -### Patch Changes - -- @objectstack/runtime@9.8.0 - -## 9.7.0 - -### Patch Changes - -- @objectstack/runtime@9.7.0 - -## 9.6.0 - -### Patch Changes - -- Updated dependencies [71578f2] - - @objectstack/runtime@9.6.0 - -## 9.5.1 - -### Patch Changes - -- @objectstack/runtime@9.5.1 - -## 9.5.0 - -### Patch Changes - -- @objectstack/runtime@9.5.0 - -## 9.4.0 - -### Patch Changes - -- Updated dependencies [0856476] - - @objectstack/runtime@9.4.0 - -## 9.3.0 - -### Patch Changes - -- Updated dependencies [1ada658] - - @objectstack/runtime@9.3.0 - -## 9.2.0 - -### Patch Changes - -- @objectstack/runtime@9.2.0 - -## 9.1.0 - -### Patch Changes - -- @objectstack/runtime@9.1.0 - -## 9.0.1 - -### Patch Changes - -- @objectstack/runtime@9.0.1 - -## 9.0.0 - -### Patch Changes - -- @objectstack/runtime@9.0.0 - -## 8.0.1 - -### Patch Changes - -- @objectstack/runtime@8.0.1 - -## 8.0.0 - -### Patch Changes - -- Updated dependencies [f68be58] -- Updated dependencies [bc0d85b] -- Updated dependencies [2537e28] -- Updated dependencies [0ec7717] -- Updated dependencies [c262301] - - @objectstack/runtime@8.0.0 - -## 7.9.0 - -### Patch Changes - -- Updated dependencies [ac1fc4c] -- Updated dependencies [ac1fc4c] - - @objectstack/runtime@7.9.0 - -## 7.8.0 - -### Patch Changes - -- Updated dependencies [a75823a] -- Updated dependencies [4fbb86a] -- Updated dependencies [e631f1e] -- Updated dependencies [424ab26] - - @objectstack/runtime@7.8.0 - -## 7.7.0 - -### Patch Changes - -- @objectstack/runtime@7.7.0 - -## 7.6.0 - -### Patch Changes - -- 3377e38: fix(release): stop the fixed-group major cascade caused by internal `@objectstack/*` peerDependencies. - - These packages declared workspace peerDependencies on other framework packages - in the changesets `fixed` group. Inside a fixed group, changesets rewrites those - peer ranges on every release and treats a peer-range change as breaking → major, - which cascaded to **all 69 packages → 8.0.0** on _any_ minor changeset. Required - internal peers are now regular `dependencies`; optional ones move to - `devDependencies` (kept for in-workspace tests, no longer a published peer edge). - Releases now bump correctly (patch/minor) instead of a spurious major. - -- Updated dependencies [8e539cc] - - @objectstack/runtime@7.6.0 - -## 7.5.0 - -## 7.4.1 - -## 7.4.0 - -## 7.3.0 - -## 7.2.1 - -## 7.2.0 - -## 7.1.0 - -## 7.0.0 - -### Patch Changes - -- Updated dependencies [dc72172] -- Updated dependencies [3a630b6] - - @objectstack/runtime@7.0.0 - -## 6.9.0 - -## 6.8.1 - -## 6.8.0 - -## 6.7.1 - -## 6.7.0 - -## 6.6.0 - -## 6.5.1 - -## 6.5.0 - -## 6.4.0 - -## 6.3.0 - -## 6.2.0 - -## 6.1.1 - -## 6.1.0 - -## 6.0.0 - -### Patch Changes - -- Updated dependencies [944f187] - - @objectstack/runtime@6.0.0 - -## 5.2.0 - -## 5.1.0 - -## 5.0.0 - -### Patch Changes - -- Updated dependencies [5e9dcb4] -- Updated dependencies [96ad4df] -- Updated dependencies [df18ae9] - - @objectstack/runtime@5.0.0 - -## 4.2.0 - -## 4.1.1 - -## 4.1.0 - -## 4.0.5 - -### Patch Changes - -- 15e0df6: chore: unify all package versions to a single patch release - -## 4.0.4 - -### Patch Changes - -- @objectstack/runtime@4.0.4 - -## 4.0.3 - -### Patch Changes - -- @objectstack/runtime@4.0.3 - -## 4.0.2 - -### Patch Changes - -- @objectstack/runtime@4.0.2 - -## 4.0.0 - -### Patch Changes - -- f08ffc3: Fix discovery API endpoint routing and protocol consistency. - - **Discovery route standardization:** - - - All adapters (Express, Fastify, Hono, NestJS, Next.js, Nuxt, SvelteKit) now mount the discovery endpoint at `{prefix}/discovery` instead of `{prefix}` root. - - `.well-known/objectstack` redirects now point to `{prefix}/discovery`. - - Client `connect()` fallback URL changed from `/api/v1` to `/api/v1/discovery`. - - Runtime dispatcher handles both `/discovery` (standard) and `/` (legacy) for backward compatibility. - - **Schema & route alignment:** - - - Added `storage` (service: `file-storage`) and `feed` (service: `data`) routes to `DEFAULT_DISPATCHER_ROUTES`. - - Added `feed` and `discovery` fields to `ApiRoutesSchema`. - - Unified `GetDiscoveryResponseSchema` with `DiscoverySchema` as single source of truth. - - Client `getRoute('feed')` fallback updated from `/api/v1/data` to `/api/v1/feed`. - - **Type safety:** - - - Extracted `ApiRouteType` from `ApiRoutes` keys for type-safe client route resolution. - - Removed `as any` type casting in client route access. - -- Updated dependencies [f08ffc3] -- Updated dependencies [e0b0a78] - - @objectstack/runtime@4.0.0 - -## 3.3.1 - -### Patch Changes - -- @objectstack/runtime@3.3.1 - -## 3.3.0 - -### Patch Changes - -- @objectstack/runtime@3.3.0 - -## 3.2.9 - -### Patch Changes - -- @objectstack/runtime@3.2.9 - -## 3.2.8 - -### Patch Changes - -- @objectstack/runtime@3.2.8 - -## 3.2.8 - -### Patch Changes - -- fix: unified catch-all dispatch pattern — `ObjectStackController` now delegates non-framework-specific routes to `HttpDispatcher.dispatch()`, automatically supporting packages, analytics, automation, i18n, ui, openapi, custom endpoints, and any future routes -- Only auth (service check), storage (file upload), GraphQL (raw result), and discovery remain as explicit routes -- Meta and data routes now use `dispatch()` for consistent query/body handling - -## 3.2.7 - -### Patch Changes - -- @objectstack/runtime@3.2.7 - -## 3.2.6 - -### Patch Changes - -- @objectstack/runtime@3.2.6 - -## 3.2.5 - -### Patch Changes - -- @objectstack/runtime@3.2.5 - -## 3.2.4 - -### Patch Changes - -- @objectstack/runtime@3.2.4 - -## 3.2.3 - -### Patch Changes - -- @objectstack/runtime@3.2.3 - -## 3.2.2 - -### Patch Changes - -- @objectstack/runtime@3.2.2 - -## 3.2.1 - -### Patch Changes - -- @objectstack/runtime@3.2.1 - -## 3.2.0 - -### Patch Changes - -- @objectstack/runtime@3.2.0 - -## 3.1.1 - -### Patch Changes - -- @objectstack/runtime@3.1.1 - -## 3.1.0 - -### Patch Changes - -- @objectstack/runtime@3.1.0 - -## 3.0.11 - -### Patch Changes - -- @objectstack/runtime@3.0.11 - -## 3.0.10 - -### Patch Changes - -- @objectstack/runtime@3.0.10 - -## 3.0.9 - -### Patch Changes - -- @objectstack/runtime@3.0.9 - -## 3.0.8 - -### Patch Changes - -- @objectstack/runtime@3.0.8 - -## 3.0.7 - -### Patch Changes - -- @objectstack/runtime@3.0.7 - -## 3.0.6 - -### Patch Changes - -- @objectstack/runtime@3.0.6 - -## 3.0.5 - -### Patch Changes - -- @objectstack/runtime@3.0.5 - -## 3.0.4 - -### Patch Changes - -- @objectstack/runtime@3.0.4 - -## 3.0.3 - -### Patch Changes - -- c7267f6: Patch release for maintenance updates and improvements. -- Updated dependencies [c7267f6] - - @objectstack/runtime@3.0.3 - -## 3.0.2 - -### Patch Changes - -- @objectstack/runtime@3.0.2 - -## 3.0.1 - -### Patch Changes - -- @objectstack/runtime@3.0.1 - -## 3.0.0 - -### Major Changes - -- Release v3.0.0 — unified version bump for all ObjectStack packages. - -### Patch Changes - -- Updated dependencies - - @objectstack/runtime@3.0.0 - -## 2.0.7 - -### Patch Changes - -- @objectstack/runtime@2.0.7 - -## 2.0.6 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/runtime@2.0.6 - -## 2.0.5 - -### Patch Changes - -- @objectstack/runtime@2.0.5 - -## 2.0.4 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/runtime@2.0.4 - -## 2.0.3 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/runtime@2.0.3 - -## 2.0.2 - -### Patch Changes - -- @objectstack/runtime@2.0.2 - -## 2.0.1 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/runtime@2.0.1 - -## 2.0.0 - -### Patch Changes - -- @objectstack/runtime@2.0.0 - -## 1.0.12 - -### Patch Changes - -- Updated dependencies - - @objectstack/runtime@1.0.12 - -## 1.0.11 - -### Patch Changes - -- @objectstack/runtime@1.0.11 - -## 1.0.10 - -### Patch Changes - -- @objectstack/runtime@1.0.10 - -## 1.0.9 - -### Patch Changes - -- @objectstack/runtime@1.0.9 - -## 1.0.8 - -### Patch Changes - -- 8f2a3a2: fix: standardize discovery endpoint response to include 'data' wrapper - - @objectstack/runtime@1.0.8 - -## 1.0.7 - -### Patch Changes - -- ebdf787: feat: implement standard service discovery via `/.well-known/objectstack` -- Updated dependencies [ebdf787] - - @objectstack/runtime@1.0.7 - -## 1.0.6 - -### Patch Changes - -- @objectstack/runtime@1.0.6 - -## 1.0.5 - -### Patch Changes - -- b1d24bd: refactor: migrate build system from tsc to tsup for faster builds - - Replaced `tsc` with `tsup` (using esbuild) across all packages - - Added shared `tsup.config.ts` in workspace root - - Added `tsup` as workspace dev dependency - - significantly improved build performance -- Updated dependencies [b1d24bd] -- Updated dependencies [877b864] - - @objectstack/runtime@1.0.5 - -## 1.0.4 - -### Patch Changes - -- @objectstack/runtime@1.0.4 - -## 1.0.3 - -### Patch Changes - -- Updated dependencies [fb2eabd] - - @objectstack/runtime@1.0.3 - -## 1.0.2 - -### Patch Changes - -- 109fc5b: Unified patch release to align all package versions. -- Updated dependencies [a0a6c85] -- Updated dependencies [109fc5b] - - @objectstack/runtime@1.0.2 - -## 1.0.1 - -### Patch Changes - -- Updated dependencies - - @objectstack/runtime@1.0.1 - -## 1.0.0 - -### Major Changes - -- Major version release for ObjectStack Protocol v1.0. - - Stabilized Protocol Definitions - - Enhanced Runtime Plugin Support - - Fixed Type Compliance across Monorepo - -### Patch Changes - -- Updated dependencies - - @objectstack/runtime@1.0.0 diff --git a/packages/adapters/nestjs/LICENSE b/packages/adapters/nestjs/LICENSE deleted file mode 100644 index 16bc23f404..0000000000 --- a/packages/adapters/nestjs/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute - must include a readable copy of the attribution notices - contained within such NOTICE file, excluding those notices - that do not pertain to any part of the Derivative Works, - in at least one of the following places: within a NOTICE - text file distributed as part of the Derivative Works; within - the Source form or documentation, if provided along with - the Derivative Works; or, within a display generated by the - Derivative Works, if and wherever such third-party notices - normally appear. The contents of the NOTICE file are for - informational purposes only and do not modify the License. - You may add Your own attribution notices within Derivative - Works that You distribute, alongside or as an addendum to - the NOTICE text from the Work, provided that such additional - attribution notices cannot be construed as modifying the - License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2026 ObjectStack - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/adapters/nestjs/README.md b/packages/adapters/nestjs/README.md deleted file mode 100644 index 1b2e685285..0000000000 --- a/packages/adapters/nestjs/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# @objectstack/nestjs - -> NestJS adapter for ObjectStack — installs the auto-generated REST API as a dynamic NestJS module. - -[![npm](https://img.shields.io/npm/v/@objectstack/nestjs.svg)](https://www.npmjs.com/package/@objectstack/nestjs) -[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) - -## Overview - -Provides `ObjectStackModule` — a `DynamicModule` that registers `ObjectStackService`, a catch-all `ObjectStackController` (routes under `/api`), and a `DiscoveryController` for service self-description. The module injects `ObjectKernel` via the `OBJECT_KERNEL` DI token so controllers and guards can resolve it normally. - -## Installation - -```bash -pnpm add @objectstack/nestjs @nestjs/common @nestjs/core -``` - -## Quick Start - -```typescript -import { Module } from '@nestjs/common'; -import { ObjectStackModule } from '@objectstack/nestjs'; -import { kernel } from './my-kernel'; - -@Module({ - imports: [ObjectStackModule.forRoot({ kernel })], -}) -export class AppModule {} -``` - -### Inject the kernel elsewhere - -```typescript -import { Inject, Injectable } from '@nestjs/common'; -import { OBJECT_KERNEL } from '@objectstack/nestjs'; -import type { ObjectKernel } from '@objectstack/runtime'; - -@Injectable() -class MyService { - constructor(@Inject(OBJECT_KERNEL) private kernel: ObjectKernel) {} -} -``` - -## Key Exports - -| Export | Kind | Description | -|:---|:---|:---| -| `ObjectStackModule` | dynamic module | `ObjectStackModule.forRoot({ kernel })`. | -| `ObjectStackService` | injectable | Exposes `dispatcher` and `getKernel()`. | -| `ObjectStackController` | controller | Catch-all under `/api` delegating to `HttpDispatcher`. | -| `DiscoveryController` | controller | Serves `/api/v1/discovery` and `/.well-known/objectstack`. | -| `OBJECT_KERNEL` | DI token | Resolves the bootstrapped kernel. | -| `ConnectReq` | param decorator | Retrieves the raw request for custom handlers. | - -## Middleware order - -NestJS global `ValidationPipe` MUST NOT run on ObjectStack routes — the protocol owns its validation. Use `@UsePipes` only on NestJS-owned controllers. - -## When to use - -- ✅ NestJS monoliths that need ObjectStack as a module alongside domain controllers. -- ✅ Teams standardized on NestJS DI and decorators. - -## When not to use - -- ❌ Edge runtimes — use [`@objectstack/hono`](../hono). - -## Related Packages - -- [`@objectstack/runtime`](../../runtime), [`@objectstack/rest`](../../rest), [`@objectstack/core`](../../core). - -## Links - -- 📖 Docs: -- 📚 API Reference: - -## License - -Apache-2.0 © ObjectStack diff --git a/packages/adapters/nestjs/package.json b/packages/adapters/nestjs/package.json deleted file mode 100644 index 416959fca1..0000000000 --- a/packages/adapters/nestjs/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "@objectstack/nestjs", - "version": "11.0.0", - "license": "Apache-2.0", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "scripts": { - "build": "tsup --config ../../../tsup.config.ts", - "test": "vitest run", - "test:watch": "vitest" - }, - "peerDependencies": { - "@nestjs/common": "^11.1.19", - "@nestjs/core": "^11.1.19" - }, - "devDependencies": { - "@nestjs/common": "^11.1.27", - "@nestjs/core": "^11.1.27", - "@objectstack/runtime": "workspace:*", - "typescript": "^6.0.3", - "vitest": "^4.1.9" - }, - "description": "NestJS adapter for ObjectStack — integrates the generated REST API into a NestJS module.", - "keywords": [ - "objectstack", - "nestjs", - "adapter", - "rest" - ], - "author": "ObjectStack", - "repository": { - "type": "git", - "url": "https://github.com/objectstack-ai/framework.git", - "directory": "packages/adapters/nestjs" - }, - "homepage": "https://objectstack.ai/docs", - "bugs": "https://github.com/objectstack-ai/framework/issues", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist", - "README.md" - ], - "engines": { - "node": ">=18.0.0" - }, - "dependencies": { - "@objectstack/runtime": "workspace:^" - } -} diff --git a/packages/adapters/nestjs/src/__mocks__/runtime.ts b/packages/adapters/nestjs/src/__mocks__/runtime.ts deleted file mode 100644 index 3b6d884d36..0000000000 --- a/packages/adapters/nestjs/src/__mocks__/runtime.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Stub for @objectstack/runtime - resolved via vitest alias -import { vi } from 'vitest'; - -export class HttpDispatcher { - getDiscoveryInfo = vi.fn().mockResolvedValue({ version: '1.0' }); - handleGraphQL = vi.fn().mockResolvedValue({ data: {} }); - handleAuth = vi.fn().mockResolvedValue({ handled: true, response: { status: 200, body: { ok: true } } }); - handleMetadata = vi.fn().mockResolvedValue({ handled: true, response: { status: 200, body: [] } }); - handleData = vi.fn().mockResolvedValue({ handled: true, response: { status: 200, body: [] } }); - handleStorage = vi.fn().mockResolvedValue({ handled: true, response: { status: 200, body: {} } }); - dispatch = vi.fn().mockResolvedValue({ handled: true, response: { status: 200, body: { success: true } } }); - - constructor(_kernel: any) {} -} - -export type ObjectKernel = any; -export type HttpDispatcherResult = any; diff --git a/packages/adapters/nestjs/src/index.ts b/packages/adapters/nestjs/src/index.ts deleted file mode 100644 index 79e856cc9b..0000000000 --- a/packages/adapters/nestjs/src/index.ts +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { DynamicModule, Module, Global, Inject, Provider, Controller, Post, Get, Body, Query, Req, Res, All, createParamDecorator, ExecutionContext } from '@nestjs/common'; -import { Injectable } from '@nestjs/common'; -import { ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime'; - -export const OBJECT_KERNEL = 'OBJECT_KERNEL'; - -export const ConnectReq = createParamDecorator( - (_data: unknown, ctx: ExecutionContext) => { - return ctx.switchToHttp().getRequest(); - }, -); - -/** - * Auth service interface with handleRequest method - */ -interface AuthService { - handleRequest(request: Request): Promise; -} - -// --- Service --- - -@Injectable() -export class ObjectStackService { - public dispatcher: HttpDispatcher; - - constructor(@Inject(OBJECT_KERNEL) private readonly kernel: ObjectKernel) { - this.dispatcher = new HttpDispatcher(kernel); - } - - getKernel() { - return this.kernel; - } -} - -// --- Controller --- - -@Controller('api') -export class ObjectStackController { - constructor(private readonly service: ObjectStackService) {} - - private async normalizeResponse(result: HttpDispatcherResult, res: any) { - if (result.handled) { - if (result.response) { - res.status(result.response.status); - if (result.response.headers) { - Object.entries(result.response.headers).forEach(([k, v]) => res.setHeader(k, v)); - } - return res.json(result.response.body); - } - if (result.result) { - const response = result.result; - - // Handle redirect - if (response.type === 'redirect' && response.url) { - return res.redirect(response.url); - } - - // Handle stream - if (response.type === 'stream' && response.stream) { - if (response.headers) { - Object.entries(response.headers).forEach(([k, v]) => res.setHeader(k, v)); - } - response.stream.pipe(res); - return; - } - - // If response is a standard Response object - if (response && typeof response.status === 'number' && typeof response.text === 'function') { - res.status(response.status); - if (response.headers && typeof response.headers.forEach === 'function') { - response.headers.forEach((v: string, k: string) => res.setHeader(k, v)); - } - const text = await response.text(); - res.send(text); - return; - } - return res.status(200).json(response); - } - } - return res.status(404).json({ success: false, error: { message: 'Not Found', code: 404 } }); - } - - private async handleError(err: any, res: any) { - return res.status(err.statusCode || 500).json({ - success: false, - error: { - message: err.message || 'Internal Server Error', - code: err.statusCode || 500, - details: err.details - } - }); - } - - // --- Discovery Endpoint --- - @Get('discovery') - async discovery() { - return { data: await this.service.dispatcher.getDiscoveryInfo('/api') }; - } - - @Post('graphql') - async graphql(@Body() body: any, @Req() req: any, @Res() res: any) { - try { - const result = await this.service.dispatcher.handleGraphQL(body, { request: req }); - return res.json(result); - } catch (err) { - return this.handleError(err, res); - } - } - - // Auth (Generic Auth Handler) - @All('auth/*') - async auth(@Req() req: any, @Res() res: any, @Body() body: any) { - try { - // Try AuthPlugin service first (prefer async to support factory-based services) - const kernel = this.service.getKernel(); - let authService: AuthService | null = null; - try { - if (typeof kernel.getServiceAsync === 'function') { - authService = await kernel.getServiceAsync('auth'); - } else if (typeof kernel.getService === 'function') { - authService = kernel.getService('auth'); - } - } catch { - // Service not registered — fall through to legacy dispatcher - authService = null; - } - - if (authService && typeof authService.handleRequest === 'function') { - // Construct a Web standard Request from the Express/Fastify request - const protocol = req.protocol || 'http'; - const host = req.get?.('host') || req.headers?.host || 'localhost'; - const url = `${protocol}://${host}${req.originalUrl || req.url}`; - const headers = new Headers(); - if (req.headers) { - Object.entries(req.headers).forEach(([k, v]) => { - if (typeof v === 'string') headers.set(k, v); - else if (Array.isArray(v)) headers.set(k, v.join(', ')); - }); - } - const init: RequestInit = { method: req.method, headers }; - if (req.method !== 'GET' && req.method !== 'HEAD' && body) { - init.body = JSON.stringify(body); - if (!headers.has('content-type')) { - headers.set('content-type', 'application/json'); - } - } - const webRequest = new Request(url, init); - const response = await authService.handleRequest(webRequest); - - // Convert Web Response to Express/Fastify response - res.status(response.status); - response.headers.forEach((v: string, k: string) => res.setHeader(k, v)); - const text = await response.text(); - return res.send(text); - } - - // Fallback to legacy dispatcher - const path = req.params[0] || req.url.split('/auth/')[1]?.split('?')[0] || ''; - const result = await this.service.dispatcher.handleAuth(path, req.method, body, { request: req, response: res }); - return this.normalizeResponse(result, res); - } catch (err: any) { - return this.handleError(err, res); - } - } - - // Metadata - @All('meta*') - async metadata(@Req() req: any, @Res() res: any, @Body() body?: any, @Query() query?: any) { - try { - // /api/meta/objects -> objects - let path = req.params[0] || ''; - if (req.url.includes('/meta')) { - path = req.url.split('/meta')[1].split('?')[0]; - } - - const result = await this.service.dispatcher.dispatch(req.method, '/meta' + path, body || req.body, query, { request: req }); - return this.normalizeResponse(result, res); - } catch (err) { - return this.handleError(err, res); - } - } - - // Data - @All('data*') - async data(@Req() req: any, @Res() res: any, @Body() body: any, @Query() query: any) { - try { - let path = req.params[0] || ''; - if (req.url.includes('/data')) { - path = req.url.substring(req.url.indexOf('/data') + 5).split('?')[0]; - } - - const result = await this.service.dispatcher.dispatch(req.method, '/data' + path, body, query, { request: req }); - return this.normalizeResponse(result, res); - } catch (err) { - return this.handleError(err, res); - } - } - - // Storage - @All('storage*') - async storage(@Req() req: any, @Res() res: any) { - try { - let path = req.params[0] || ''; - if (req.url.includes('/storage')) { - path = req.url.substring(req.url.indexOf('/storage') + 8).split('?')[0]; - } - - // Handle File for NestJS (Express/Fastify) - const file = req.file || req.files?.file; - - const result = await this.service.dispatcher.handleStorage(path, req.method, file, { request: req }); - return this.normalizeResponse(result, res); - } catch (err) { - return this.handleError(err, res); - } - } - - // Catch-all: delegate remaining routes to dispatcher.dispatch() - // Handles packages, analytics, automation, i18n, ui, openapi, - // custom API endpoints, and any future routes. - @All('*') - async catchAll(@Req() req: any, @Res() res: any, @Body() body: any, @Query() query: any) { - try { - // Extract the sub-path after /api - const path = req.url.split('/api')[1]?.split('?')[0] || ''; - const method = req.method; - const payload = (method === 'POST' || method === 'PUT' || method === 'PATCH') ? body : undefined; - const result = await this.service.dispatcher.dispatch(method, path, payload, query, { request: req }); - return this.normalizeResponse(result, res); - } catch (err) { - return this.handleError(err, res); - } - } -} - -// --- Discovery Controller --- - -@Controller('.well-known') -export class DiscoveryController { - @Get('objectstack') - discover(@Res() res: any) { - return res.redirect('/api/discovery'); - } -} - -// --- Module --- - -@Global() -@Module({}) -export class ObjectStackModule { - static forRoot(kernel: ObjectKernel): DynamicModule { - const kernelProvider: Provider = { - provide: OBJECT_KERNEL, - useValue: kernel, - }; - - return { - module: ObjectStackModule, - controllers: [ObjectStackController, DiscoveryController], - providers: [kernelProvider, ObjectStackService], - exports: [kernelProvider, ObjectStackService], - }; - } -} diff --git a/packages/adapters/nestjs/src/metadata-api.test.ts b/packages/adapters/nestjs/src/metadata-api.test.ts deleted file mode 100644 index ce1440c2a7..0000000000 --- a/packages/adapters/nestjs/src/metadata-api.test.ts +++ /dev/null @@ -1,713 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -/** - * @objectstack/nestjs — Comprehensive Metadata API Integration Tests - * - * Validates that the NestJS adapter correctly routes ALL metadata API operations - * defined by the @objectstack/metadata package through the HttpDispatcher. - * - * Covers: CRUD, Query, Bulk, Overlay, Import/Export, Validation, Type Registry, Dependencies - */ - -import { describe, it, expect, vi, beforeEach } from 'vitest'; - -// Mock NestJS decorators as no-ops -vi.mock('@nestjs/common', () => { - const classDecorator = () => (target: any) => target; - const methodDecorator = () => (_target: any, _key: string, descriptor: PropertyDescriptor) => descriptor; - const paramDecorator = () => () => (_target: any, _key: string, _index: number) => {}; - return { - Module: classDecorator, - Global: () => (target: any) => target, - Controller: (_prefix?: string) => (target: any) => target, - Injectable: () => (target: any) => target, - Inject: (_token: any) => (_target: any, _key: string | undefined, _index: number) => {}, - DynamicModule: class {}, - Post: methodDecorator, - Get: methodDecorator, - All: methodDecorator, - Body: paramDecorator, - Query: paramDecorator, - Req: paramDecorator, - Res: paramDecorator, - createParamDecorator: (_fn: any) => () => (_target: any, _key: string, _index: number) => {}, - ExecutionContext: class {}, - Provider: class {}, - }; -}); - -import { - ObjectStackService, - ObjectStackController, -} from './index.js'; - -// --- Helpers --- - -function createMockKernel() { - return { id: 'test-kernel' } as any; -} - -function createMockRes() { - const res: any = { - _status: 200, - _body: null, - _headers: {} as Record, - _redirectUrl: null as string | null, - status(code: number) { res._status = code; return res; }, - json(body: any) { res._body = body; return res; }, - send(body: any) { res._body = body; return res; }, - setHeader(k: string, v: string) { res._headers[k] = v; return res; }, - redirect(url: string) { res._redirectUrl = url; return res; }, - }; - return res; -} - -describe('NestJS Metadata API Integration Tests', () => { - let controller: ObjectStackController; - let service: ObjectStackService; - let res: ReturnType; - - beforeEach(() => { - const kernel = createMockKernel(); - service = new ObjectStackService(kernel); - controller = new ObjectStackController(service); - res = createMockRes(); - }); - - // ========================================== - // CRUD Operations - // ========================================== - - describe('CRUD Operations', () => { - describe('GET /api/meta/objects — List all objects', () => { - it('dispatches to dispatch with correct path', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: [ - { name: 'account', label: 'Account' }, - { name: 'contact', label: 'Contact' }, - ], - }, - status: 200, - }, - }); - - const req = { params: {}, url: '/api/meta/objects', method: 'GET' }; - await controller.metadata(req, res, undefined); - - expect(res._status).toBe(200); - expect(res._body.data).toHaveLength(2); - expect(service.dispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/objects', - undefined, - undefined, - { request: req }, - ); - }); - }); - - describe('GET /api/meta/objects/account — Get single object', () => { - it('dispatches with item-level path', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { type: 'object', name: 'account', definition: { label: 'Account' } }, - }, - status: 200, - }, - }); - - const req = { params: {}, url: '/api/meta/objects/account', method: 'GET' }; - await controller.metadata(req, res, undefined); - - expect(res._body.data.name).toBe('account'); - expect(service.dispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/objects/account', - undefined, - undefined, - { request: req }, - ); - }); - }); - - describe('POST /api/meta/objects — Register metadata', () => { - it('dispatches POST with body', async () => { - const body = { type: 'object', name: 'project_task', data: { label: 'Task' } }; - - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { body: { success: true }, status: 201 }, - }); - - const req = { params: {}, url: '/api/meta/objects', method: 'POST', body }; - await controller.metadata(req, res, body); - - expect(res._status).toBe(201); - expect(service.dispatcher.dispatch).toHaveBeenCalledWith( - 'POST', - '/meta/objects', - body, - undefined, - { request: req }, - ); - }); - }); - - describe('PUT /api/meta/objects/account — Update metadata', () => { - it('dispatches PUT with body', async () => { - const body = { label: 'Updated Account' }; - - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { body: { success: true }, status: 200 }, - }); - - const req = { params: {}, url: '/api/meta/objects/account', method: 'PUT', body }; - await controller.metadata(req, res, body); - - expect(res._status).toBe(200); - }); - }); - - describe('DELETE /api/meta/objects/old_entity — Delete metadata', () => { - it('dispatches DELETE', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: { type: 'object', name: 'old_entity' } }, - status: 200, - }, - }); - - const req = { params: {}, url: '/api/meta/objects/old_entity', method: 'DELETE' }; - await controller.metadata(req, res, undefined); - - expect(res._body.data.name).toBe('old_entity'); - }); - }); - - describe('Multiple metadata types', () => { - it('dispatches for views', async () => { - const req = { params: {}, url: '/api/meta/views', method: 'GET' }; - await controller.metadata(req, res, undefined); - expect(service.dispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/views', - undefined, - undefined, - { request: req }, - ); - }); - - it('dispatches for flows', async () => { - const req = { params: {}, url: '/api/meta/flows', method: 'GET' }; - await controller.metadata(req, res, undefined); - expect(service.dispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/flows', - undefined, - undefined, - { request: req }, - ); - }); - - it('dispatches for agents', async () => { - const req = { params: {}, url: '/api/meta/agents', method: 'GET' }; - await controller.metadata(req, res, undefined); - expect(service.dispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/agents', - undefined, - undefined, - { request: req }, - ); - }); - }); - }); - - // ========================================== - // Query / Search - // ========================================== - - describe('Query / Search', () => { - describe('POST /api/meta/query — Advanced search', () => { - it('dispatches query with payload', async () => { - const body = { - types: ['object', 'view'], - search: 'account', - page: 1, - pageSize: 25, - }; - - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { - items: [{ type: 'object', name: 'account' }], - total: 1, - page: 1, - pageSize: 25, - }, - }, - status: 200, - }, - }); - - const req = { params: {}, url: '/api/meta/query', method: 'POST', body }; - await controller.metadata(req, res, body); - - expect(res._body.data.items).toHaveLength(1); - expect(res._body.data.total).toBe(1); - }); - }); - }); - - // ========================================== - // Bulk Operations - // ========================================== - - describe('Bulk Operations', () => { - describe('POST /api/meta/bulk/register — Bulk register', () => { - it('dispatches bulk register', async () => { - const body = { - items: [ - { type: 'object', name: 'customer', data: {} }, - { type: 'view', name: 'customer_list', data: {} }, - ], - }; - - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: { total: 2, succeeded: 2, failed: 0 } }, - status: 200, - }, - }); - - const req = { params: {}, url: '/api/meta/bulk/register', method: 'POST', body }; - await controller.metadata(req, res, body); - - expect(res._body.data.succeeded).toBe(2); - }); - }); - - describe('POST /api/meta/bulk/unregister — Bulk unregister', () => { - it('dispatches bulk unregister', async () => { - const body = { - items: [{ type: 'object', name: 'old' }], - }; - - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: { total: 1, succeeded: 1, failed: 0 } }, - status: 200, - }, - }); - - const req = { params: {}, url: '/api/meta/bulk/unregister', method: 'POST', body }; - await controller.metadata(req, res, body); - - expect(res._body.data.succeeded).toBe(1); - }); - }); - - describe('Bulk with partial failures', () => { - it('returns error details', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { - total: 3, - succeeded: 2, - failed: 1, - errors: [{ type: 'object', name: 'bad', error: 'Validation failed' }], - }, - }, - status: 200, - }, - }); - - const body = { - items: [ - { type: 'object', name: 'good', data: {} }, - { type: 'object', name: 'good2', data: {} }, - { type: 'object', name: 'bad', data: {} }, - ], - continueOnError: true, - }; - const req = { params: {}, url: '/api/meta/bulk/register', method: 'POST', body }; - await controller.metadata(req, res, body); - - expect(res._body.data.failed).toBe(1); - expect(res._body.data.errors[0].name).toBe('bad'); - }); - }); - }); - - // ========================================== - // Overlay / Customization - // ========================================== - - describe('Overlay / Customization', () => { - describe('GET /api/meta/objects/account/overlay — Get overlay', () => { - it('dispatches overlay retrieval', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { - id: 'overlay-001', - baseType: 'object', - baseName: 'account', - scope: 'platform', - }, - }, - status: 200, - }, - }); - - const req = { params: {}, url: '/api/meta/objects/account/overlay', method: 'GET' }; - await controller.metadata(req, res, undefined); - - expect(res._body.data.scope).toBe('platform'); - }); - }); - - describe('PUT /api/meta/objects/account/overlay — Save overlay', () => { - it('dispatches overlay save', async () => { - const body = { - id: 'overlay-002', - baseType: 'object', - baseName: 'account', - patch: { fields: { status: { label: 'Custom' } } }, - }; - - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { body: { success: true }, status: 200 }, - }); - - const req = { params: {}, url: '/api/meta/objects/account/overlay', method: 'PUT', body }; - await controller.metadata(req, res, body); - - expect(res._status).toBe(200); - }); - }); - - describe('GET /api/meta/objects/account/effective — Get effective', () => { - it('dispatches effective metadata retrieval', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { name: 'account', fields: { status: { label: 'Custom Status' } } }, - }, - status: 200, - }, - }); - - const req = { params: {}, url: '/api/meta/objects/account/effective', method: 'GET' }; - await controller.metadata(req, res, undefined); - - expect(res._body.data.fields.status.label).toBe('Custom Status'); - }); - }); - }); - - // ========================================== - // Import / Export - // ========================================== - - describe('Import / Export', () => { - describe('POST /api/meta/export — Export metadata', () => { - it('dispatches export request', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: { version: '1.0', objects: {} } }, - status: 200, - }, - }); - - const body = { types: ['object'], format: 'json' }; - const req = { params: {}, url: '/api/meta/export', method: 'POST', body }; - await controller.metadata(req, res, body); - - expect(res._body.data.version).toBe('1.0'); - }); - }); - - describe('POST /api/meta/import — Import metadata', () => { - it('dispatches import request', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: { total: 3, imported: 3, skipped: 0, failed: 0 } }, - status: 200, - }, - }); - - const body = { data: { objects: { customer: {} } }, conflictResolution: 'merge' }; - const req = { params: {}, url: '/api/meta/import', method: 'POST', body }; - await controller.metadata(req, res, body); - - expect(res._body.data.imported).toBe(3); - }); - }); - - describe('POST /api/meta/import — Dry run', () => { - it('returns preview without saving', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: { total: 2, imported: 0, skipped: 0, failed: 0 } }, - status: 200, - }, - }); - - const body = { data: {}, dryRun: true }; - const req = { params: {}, url: '/api/meta/import', method: 'POST', body }; - await controller.metadata(req, res, body); - - expect(res._body.data.total).toBe(2); - }); - }); - }); - - // ========================================== - // Validation - // ========================================== - - describe('Validation', () => { - describe('POST /api/meta/validate — Validate metadata', () => { - it('dispatches validation for valid payload', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: { valid: true } }, - status: 200, - }, - }); - - const body = { type: 'object', data: { name: 'test', fields: {} } }; - const req = { params: {}, url: '/api/meta/validate', method: 'POST', body }; - await controller.metadata(req, res, body); - - expect(res._body.data.valid).toBe(true); - }); - - it('returns errors for invalid metadata', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { - valid: false, - errors: [{ path: 'name', message: 'Required', code: 'required' }], - }, - }, - status: 200, - }, - }); - - const body = { type: 'object', data: {} }; - const req = { params: {}, url: '/api/meta/validate', method: 'POST', body }; - await controller.metadata(req, res, body); - - expect(res._body.data.valid).toBe(false); - expect(res._body.data.errors).toHaveLength(1); - }); - }); - }); - - // ========================================== - // Type Registry - // ========================================== - - describe('Type Registry', () => { - describe('GET /api/meta/types — List types', () => { - it('returns all types', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: ['object', 'view', 'flow', 'agent'] }, - status: 200, - }, - }); - - const req = { params: {}, url: '/api/meta/types', method: 'GET' }; - await controller.metadata(req, res, undefined); - - expect(res._body.data).toContain('object'); - expect(res._body.data).toContain('agent'); - }); - }); - - describe('GET /api/meta/types/object — Get type info', () => { - it('returns type metadata', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { - type: 'object', - label: 'Object', - filePatterns: ['**/*.object.ts'], - supportsOverlay: true, - domain: 'data', - }, - }, - status: 200, - }, - }); - - const req = { params: {}, url: '/api/meta/types/object', method: 'GET' }; - await controller.metadata(req, res, undefined); - - expect(res._body.data.domain).toBe('data'); - expect(res._body.data.supportsOverlay).toBe(true); - }); - }); - }); - - // ========================================== - // Dependency Tracking - // ========================================== - - describe('Dependency Tracking', () => { - describe('GET /api/meta/objects/account/dependencies', () => { - it('returns dependencies', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: [{ - sourceType: 'object', - sourceName: 'account', - targetType: 'object', - targetName: 'organization', - kind: 'reference', - }], - }, - status: 200, - }, - }); - - const req = { params: {}, url: '/api/meta/objects/account/dependencies', method: 'GET' }; - await controller.metadata(req, res, undefined); - - expect(res._body.data).toHaveLength(1); - expect(res._body.data[0].kind).toBe('reference'); - }); - }); - - describe('GET /api/meta/objects/account/dependents', () => { - it('returns dependents', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: [ - { sourceType: 'view', sourceName: 'account_list', targetType: 'object', targetName: 'account', kind: 'reference' }, - { sourceType: 'flow', sourceName: 'new_account', targetType: 'object', targetName: 'account', kind: 'triggers' }, - ], - }, - status: 200, - }, - }); - - const req = { params: {}, url: '/api/meta/objects/account/dependents', method: 'GET' }; - await controller.metadata(req, res, undefined); - - expect(res._body.data).toHaveLength(2); - }); - }); - }); - - // ========================================== - // Error Handling - // ========================================== - - describe('Error Handling', () => { - it('returns 404 when metadata not found', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ handled: false }); - - const req = { params: {}, url: '/api/meta/objects/nonexistent', method: 'GET' }; - await controller.metadata(req, res, undefined); - - expect(res._status).toBe(404); - expect(res._body.success).toBe(false); - }); - - it('returns 500 on dispatcher exception', async () => { - (service.dispatcher.dispatch as any).mockRejectedValueOnce(new Error('Internal error')); - - const req = { params: {}, url: '/api/meta/objects', method: 'GET' }; - await controller.metadata(req, res, undefined); - - expect(res._status).toBe(500); - expect(res._body.error.message).toBe('Internal error'); - }); - - it('returns custom status code from error', async () => { - (service.dispatcher.dispatch as any).mockRejectedValueOnce( - Object.assign(new Error('Forbidden'), { statusCode: 403 }), - ); - - const req = { params: {}, url: '/api/meta/objects', method: 'GET' }; - await controller.metadata(req, res, undefined); - - expect(res._status).toBe(403); - }); - }); - - // ========================================== - // Path Parsing - // ========================================== - - describe('Path Parsing', () => { - it('correctly extracts nested paths', async () => { - const req = { params: {}, url: '/api/meta/objects/account/fields/name', method: 'GET' }; - await controller.metadata(req, res, undefined); - - expect(service.dispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/objects/account/fields/name', - undefined, - undefined, - { request: req }, - ); - }); - - it('correctly extracts path with query string', async () => { - const req = { params: {}, url: '/api/meta/objects?scope=platform', method: 'GET' }; - await controller.metadata(req, res, undefined); - - expect(service.dispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/objects', - undefined, - undefined, - { request: req }, - ); - }); - }); -}); diff --git a/packages/adapters/nestjs/src/nestjs.test.ts b/packages/adapters/nestjs/src/nestjs.test.ts deleted file mode 100644 index 2689ce7208..0000000000 --- a/packages/adapters/nestjs/src/nestjs.test.ts +++ /dev/null @@ -1,453 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { describe, it, expect, vi, beforeEach } from 'vitest'; - -// Mock NestJS decorators as no-ops -vi.mock('@nestjs/common', () => { - const classDecorator = () => (target: any) => target; - const methodDecorator = () => (_target: any, _key: string, descriptor: PropertyDescriptor) => descriptor; - const paramDecorator = () => () => (_target: any, _key: string, _index: number) => {}; - return { - Module: classDecorator, - Global: () => (target: any) => target, - Controller: (_prefix?: string) => (target: any) => target, - Injectable: () => (target: any) => target, - Inject: (_token: any) => (_target: any, _key: string | undefined, _index: number) => {}, - DynamicModule: class {}, - Post: methodDecorator, - Get: methodDecorator, - All: methodDecorator, - Body: paramDecorator, - Query: paramDecorator, - Req: paramDecorator, - Res: paramDecorator, - createParamDecorator: (_fn: any) => () => (_target: any, _key: string, _index: number) => {}, - ExecutionContext: class {}, - Provider: class {}, - }; -}); - -import { - OBJECT_KERNEL, - ObjectStackModule, - ObjectStackService, - ObjectStackController, - DiscoveryController, -} from './index.js'; - -// --- Helpers --- - -function createMockKernel() { - return { id: 'test-kernel' } as any; -} - -function createMockRes() { - const res: any = { - _status: 200, - _body: null, - _headers: {} as Record, - _redirectUrl: null as string | null, - status(code: number) { res._status = code; return res; }, - json(body: any) { res._body = body; return res; }, - send(body: any) { res._body = body; return res; }, - setHeader(k: string, v: string) { res._headers[k] = v; return res; }, - redirect(url: string) { res._redirectUrl = url; return res; }, - }; - return res; -} - -// --- Tests --- - -describe('OBJECT_KERNEL constant', () => { - it('is exported as a string token', () => { - expect(OBJECT_KERNEL).toBe('OBJECT_KERNEL'); - }); -}); - -describe('ObjectStackModule', () => { - it('forRoot returns a DynamicModule with correct shape', () => { - const kernel = createMockKernel(); - const mod = ObjectStackModule.forRoot(kernel); - - expect(mod).toBeDefined(); - expect(mod.module).toBe(ObjectStackModule); - expect(mod.controllers).toContain(ObjectStackController); - expect(mod.controllers).toContain(DiscoveryController); - expect(mod.providers).toHaveLength(2); - expect(mod.exports).toHaveLength(2); - }); - - it('forRoot provides the kernel under OBJECT_KERNEL token', () => { - const kernel = createMockKernel(); - const mod = ObjectStackModule.forRoot(kernel); - - const kernelProvider = (mod.providers as any[])?.find( - (p: any) => p.provide === OBJECT_KERNEL, - ); - expect(kernelProvider).toBeDefined(); - expect(kernelProvider.useValue).toBe(kernel); - }); - - it('forRoot exports ObjectStackService', () => { - const kernel = createMockKernel(); - const mod = ObjectStackModule.forRoot(kernel); - - expect(mod.exports).toContain(ObjectStackService); - }); -}); - -describe('ObjectStackService', () => { - let service: ObjectStackService; - let kernel: any; - - beforeEach(() => { - kernel = createMockKernel(); - service = new ObjectStackService(kernel); - }); - - it('creates an HttpDispatcher on construction', () => { - expect(service.dispatcher).toBeDefined(); - }); - - it('getKernel returns the injected kernel', () => { - expect(service.getKernel()).toBe(kernel); - }); -}); - -describe('ObjectStackController', () => { - let controller: ObjectStackController; - let service: ObjectStackService; - let res: ReturnType; - - beforeEach(() => { - const kernel = createMockKernel(); - service = new ObjectStackService(kernel); - controller = new ObjectStackController(service); - res = createMockRes(); - }); - - it('has all expected route handler methods', () => { - expect(typeof controller.discovery).toBe('function'); - expect(typeof controller.graphql).toBe('function'); - expect(typeof controller.auth).toBe('function'); - expect(typeof controller.metadata).toBe('function'); - expect(typeof controller.data).toBe('function'); - expect(typeof controller.storage).toBe('function'); - }); - - describe('discovery()', () => { - it('returns discovery info from the dispatcher', async () => { - const result = await controller.discovery(); - expect(result).toEqual({ data: { version: '1.0' } }); - expect(service.dispatcher.getDiscoveryInfo).toHaveBeenCalledWith('/api'); - }); - }); - - describe('graphql()', () => { - it('dispatches to handleGraphQL and returns json', async () => { - const req = { headers: {} }; - const body = { query: '{ objects { name } }' }; - - await controller.graphql(body, req, res); - - expect(service.dispatcher.handleGraphQL).toHaveBeenCalledWith(body, { request: req }); - expect(res._body).toEqual({ data: {} }); - }); - - it('handles errors from handleGraphQL', async () => { - (service.dispatcher.handleGraphQL as any).mockRejectedValueOnce( - Object.assign(new Error('GQL Error'), { statusCode: 400 }), - ); - - await controller.graphql({}, {}, res); - - expect(res._status).toBe(400); - expect(res._body.success).toBe(false); - expect(res._body.error.message).toBe('GQL Error'); - }); - }); - - describe('auth()', () => { - it('dispatches to handleAuth with extracted path', async () => { - const req = { params: { 0: 'login' }, url: '/api/auth/login', method: 'POST' }; - const body = { username: 'admin' }; - - await controller.auth(req, res, body); - - expect(service.dispatcher.handleAuth).toHaveBeenCalledWith( - 'login', 'POST', body, { request: req, response: res }, - ); - }); - - it('falls back to URL parsing for path extraction', async () => { - const req = { params: {}, url: '/api/auth/callback?code=abc', method: 'GET' }; - - await controller.auth(req, res, {}); - - expect(service.dispatcher.handleAuth).toHaveBeenCalledWith( - 'callback', 'GET', {}, { request: req, response: res }, - ); - }); - }); - - describe('auth() via AuthPlugin service', () => { - it('uses kernel.getService("auth") when available', async () => { - const mockHandleRequest = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ user: { id: '1' } }), { - status: 200, - headers: new Headers({ 'Content-Type': 'application/json' }), - }), - ); - const kernelWithAuth = { - ...createMockKernel(), - getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }), - }; - const svc = new ObjectStackService(kernelWithAuth); - const ctrl = new ObjectStackController(svc); - const r = createMockRes(); - const req = { - params: { 0: 'sign-in/email' }, - url: '/api/auth/sign-in/email', - method: 'POST', - protocol: 'http', - get: (key: string) => key === 'host' ? 'localhost' : undefined, - headers: { 'content-type': 'application/json' }, - originalUrl: '/api/auth/sign-in/email', - }; - - await ctrl.auth(req, r, { email: 'a@b.com', password: 'pass' }); - - expect(kernelWithAuth.getService).toHaveBeenCalledWith('auth'); - expect(mockHandleRequest).toHaveBeenCalledWith(expect.any(Request)); - expect(r._status).toBe(200); - }); - - it('falls back to dispatcher when auth service is not available', async () => { - const kernelWithoutAuth = { - ...createMockKernel(), - getService: vi.fn().mockReturnValue(null), - }; - const svc = new ObjectStackService(kernelWithoutAuth); - const ctrl = new ObjectStackController(svc); - const r = createMockRes(); - const req = { params: { 0: 'login' }, url: '/api/auth/login', method: 'POST' }; - - await ctrl.auth(req, r, { email: 'a@b.com' }); - - expect(svc.dispatcher.handleAuth).toHaveBeenCalled(); - }); - - it('forwards GET requests to auth service', async () => { - const mockHandleRequest = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ session: { token: 'abc' } }), { - status: 200, - headers: new Headers({ 'Content-Type': 'application/json' }), - }), - ); - const kernelWithAuth = { - ...createMockKernel(), - getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }), - }; - const svc = new ObjectStackService(kernelWithAuth); - const ctrl = new ObjectStackController(svc); - const r = createMockRes(); - const req = { - params: { 0: 'get-session' }, - url: '/api/auth/get-session', - method: 'GET', - protocol: 'http', - get: (key: string) => key === 'host' ? 'localhost' : undefined, - headers: {}, - originalUrl: '/api/auth/get-session', - }; - - await ctrl.auth(req, r, {}); - - expect(mockHandleRequest).toHaveBeenCalled(); - expect(r._status).toBe(200); - }); - - it('returns error when auth service throws', async () => { - const mockHandleRequest = vi.fn().mockRejectedValue(new Error('Auth failed')); - const kernelWithAuth = { - ...createMockKernel(), - getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }), - }; - const svc = new ObjectStackService(kernelWithAuth); - const ctrl = new ObjectStackController(svc); - const r = createMockRes(); - const req = { - params: { 0: 'sign-in/email' }, - url: '/api/auth/sign-in/email', - method: 'POST', - protocol: 'http', - get: (key: string) => key === 'host' ? 'localhost' : undefined, - headers: {}, - originalUrl: '/api/auth/sign-in/email', - }; - - await ctrl.auth(req, r, {}); - - expect(r._status).toBe(500); - expect(r._body.success).toBe(false); - }); - }); - - describe('metadata()', () => { - it('dispatches to dispatch with extracted path', async () => { - const req = { params: { 0: '' }, url: '/api/meta/objects', method: 'GET' }; - - await controller.metadata(req, res, undefined); - - expect(service.dispatcher.dispatch).toHaveBeenCalledWith( - 'GET', '/meta/objects', undefined, undefined, { request: req }, - ); - }); - }); - - describe('data()', () => { - it('dispatches to dispatch with extracted path', async () => { - const req = { params: { 0: '' }, url: '/api/data/account', method: 'GET' }; - const query = { limit: '10' }; - - await controller.data(req, res, {}, query); - - expect(service.dispatcher.dispatch).toHaveBeenCalledWith( - 'GET', '/data/account', {}, query, { request: req }, - ); - }); - }); - - describe('storage()', () => { - it('dispatches to handleStorage with extracted path', async () => { - const req = { params: { 0: '' }, url: '/api/storage/files/test.png', method: 'GET', file: null, files: {} }; - - await controller.storage(req, res); - - expect(service.dispatcher.handleStorage).toHaveBeenCalledWith( - '/files/test.png', 'GET', undefined, { request: req }, - ); - }); - }); - - describe('normalizeResponse (via handlers)', () => { - it('returns 404 when result is not handled', async () => { - (service.dispatcher.handleAuth as any).mockResolvedValueOnce({ handled: false }); - const req = { params: { 0: 'noop' }, url: '/api/auth/noop', method: 'GET' }; - - await controller.auth(req, res, {}); - - expect(res._status).toBe(404); - expect(res._body.error.code).toBe(404); - }); - - it('sets custom headers from response', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - response: { status: 201, body: { id: 1 }, headers: { 'X-Custom': 'yes' } }, - }); - const req = { params: {}, url: '/api/data/account', method: 'POST' }; - - await controller.data(req, res, {}, {}); - - expect(res._status).toBe(201); - expect(res._headers['X-Custom']).toBe('yes'); - expect(res._body).toEqual({ id: 1 }); - }); - - it('handles redirect results', async () => { - (service.dispatcher.handleAuth as any).mockResolvedValueOnce({ - handled: true, - result: { type: 'redirect', url: 'https://example.com/callback' }, - }); - const req = { params: { 0: 'oauth' }, url: '/api/auth/oauth', method: 'GET' }; - - await controller.auth(req, res, {}); - - expect(res._redirectUrl).toBe('https://example.com/callback'); - }); - - it('handles stream results', async () => { - const pipeFn = vi.fn(); - (service.dispatcher.handleStorage as any).mockResolvedValueOnce({ - handled: true, - result: { - type: 'stream', - stream: { pipe: pipeFn }, - headers: { 'Content-Type': 'application/octet-stream' }, - }, - }); - const req = { params: {}, url: '/api/storage/download', method: 'GET', file: null, files: {} }; - - await controller.storage(req, res); - - expect(pipeFn).toHaveBeenCalledWith(res); - expect(res._headers['Content-Type']).toBe('application/octet-stream'); - }); - - it('handles generic result objects with 200 status', async () => { - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - result: { foo: 'bar' }, - }); - const req = { params: {}, url: '/api/data/x', method: 'GET' }; - - await controller.data(req, res, {}, {}); - - expect(res._status).toBe(200); - expect(res._body).toEqual({ foo: 'bar' }); - }); - - it('handles Response-like objects', async () => { - const mockHeaders = new Map([['content-type', 'text/plain']]); - (service.dispatcher.dispatch as any).mockResolvedValueOnce({ - handled: true, - result: { - status: 203, - headers: mockHeaders, - text: vi.fn().mockResolvedValue('hello world'), - }, - }); - const req = { params: {}, url: '/api/data/x', method: 'GET' }; - - await controller.data(req, res, {}, {}); - - expect(res._status).toBe(203); - expect(res._body).toBe('hello world'); - }); - }); - - describe('handleError', () => { - it('uses statusCode from error if available', async () => { - (service.dispatcher.handleGraphQL as any).mockRejectedValueOnce( - Object.assign(new Error('Forbidden'), { statusCode: 403, details: { reason: 'no access' } }), - ); - - await controller.graphql({}, {}, res); - - expect(res._status).toBe(403); - expect(res._body.error.message).toBe('Forbidden'); - expect(res._body.error.details).toEqual({ reason: 'no access' }); - }); - - it('defaults to 500 when no statusCode', async () => { - (service.dispatcher.handleGraphQL as any).mockRejectedValueOnce(new Error('Unexpected')); - - await controller.graphql({}, {}, res); - - expect(res._status).toBe(500); - expect(res._body.error.code).toBe(500); - }); - }); -}); - -describe('DiscoveryController', () => { - it('redirects to /api/discovery', () => { - const controller = new DiscoveryController(); - const res = createMockRes(); - - controller.discover(res); - - expect(res._redirectUrl).toBe('/api/discovery'); - }); -}); diff --git a/packages/adapters/nestjs/tsconfig.json b/packages/adapters/nestjs/tsconfig.json deleted file mode 100644 index f608c45aa3..0000000000 --- a/packages/adapters/nestjs/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src", - "module": "NodeNext", - "experimentalDecorators": true, - "emitDecoratorMetadata": true, - "moduleResolution": "NodeNext", - "esModuleInterop": true, - "skipLibCheck": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "types": ["node"] - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/packages/adapters/nestjs/vitest.config.ts b/packages/adapters/nestjs/vitest.config.ts deleted file mode 100644 index e2051df210..0000000000 --- a/packages/adapters/nestjs/vitest.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { defineConfig } from 'vitest/config'; -import path from 'node:path'; - -export default defineConfig({ - test: { - globals: true, - environment: 'node', - }, - resolve: { - alias: { - '@objectstack/runtime': path.resolve(__dirname, 'src/__mocks__/runtime.ts'), - }, - }, -}); diff --git a/packages/adapters/nextjs/CHANGELOG.md b/packages/adapters/nextjs/CHANGELOG.md deleted file mode 100644 index 1abbf90324..0000000000 --- a/packages/adapters/nextjs/CHANGELOG.md +++ /dev/null @@ -1,656 +0,0 @@ -# @objectstack/nextjs - -## 11.0.0 - -### Patch Changes - -- Updated dependencies [4d99a5c] -- Updated dependencies [61d441f] -- Updated dependencies [c224e18] -- Updated dependencies [6c4fbd9] -- Updated dependencies [aa33b02] - - @objectstack/runtime@11.0.0 - -## 10.3.0 - -### Patch Changes - -- Updated dependencies [8cf4f7c] -- Updated dependencies [f2063f3] - - @objectstack/runtime@10.3.0 - -## 10.2.0 - -### Patch Changes - -- @objectstack/runtime@10.2.0 - -## 10.1.0 - -### Patch Changes - -- Updated dependencies [ac79f16] -- Updated dependencies [94d2161] - - @objectstack/runtime@10.1.0 - -## 10.0.0 - -### Patch Changes - -- Updated dependencies [e16f2a8] -- Updated dependencies [47d978a] -- Updated dependencies [220ce5b] - - @objectstack/runtime@10.0.0 - -## 9.11.0 - -### Patch Changes - -- Updated dependencies [2afb612] - - @objectstack/runtime@9.11.0 - -## 9.10.0 - -### Patch Changes - -- Updated dependencies [1f88fd9] -- Updated dependencies [e2b5324] - - @objectstack/runtime@9.10.0 - -## 9.9.1 - -### Patch Changes - -- @objectstack/runtime@9.9.1 - -## 9.9.0 - -### Patch Changes - -- Updated dependencies [11af299] -- Updated dependencies [83fd318] -- Updated dependencies [9afeb2d] - - @objectstack/runtime@9.9.0 - -## 9.8.0 - -### Patch Changes - -- @objectstack/runtime@9.8.0 - -## 9.7.0 - -### Patch Changes - -- @objectstack/runtime@9.7.0 - -## 9.6.0 - -### Patch Changes - -- Updated dependencies [71578f2] - - @objectstack/runtime@9.6.0 - -## 9.5.1 - -### Patch Changes - -- @objectstack/runtime@9.5.1 - -## 9.5.0 - -### Patch Changes - -- @objectstack/runtime@9.5.0 - -## 9.4.0 - -### Patch Changes - -- Updated dependencies [0856476] - - @objectstack/runtime@9.4.0 - -## 9.3.0 - -### Patch Changes - -- Updated dependencies [1ada658] - - @objectstack/runtime@9.3.0 - -## 9.2.0 - -### Patch Changes - -- @objectstack/runtime@9.2.0 - -## 9.1.0 - -### Patch Changes - -- @objectstack/runtime@9.1.0 - -## 9.0.1 - -### Patch Changes - -- @objectstack/runtime@9.0.1 - -## 9.0.0 - -### Patch Changes - -- @objectstack/runtime@9.0.0 - -## 8.0.1 - -### Patch Changes - -- @objectstack/runtime@8.0.1 - -## 8.0.0 - -### Patch Changes - -- Updated dependencies [f68be58] -- Updated dependencies [bc0d85b] -- Updated dependencies [2537e28] -- Updated dependencies [0ec7717] -- Updated dependencies [c262301] - - @objectstack/runtime@8.0.0 - -## 7.9.0 - -### Patch Changes - -- Updated dependencies [ac1fc4c] -- Updated dependencies [ac1fc4c] - - @objectstack/runtime@7.9.0 - -## 7.8.0 - -### Patch Changes - -- Updated dependencies [a75823a] -- Updated dependencies [4fbb86a] -- Updated dependencies [e631f1e] -- Updated dependencies [424ab26] - - @objectstack/runtime@7.8.0 - -## 7.7.0 - -### Patch Changes - -- @objectstack/runtime@7.7.0 - -## 7.6.0 - -### Patch Changes - -- 3377e38: fix(release): stop the fixed-group major cascade caused by internal `@objectstack/*` peerDependencies. - - These packages declared workspace peerDependencies on other framework packages - in the changesets `fixed` group. Inside a fixed group, changesets rewrites those - peer ranges on every release and treats a peer-range change as breaking → major, - which cascaded to **all 69 packages → 8.0.0** on _any_ minor changeset. Required - internal peers are now regular `dependencies`; optional ones move to - `devDependencies` (kept for in-workspace tests, no longer a published peer edge). - Releases now bump correctly (patch/minor) instead of a spurious major. - -- Updated dependencies [8e539cc] - - @objectstack/runtime@7.6.0 - -## 7.5.0 - -## 7.4.1 - -## 7.4.0 - -## 7.3.0 - -## 7.2.1 - -## 7.2.0 - -## 7.1.0 - -## 7.0.0 - -### Patch Changes - -- Updated dependencies [dc72172] -- Updated dependencies [3a630b6] - - @objectstack/runtime@7.0.0 - -## 6.9.0 - -## 6.8.1 - -## 6.8.0 - -## 6.7.1 - -## 6.7.0 - -## 6.6.0 - -## 6.5.1 - -## 6.5.0 - -## 6.4.0 - -## 6.3.0 - -## 6.2.0 - -## 6.1.1 - -## 6.1.0 - -## 6.0.0 - -### Patch Changes - -- Updated dependencies [944f187] - - @objectstack/runtime@6.0.0 - -## 5.2.0 - -## 5.1.0 - -## 5.0.0 - -### Patch Changes - -- Updated dependencies [5e9dcb4] -- Updated dependencies [96ad4df] -- Updated dependencies [df18ae9] - - @objectstack/runtime@5.0.0 - -## 4.2.0 - -## 4.1.1 - -## 4.1.0 - -## 4.0.5 - -### Patch Changes - -- 15e0df6: chore: unify all package versions to a single patch release - -## 4.0.4 - -### Patch Changes - -- @objectstack/runtime@4.0.4 - -## 4.0.3 - -### Patch Changes - -- @objectstack/runtime@4.0.3 - -## 4.0.2 - -### Patch Changes - -- 5f659e9: fix ai - - @objectstack/runtime@4.0.2 - -## 4.0.0 - -### Patch Changes - -- f08ffc3: Fix discovery API endpoint routing and protocol consistency. - - **Discovery route standardization:** - - - All adapters (Express, Fastify, Hono, NestJS, Next.js, Nuxt, SvelteKit) now mount the discovery endpoint at `{prefix}/discovery` instead of `{prefix}` root. - - `.well-known/objectstack` redirects now point to `{prefix}/discovery`. - - Client `connect()` fallback URL changed from `/api/v1` to `/api/v1/discovery`. - - Runtime dispatcher handles both `/discovery` (standard) and `/` (legacy) for backward compatibility. - - **Schema & route alignment:** - - - Added `storage` (service: `file-storage`) and `feed` (service: `data`) routes to `DEFAULT_DISPATCHER_ROUTES`. - - Added `feed` and `discovery` fields to `ApiRoutesSchema`. - - Unified `GetDiscoveryResponseSchema` with `DiscoverySchema` as single source of truth. - - Client `getRoute('feed')` fallback updated from `/api/v1/data` to `/api/v1/feed`. - - **Type safety:** - - - Extracted `ApiRouteType` from `ApiRoutes` keys for type-safe client route resolution. - - Removed `as any` type casting in client route access. - -- Updated dependencies [f08ffc3] -- Updated dependencies [e0b0a78] - - @objectstack/runtime@4.0.0 - -## 3.3.1 - -### Patch Changes - -- @objectstack/runtime@3.3.1 - -## 3.3.0 - -### Patch Changes - -- @objectstack/runtime@3.3.0 - -## 3.2.9 - -### Patch Changes - -- @objectstack/runtime@3.2.9 - -## 3.2.8 - -### Patch Changes - -- @objectstack/runtime@3.2.8 - -## 3.2.8 - -### Patch Changes - -- fix: unified catch-all dispatch pattern — `createRouteHandler()` now delegates all non-framework-specific routes to `HttpDispatcher.dispatch()`, automatically supporting packages, analytics, automation, i18n, ui, openapi, custom endpoints, and any future routes -- Only auth (service check), storage (formData), GraphQL (raw result), and discovery (response wrapper) remain as explicit routes - -## 3.2.7 - -### Patch Changes - -- @objectstack/runtime@3.2.7 - -## 3.2.6 - -### Patch Changes - -- @objectstack/runtime@3.2.6 - -## 3.2.5 - -### Patch Changes - -- @objectstack/runtime@3.2.5 - -## 3.2.4 - -### Patch Changes - -- @objectstack/runtime@3.2.4 - -## 3.2.3 - -### Patch Changes - -- @objectstack/runtime@3.2.3 - -## 3.2.2 - -### Patch Changes - -- @objectstack/runtime@3.2.2 - -## 3.2.1 - -### Patch Changes - -- @objectstack/runtime@3.2.1 - -## 3.2.0 - -### Patch Changes - -- @objectstack/runtime@3.2.0 - -## 3.1.1 - -### Patch Changes - -- @objectstack/runtime@3.1.1 - -## 3.1.0 - -### Patch Changes - -- @objectstack/runtime@3.1.0 - -## 3.0.11 - -### Patch Changes - -- @objectstack/runtime@3.0.11 - -## 3.0.10 - -### Patch Changes - -- @objectstack/runtime@3.0.10 - -## 3.0.9 - -### Patch Changes - -- @objectstack/runtime@3.0.9 - -## 3.0.8 - -### Patch Changes - -- @objectstack/runtime@3.0.8 - -## 3.0.7 - -### Patch Changes - -- @objectstack/runtime@3.0.7 - -## 3.0.6 - -### Patch Changes - -- @objectstack/runtime@3.0.6 - -## 3.0.5 - -### Patch Changes - -- @objectstack/runtime@3.0.5 - -## 3.0.4 - -### Patch Changes - -- @objectstack/runtime@3.0.4 - -## 3.0.3 - -### Patch Changes - -- c7267f6: Patch release for maintenance updates and improvements. -- Updated dependencies [c7267f6] - - @objectstack/runtime@3.0.3 - -## 3.0.2 - -### Patch Changes - -- @objectstack/runtime@3.0.2 - -## 3.0.1 - -### Patch Changes - -- @objectstack/runtime@3.0.1 - -## 3.0.0 - -### Major Changes - -- Release v3.0.0 — unified version bump for all ObjectStack packages. - -### Patch Changes - -- Updated dependencies - - @objectstack/runtime@3.0.0 - -## 2.0.7 - -### Patch Changes - -- @objectstack/runtime@2.0.7 - -## 2.0.6 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/runtime@2.0.6 - -## 2.0.5 - -### Patch Changes - -- @objectstack/runtime@2.0.5 - -## 2.0.4 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/runtime@2.0.4 - -## 2.0.3 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/runtime@2.0.3 - -## 2.0.2 - -### Patch Changes - -- @objectstack/runtime@2.0.2 - -## 2.0.1 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/runtime@2.0.1 - -## 2.0.0 - -### Patch Changes - -- @objectstack/runtime@2.0.0 - -## 1.0.12 - -### Patch Changes - -- Updated dependencies - - @objectstack/runtime@1.0.12 - -## 1.0.11 - -### Patch Changes - -- @objectstack/runtime@1.0.11 - -## 1.0.10 - -### Patch Changes - -- @objectstack/runtime@1.0.10 - -## 1.0.9 - -### Patch Changes - -- @objectstack/runtime@1.0.9 - -## 1.0.8 - -### Patch Changes - -- 8f2a3a2: fix: standardize discovery endpoint response to include 'data' wrapper - - @objectstack/runtime@1.0.8 - -## 1.0.7 - -### Patch Changes - -- ebdf787: feat: implement standard service discovery via `/.well-known/objectstack` -- Updated dependencies [ebdf787] - - @objectstack/runtime@1.0.7 - -## 1.0.6 - -### Patch Changes - -- @objectstack/runtime@1.0.6 - -## 1.0.5 - -### Patch Changes - -- b1d24bd: refactor: migrate build system from tsc to tsup for faster builds - - Replaced `tsc` with `tsup` (using esbuild) across all packages - - Added shared `tsup.config.ts` in workspace root - - Added `tsup` as workspace dev dependency - - significantly improved build performance -- Updated dependencies [b1d24bd] -- Updated dependencies [877b864] - - @objectstack/runtime@1.0.5 - -## 1.0.4 - -### Patch Changes - -- @objectstack/runtime@1.0.4 - -## 1.0.3 - -### Patch Changes - -- Updated dependencies [fb2eabd] - - @objectstack/runtime@1.0.3 - -## 1.0.2 - -### Patch Changes - -- 109fc5b: Unified patch release to align all package versions. -- Updated dependencies [a0a6c85] -- Updated dependencies [109fc5b] - - @objectstack/runtime@1.0.2 - -## 1.0.1 - -### Patch Changes - -- Updated dependencies - - @objectstack/runtime@1.0.1 - -## 1.0.0 - -### Major Changes - -- Major version release for ObjectStack Protocol v1.0. - - Stabilized Protocol Definitions - - Enhanced Runtime Plugin Support - - Fixed Type Compliance across Monorepo - -### Patch Changes - -- Updated dependencies - - @objectstack/runtime@1.0.0 diff --git a/packages/adapters/nextjs/LICENSE b/packages/adapters/nextjs/LICENSE deleted file mode 100644 index 16bc23f404..0000000000 --- a/packages/adapters/nextjs/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute - must include a readable copy of the attribution notices - contained within such NOTICE file, excluding those notices - that do not pertain to any part of the Derivative Works, - in at least one of the following places: within a NOTICE - text file distributed as part of the Derivative Works; within - the Source form or documentation, if provided along with - the Derivative Works; or, within a display generated by the - Derivative Works, if and wherever such third-party notices - normally appear. The contents of the NOTICE file are for - informational purposes only and do not modify the License. - You may add Your own attribution notices within Derivative - Works that You distribute, alongside or as an addendum to - the NOTICE text from the Work, provided that such additional - attribution notices cannot be construed as modifying the - License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2026 ObjectStack - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/adapters/nextjs/README.md b/packages/adapters/nextjs/README.md deleted file mode 100644 index 0f15747036..0000000000 --- a/packages/adapters/nextjs/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# @objectstack/nextjs - -> Next.js adapter for ObjectStack — App Router route handlers and server actions for the generated REST API. - -[![npm](https://img.shields.io/npm/v/@objectstack/nextjs.svg)](https://www.npmjs.com/package/@objectstack/nextjs) -[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) - -## Overview - -Bridges Next.js App Router to `HttpDispatcher`. Provides one catch-all route handler that supports `/api/[...objectstack]`, a dedicated `/api/v1/discovery` handler, and typed server actions for CRUD and batch operations from React Server Components. - -## Installation - -```bash -pnpm add @objectstack/nextjs next -``` - -## Quick Start - -```typescript -// app/api/[...objectstack]/route.ts -import { createRouteHandler } from '@objectstack/nextjs'; -import { kernel } from '@/lib/kernel'; - -const handler = createRouteHandler({ kernel }); - -export { handler as GET, handler as POST, handler as PUT, handler as PATCH, handler as DELETE }; -``` - -```typescript -// app/api/v1/discovery/route.ts -import { createDiscoveryHandler } from '@objectstack/nextjs'; -import { kernel } from '@/lib/kernel'; - -export const GET = createDiscoveryHandler({ kernel }); -``` - -### Server actions - -```typescript -// app/actions.ts -'use server'; -import { createServerActions } from '@objectstack/nextjs'; -import { kernel } from '@/lib/kernel'; - -export const { find, findOne, create, update, remove } = createServerActions({ kernel }); -``` - -## Key Exports - -| Export | Kind | Description | -|:---|:---|:---| -| `createRouteHandler(options)` | function | Catch-all App Router handler. | -| `createDiscoveryHandler(options)` | function | `/api/v1/discovery` handler. | -| `createServerActions(options)` | function | Returns typed server actions. | -| `NextAdapterOptions` | interface | `{ kernel, prefix? }`. | -| `ServerActionResult` | type | `{ success: boolean; data?: T; error?: string }` envelope. | - -## Edge vs Node - -- Default runtime is Node.js. Edge runtimes need an edge-compatible driver (ObjectStack Cloud ships `@objectstack/driver-turso` for that case). -- `HttpDispatcher` is isomorphic; the adapter emits `NextResponse` which works in both runtimes. - -## When to use - -- ✅ Next.js App Router projects. -- ✅ React Server Components that need type-safe data mutations via server actions. - -## When not to use - -- ❌ Pages Router projects — use [`@objectstack/express`](../express) with `next-connect` or migrate to App Router. - -## Related Packages - -- [`@objectstack/client-react`](../../client-react) — client-side hooks. -- [`@objectstack/runtime`](../../runtime), [`@objectstack/rest`](../../rest). - -## Links - -- 📖 Docs: -- 📚 API Reference: - -## License - -Apache-2.0 © ObjectStack diff --git a/packages/adapters/nextjs/package.json b/packages/adapters/nextjs/package.json deleted file mode 100644 index a076c768a1..0000000000 --- a/packages/adapters/nextjs/package.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "name": "@objectstack/nextjs", - "version": "11.0.0", - "license": "Apache-2.0", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "scripts": { - "build": "tsup --config ../../../tsup.config.ts", - "test": "vitest run", - "test:watch": "vitest" - }, - "peerDependencies": { - "next": "^16.2.4", - "react": "^19.2.5", - "react-dom": "^19.2.5" - }, - "devDependencies": { - "@objectstack/runtime": "workspace:*", - "next": "^16.2.9", - "react": "^19.2.7", - "react-dom": "^19.2.7", - "typescript": "^6.0.3", - "vitest": "^4.1.9" - }, - "description": "Next.js adapter for ObjectStack — App Router route handlers for the ObjectStack REST API.", - "keywords": [ - "objectstack", - "nextjs", - "adapter", - "app-router", - "rest" - ], - "author": "ObjectStack", - "repository": { - "type": "git", - "url": "https://github.com/objectstack-ai/framework.git", - "directory": "packages/adapters/nextjs" - }, - "homepage": "https://objectstack.ai/docs", - "bugs": "https://github.com/objectstack-ai/framework/issues", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist", - "README.md" - ], - "engines": { - "node": ">=18.0.0" - }, - "dependencies": { - "@objectstack/runtime": "workspace:^" - } -} diff --git a/packages/adapters/nextjs/src/__mocks__/runtime.ts b/packages/adapters/nextjs/src/__mocks__/runtime.ts deleted file mode 100644 index 7961fd466c..0000000000 --- a/packages/adapters/nextjs/src/__mocks__/runtime.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Stub for @objectstack/runtime - replaced by vi.mock in tests -export const HttpDispatcher = class {}; -export type ObjectKernel = any; -export type HttpDispatcherResult = any; diff --git a/packages/adapters/nextjs/src/index.ts b/packages/adapters/nextjs/src/index.ts deleted file mode 100644 index 5403d0296a..0000000000 --- a/packages/adapters/nextjs/src/index.ts +++ /dev/null @@ -1,295 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { NextRequest, NextResponse } from 'next/server'; -import { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime'; - -export interface NextAdapterOptions { - kernel: ObjectKernel; - prefix?: string; -} - -/** - * Auth service interface with handleRequest method - */ -interface AuthService { - handleRequest(request: Request): Promise; -} - -/** - * Creates a route handler for Next.js App Router - * Handles /api/[...objectstack] pattern - * - * Only auth, GraphQL, storage, and discovery need explicit handling. - * All other routes delegate to `HttpDispatcher.dispatch()` automatically. - */ -export function createRouteHandler(options: NextAdapterOptions) { - const dispatcher = new HttpDispatcher(options.kernel); - const error = (msg: string, code: number = 500) => NextResponse.json({ success: false, error: { message: msg, code } }, { status: code }); - - // Helper to convert DispatchResult to NextResponse - const toResponse = (result: HttpDispatcherResult) => { - if (result.handled) { - if (result.response) { - return NextResponse.json(result.response.body, { - status: result.response.status, - headers: result.response.headers - }); - } - if (result.result) { - const res = result.result; - // Redirect - if (res.type === 'redirect' && res.url) { - return NextResponse.redirect(res.url); - } - // Stream - if (res.type === 'stream' && res.stream) { - return new NextResponse(res.stream, { - status: 200, - headers: res.headers - }); - } - // If it's a standard response object (like from another fetch) - // Next.js might handle it, or we return it directly - return res; - } - } - return error('Not Found', 404); - } - - return async function handler(req: NextRequest, { params }: { params: { objectstack: string[] } }) { - const resolvedParams = await Promise.resolve(params); - const segments = resolvedParams.objectstack || []; - const method = req.method; - - // --- 0. Discovery Endpoint --- - if (segments.length === 0 && method === 'GET') { - return NextResponse.json({ data: await dispatcher.getDiscoveryInfo(options.prefix || '/api') }); - } - - if (segments.length === 1 && segments[0] === 'discovery' && method === 'GET') { - return NextResponse.json({ data: await dispatcher.getDiscoveryInfo(options.prefix || '/api') }); - } - - try { - const rawRequest = req; - - // --- 1. Auth (needs auth service integration) --- - if (segments[0] === 'auth') { - // Try AuthPlugin service first (prefer async to support factory-based services) - let authService: AuthService | null = null; - try { - if (typeof options.kernel.getServiceAsync === 'function') { - authService = await options.kernel.getServiceAsync('auth'); - } else if (typeof options.kernel.getService === 'function') { - authService = options.kernel.getService('auth'); - } - } catch { - // Service not registered — fall through to dispatcher - authService = null; - } - - if (authService && typeof authService.handleRequest === 'function') { - const response = await authService.handleRequest(req); - // Convert Web Response to NextResponse - const body = await response.text(); - const headers: Record = {}; - response.headers.forEach((v: string, k: string) => { headers[k] = v; }); - return new NextResponse(body, { status: response.status, headers }); - } - - // Fallback to legacy dispatcher - const subPath = segments.slice(1).join('/'); - const body = method === 'POST' ? await req.json().catch(() => ({})) : {}; - const result = await dispatcher.handleAuth(subPath, method, body, { request: req }); - return toResponse(result); - } - - // --- 2. GraphQL (returns raw result, not HttpDispatcherResult) --- - if (segments[0] === 'graphql' && method === 'POST') { - const body = await req.json(); - const result = await dispatcher.handleGraphQL(body as any, { request: rawRequest } as any); - return NextResponse.json(result); - } - - // --- 3. Storage (needs formData parsing) --- - if (segments[0] === 'storage') { - const subPath = segments.slice(1).join('/'); - - let file: any = undefined; - if (method === 'POST' && subPath === 'upload') { - const formData = await req.formData(); - file = formData.get('file'); - } - - const result = await dispatcher.handleStorage(subPath, method, file, { request: rawRequest }); - return toResponse(result); - } - - // --- 4. Catch-all: delegate to dispatcher.dispatch() --- - // Handles meta, data, packages, analytics, automation, i18n, ui, - // openapi, custom API endpoints, and any future routes. - const path = '/' + segments.join('/'); - - let body: any = undefined; - if (method === 'POST' || method === 'PUT' || method === 'PATCH') { - body = await req.json().catch(() => ({})); - } - - const url = new URL(req.url); - const queryParams: Record = {}; - url.searchParams.forEach((val, key) => queryParams[key] = val); - - const result = await dispatcher.dispatch(method, path, body, queryParams, { request: rawRequest }, options.prefix || '/api'); - return toResponse(result); - - } catch (err: any) { - return error(err.message || 'Internal Server Error', err.statusCode || 500); - } - } -} - -/** - * Creates a discovery handler for Next.js App Router - * Handles /.well-known/objectstack - */ -export function createDiscoveryHandler(options: NextAdapterOptions) { - return async function discoveryHandler(req: NextRequest) { - const apiPath = options.prefix || '/api'; - const url = new URL(req.url); - const targetUrl = new URL(apiPath, url.origin); - return NextResponse.redirect(targetUrl); - } -} - -// ─── Server Actions ────────────────────────────────────────────────────────── - -/** - * Result type for server actions - */ -export interface ServerActionResult { - success: boolean; - data?: T; - error?: { message: string; code: number }; -} - -/** - * Creates type-safe React Server Actions for ObjectStack data operations. - * Each action maps to a dispatcher method and can be called directly from - * React Server Components or client components via `"use server"`. - * - * @example - * ```ts - * // app/actions.ts - * "use server"; - * import { createServerActions } from '@objectstack/nextjs'; - * import { kernel } from '@/lib/kernel'; - * - * const actions = createServerActions({ kernel }); - * - * export async function getAccounts() { - * return actions.query('account'); - * } - * - * export async function createAccount(formData: FormData) { - * return actions.create('account', { - * name: formData.get('name') as string, - * }); - * } - * ``` - */ -export function createServerActions(options: NextAdapterOptions) { - const dispatcher = new HttpDispatcher(options.kernel); - const emptyContext = { request: undefined }; - - return { - /** - * Query records from an object - */ - async query(objectName: string, params?: Record): Promise { - try { - const result = await dispatcher.handleData(`/${objectName}`, 'GET', {}, params || {}, emptyContext); - if (result.handled && result.response) { - return { success: true, data: result.response.body }; - } - return { success: false, error: { message: 'Not found', code: 404 } }; - } catch (err: any) { - return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } }; - } - }, - - /** - * Get a single record by ID - */ - async getById(objectName: string, id: string): Promise { - try { - const result = await dispatcher.handleData(`/${objectName}/${id}`, 'GET', {}, {}, emptyContext); - if (result.handled && result.response) { - return { success: true, data: result.response.body }; - } - return { success: false, error: { message: 'Not found', code: 404 } }; - } catch (err: any) { - return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } }; - } - }, - - /** - * Create a new record - */ - async create(objectName: string, data: Record): Promise { - try { - const result = await dispatcher.handleData(`/${objectName}`, 'POST', data, {}, emptyContext); - if (result.handled && result.response) { - return { success: true, data: result.response.body }; - } - return { success: false, error: { message: 'Create failed', code: 500 } }; - } catch (err: any) { - return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } }; - } - }, - - /** - * Update a record by ID - */ - async update(objectName: string, id: string, data: Record): Promise { - try { - const result = await dispatcher.handleData(`/${objectName}/${id}`, 'PATCH', data, {}, emptyContext); - if (result.handled && result.response) { - return { success: true, data: result.response.body }; - } - return { success: false, error: { message: 'Update failed', code: 500 } }; - } catch (err: any) { - return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } }; - } - }, - - /** - * Delete a record by ID - */ - async remove(objectName: string, id: string): Promise { - try { - const result = await dispatcher.handleData(`/${objectName}/${id}`, 'DELETE', {}, {}, emptyContext); - if (result.handled && result.response) { - return { success: true, data: result.response.body }; - } - return { success: false, error: { message: 'Delete failed', code: 500 } }; - } catch (err: any) { - return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } }; - } - }, - - /** - * Get metadata for objects - */ - async getMetadata(path?: string): Promise { - try { - const result = await dispatcher.handleMetadata(path || '', emptyContext, 'GET'); - if (result.handled && result.response) { - return { success: true, data: result.response.body }; - } - return { success: false, error: { message: 'Not found', code: 404 } }; - } catch (err: any) { - return { success: false, error: { message: err.message || 'Internal Server Error', code: err.statusCode || 500 } }; - } - }, - }; -} diff --git a/packages/adapters/nextjs/src/metadata-api.test.ts b/packages/adapters/nextjs/src/metadata-api.test.ts deleted file mode 100644 index bf69b14837..0000000000 --- a/packages/adapters/nextjs/src/metadata-api.test.ts +++ /dev/null @@ -1,724 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -/** - * @objectstack/nextjs — Comprehensive Metadata API Integration Tests - * - * Validates that the Next.js adapter correctly routes ALL metadata API operations - * defined by the @objectstack/metadata package through the HttpDispatcher. - * - * Covers: CRUD, Query, Bulk, Overlay, Import/Export, Validation, Type Registry, Dependencies - */ - -import { describe, it, expect, vi, beforeEach } from 'vitest'; - -// Mock dispatcher instance -const mockDispatcher = { - getDiscoveryInfo: vi.fn().mockReturnValue({ version: '1.0', endpoints: [] }), - handleAuth: vi.fn().mockResolvedValue({ handled: true, response: { body: { ok: true }, status: 200 } }), - handleGraphQL: vi.fn().mockResolvedValue({ data: {} }), - handleMetadata: vi.fn().mockResolvedValue({ handled: true, response: { body: { success: true }, status: 200 } }), - handleData: vi.fn().mockResolvedValue({ handled: true, response: { body: { records: [] }, status: 200 } }), - handleStorage: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }), - dispatch: vi.fn().mockResolvedValue({ handled: true, response: { body: { success: true }, status: 200 } }), -}; - -vi.mock('@objectstack/runtime', () => { - return { - HttpDispatcher: function HttpDispatcher() { - return mockDispatcher; - }, - }; -}); - -vi.mock('next/server', () => { - class MockNextRequest { - url: string; - method: string; - private _body: any; - - constructor(url: string, init?: any) { - this.url = url; - this.method = init?.method || 'GET'; - this._body = init?.body; - } - - async json() { - return this._body ? JSON.parse(this._body) : {}; - } - - async formData() { - const map = new Map(); - map.set('file', { name: 'test.txt', type: 'text/plain' }); - return { get: (key: string) => map.get(key) }; - } - } - - class MockNextResponse { - body: any; - status: number; - headers: Record; - - constructor(body?: any, init?: any) { - this.body = body; - this.status = init?.status || 200; - this.headers = init?.headers || {}; - } - - async json() { - return typeof this.body === 'string' ? JSON.parse(this.body) : this.body; - } - - static json(body: any, init?: any) { - return new MockNextResponse(body, init); - } - - static redirect(url: string | URL) { - const res = new MockNextResponse(null, { status: 307 }); - (res as any).redirectUrl = typeof url === 'string' ? url : url.toString(); - return res; - } - } - - return { NextRequest: MockNextRequest, NextResponse: MockNextResponse }; -}); - -import { NextRequest } from 'next/server'; -import { createRouteHandler } from './index'; - -const mockKernel = { name: 'test-kernel' } as any; - -function makeReq(url: string, method = 'GET', body?: any) { - const init: any = { method }; - if (body) init.body = JSON.stringify(body); - return new (NextRequest as any)(url, init); -} - -describe('Next.js Metadata API Integration Tests', () => { - let handler: ReturnType; - - beforeEach(() => { - vi.clearAllMocks(); - handler = createRouteHandler({ kernel: mockKernel }); - }); - - // ========================================== - // CRUD Operations - // ========================================== - - describe('CRUD Operations', () => { - describe('GET meta/objects — List all objects', () => { - it('dispatches to dispatch with correct path', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: [ - { name: 'account', label: 'Account' }, - { name: 'contact', label: 'Contact' }, - ], - }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/objects'); - const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } }); - expect(res.status).toBe(200); - expect(res.body.data).toHaveLength(2); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/objects', - undefined, - {}, - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - }); - - describe('GET meta/objects/account — Get single object', () => { - it('dispatches to dispatch with item-level path', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { type: 'object', name: 'account', definition: { label: 'Account' } }, - }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/objects/account'); - const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'account'] } }); - expect(res.status).toBe(200); - expect(res.body.data.name).toBe('account'); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/objects/account', - undefined, - {}, - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - }); - - describe('POST meta/objects — Register metadata', () => { - it('dispatches POST with JSON body', async () => { - const body = { - type: 'object', - name: 'project_task', - data: { label: 'Project Task', fields: {} }, - }; - - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { body: { success: true }, status: 201 }, - }); - - const req = makeReq('http://localhost/api/meta/objects', 'POST', body); - const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } }); - expect(res.status).toBe(201); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'POST', - '/meta/objects', - body, - {}, - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - }); - - describe('PUT meta/objects/account — Update metadata', () => { - it('dispatches PUT with JSON body', async () => { - const body = { label: 'Updated Account' }; - - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { body: { success: true }, status: 200 }, - }); - - const req = makeReq('http://localhost/api/meta/objects/account', 'PUT', body); - const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'account'] } }); - expect(res.status).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'PUT', - '/meta/objects/account', - body, - {}, - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - }); - - describe('DELETE meta/objects/old_entity — Delete metadata', () => { - it('dispatches DELETE to dispatch', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: { type: 'object', name: 'old_entity' } }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/objects/old_entity', 'DELETE'); - const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'old_entity'] } }); - expect(res.status).toBe(200); - expect(res.body.data.name).toBe('old_entity'); - }); - }); - - describe('Multiple metadata types', () => { - it('dispatches for views', async () => { - const req = makeReq('http://localhost/api/meta/views'); - await handler(req, { params: { objectstack: ['meta', 'views'] } }); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/views', - undefined, - {}, - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('dispatches for flows', async () => { - const req = makeReq('http://localhost/api/meta/flows'); - await handler(req, { params: { objectstack: ['meta', 'flows'] } }); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/flows', - undefined, - {}, - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('dispatches for agents', async () => { - const req = makeReq('http://localhost/api/meta/agents'); - await handler(req, { params: { objectstack: ['meta', 'agents'] } }); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/agents', - undefined, - {}, - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - }); - }); - - // ========================================== - // Query / Search - // ========================================== - - describe('Query / Search', () => { - describe('POST meta/query — Advanced search', () => { - it('dispatches query with full filter payload', async () => { - const queryBody = { - types: ['object', 'view'], - search: 'account', - page: 1, - pageSize: 25, - }; - - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { - items: [{ type: 'object', name: 'account' }], - total: 1, - page: 1, - pageSize: 25, - }, - }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/query', 'POST', queryBody); - const res = await handler(req, { params: { objectstack: ['meta', 'query'] } }); - - expect(res.status).toBe(200); - expect(res.body.data.items).toHaveLength(1); - }); - }); - }); - - // ========================================== - // Bulk Operations - // ========================================== - - describe('Bulk Operations', () => { - describe('POST meta/bulk/register — Bulk register', () => { - it('dispatches bulk register', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: { total: 2, succeeded: 2, failed: 0 } }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/bulk/register', 'POST', { - items: [ - { type: 'object', name: 'customer', data: {} }, - { type: 'view', name: 'customer_list', data: {} }, - ], - }); - const res = await handler(req, { params: { objectstack: ['meta', 'bulk', 'register'] } }); - - expect(res.status).toBe(200); - expect(res.body.data.succeeded).toBe(2); - }); - }); - - describe('POST meta/bulk/unregister — Bulk unregister', () => { - it('dispatches bulk unregister', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: { total: 2, succeeded: 2, failed: 0 } }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/bulk/unregister', 'POST', { - items: [{ type: 'object', name: 'old' }, { type: 'view', name: 'old_view' }], - }); - const res = await handler(req, { params: { objectstack: ['meta', 'bulk', 'unregister'] } }); - - expect(res.status).toBe(200); - expect(res.body.data.succeeded).toBe(2); - }); - }); - - describe('Bulk operation with partial failures', () => { - it('returns error details', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { - total: 3, - succeeded: 2, - failed: 1, - errors: [{ type: 'object', name: 'bad', error: 'Validation failed' }], - }, - }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/bulk/register', 'POST', { - items: [ - { type: 'object', name: 'good', data: {} }, - { type: 'object', name: 'good2', data: {} }, - { type: 'object', name: 'bad', data: {} }, - ], - continueOnError: true, - }); - const res = await handler(req, { params: { objectstack: ['meta', 'bulk', 'register'] } }); - - expect(res.body.data.failed).toBe(1); - expect(res.body.data.errors[0].name).toBe('bad'); - }); - }); - }); - - // ========================================== - // Overlay / Customization - // ========================================== - - describe('Overlay / Customization', () => { - describe('GET meta/objects/account/overlay — Get overlay', () => { - it('dispatches overlay retrieval', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { - id: 'overlay-001', - baseType: 'object', - baseName: 'account', - scope: 'platform', - patch: {}, - }, - }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/objects/account/overlay'); - const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'account', 'overlay'] } }); - expect(res.status).toBe(200); - expect(res.body.data.scope).toBe('platform'); - }); - }); - - describe('PUT meta/objects/account/overlay — Save overlay', () => { - it('dispatches overlay save', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { body: { success: true }, status: 200 }, - }); - - const req = makeReq('http://localhost/api/meta/objects/account/overlay', 'PUT', { - id: 'overlay-002', - baseType: 'object', - baseName: 'account', - patch: { fields: { status: { label: 'Custom' } } }, - }); - const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'account', 'overlay'] } }); - expect(res.status).toBe(200); - }); - }); - - describe('GET meta/objects/account/effective — Get effective metadata', () => { - it('dispatches effective metadata retrieval', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { name: 'account', fields: { status: { label: 'Custom Status' } } }, - }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/objects/account/effective'); - const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'account', 'effective'] } }); - expect(res.status).toBe(200); - expect(res.body.data.fields.status.label).toBe('Custom Status'); - }); - }); - }); - - // ========================================== - // Import / Export - // ========================================== - - describe('Import / Export', () => { - describe('POST meta/export — Export metadata', () => { - it('dispatches export request', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: { version: '1.0', objects: {} } }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/export', 'POST', { types: ['object'], format: 'json' }); - const res = await handler(req, { params: { objectstack: ['meta', 'export'] } }); - expect(res.status).toBe(200); - expect(res.body.data.version).toBe('1.0'); - }); - }); - - describe('POST meta/import — Import metadata', () => { - it('dispatches import request', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: { total: 3, imported: 3, skipped: 0, failed: 0 } }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/import', 'POST', { - data: { objects: { a: {} } }, - conflictResolution: 'merge', - }); - const res = await handler(req, { params: { objectstack: ['meta', 'import'] } }); - expect(res.status).toBe(200); - expect(res.body.data.imported).toBe(3); - }); - }); - }); - - // ========================================== - // Validation - // ========================================== - - describe('Validation', () => { - describe('POST meta/validate — Validate metadata', () => { - it('dispatches validation', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: { valid: true } }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/validate', 'POST', { type: 'object', data: {} }); - const res = await handler(req, { params: { objectstack: ['meta', 'validate'] } }); - expect(res.status).toBe(200); - expect(res.body.data.valid).toBe(true); - }); - - it('returns errors for invalid metadata', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { - valid: false, - errors: [{ path: 'name', message: 'Required', code: 'required' }], - }, - }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/validate', 'POST', { type: 'object', data: {} }); - const res = await handler(req, { params: { objectstack: ['meta', 'validate'] } }); - expect(res.body.data.valid).toBe(false); - expect(res.body.data.errors).toHaveLength(1); - }); - }); - }); - - // ========================================== - // Type Registry - // ========================================== - - describe('Type Registry', () => { - describe('GET meta/types — List types', () => { - it('returns all registered types', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { success: true, data: ['object', 'view', 'flow', 'agent'] }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/types'); - const res = await handler(req, { params: { objectstack: ['meta', 'types'] } }); - expect(res.status).toBe(200); - expect(res.body.data).toContain('object'); - }); - }); - - describe('GET meta/types/object — Get type info', () => { - it('returns type metadata', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: { - type: 'object', - label: 'Object', - filePatterns: ['**/*.object.ts'], - supportsOverlay: true, - domain: 'data', - }, - }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/types/object'); - const res = await handler(req, { params: { objectstack: ['meta', 'types', 'object'] } }); - expect(res.status).toBe(200); - expect(res.body.data.domain).toBe('data'); - }); - }); - }); - - // ========================================== - // Dependency Tracking - // ========================================== - - describe('Dependency Tracking', () => { - describe('GET meta/objects/account/dependencies — Get dependencies', () => { - it('returns dependencies', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: [{ - sourceType: 'object', - sourceName: 'account', - targetType: 'object', - targetName: 'organization', - kind: 'reference', - }], - }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/objects/account/dependencies'); - const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'account', 'dependencies'] } }); - expect(res.status).toBe(200); - expect(res.body.data).toHaveLength(1); - }); - }); - - describe('GET meta/objects/account/dependents — Get dependents', () => { - it('returns dependents', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - response: { - body: { - success: true, - data: [ - { sourceType: 'view', sourceName: 'account_list', targetType: 'object', targetName: 'account', kind: 'reference' }, - { sourceType: 'flow', sourceName: 'new_account', targetType: 'object', targetName: 'account', kind: 'triggers' }, - ], - }, - status: 200, - }, - }); - - const req = makeReq('http://localhost/api/meta/objects/account/dependents'); - const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'account', 'dependents'] } }); - expect(res.status).toBe(200); - expect(res.body.data).toHaveLength(2); - }); - }); - }); - - // ========================================== - // Error Handling - // ========================================== - - describe('Error Handling', () => { - it('returns 404 when metadata not found', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ handled: false }); - - const req = makeReq('http://localhost/api/meta/objects/nonexistent'); - const res = await handler(req, { params: { objectstack: ['meta', 'objects', 'nonexistent'] } }); - expect(res.status).toBe(404); - }); - - it('returns 500 on dispatcher exception', async () => { - mockDispatcher.dispatch.mockRejectedValueOnce(new Error('Internal error')); - - const req = makeReq('http://localhost/api/meta/objects'); - const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } }); - expect(res.status).toBe(500); - expect(res.body.error.message).toBe('Internal error'); - }); - - it('returns custom status code from error', async () => { - mockDispatcher.dispatch.mockRejectedValueOnce( - Object.assign(new Error('Forbidden'), { statusCode: 403 }), - ); - - const req = makeReq('http://localhost/api/meta/objects'); - const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } }); - expect(res.status).toBe(403); - }); - }); - - // ========================================== - // Path Parsing - // ========================================== - - describe('Path Parsing', () => { - it('correctly joins nested segments', async () => { - const req = makeReq('http://localhost/api/meta/objects/account/fields/name'); - await handler(req, { params: { objectstack: ['meta', 'objects', 'account', 'fields', 'name'] } }); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/objects/account/fields/name', - undefined, - {}, - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('handles single segment meta path', async () => { - const req = makeReq('http://localhost/api/meta'); - // With just ['meta'], subPath becomes empty after slice(1) - await handler(req, { params: { objectstack: ['meta'] } }); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta', - undefined, - {}, - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - }); -}); diff --git a/packages/adapters/nextjs/src/nextjs.test.ts b/packages/adapters/nextjs/src/nextjs.test.ts deleted file mode 100644 index b7e0a5b236..0000000000 --- a/packages/adapters/nextjs/src/nextjs.test.ts +++ /dev/null @@ -1,589 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { describe, it, expect, vi, beforeEach } from 'vitest'; - -// Mock dispatcher instance -const mockDispatcher = { - getDiscoveryInfo: vi.fn().mockReturnValue({ version: '1.0', endpoints: [] }), - handleAuth: vi.fn().mockResolvedValue({ handled: true, response: { body: { ok: true }, status: 200 } }), - handleGraphQL: vi.fn().mockResolvedValue({ data: {} }), - handleMetadata: vi.fn().mockResolvedValue({ handled: true, response: { body: { objects: [] }, status: 200 } }), - handleData: vi.fn().mockResolvedValue({ handled: true, response: { body: { records: [] }, status: 200 } }), - handleStorage: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }), - dispatch: vi.fn().mockResolvedValue({ handled: true, response: { body: { success: true }, status: 200 } }), -}; - -vi.mock('@objectstack/runtime', () => { - return { - HttpDispatcher: function HttpDispatcher() { - return mockDispatcher; - }, - }; -}); - -vi.mock('next/server', () => { - class MockNextRequest { - url: string; - method: string; - private _body: any; - - constructor(url: string, init?: any) { - this.url = url; - this.method = init?.method || 'GET'; - this._body = init?.body; - } - - async json() { - return this._body ? JSON.parse(this._body) : {}; - } - - async formData() { - const map = new Map(); - map.set('file', { name: 'test.txt', type: 'text/plain' }); - // Return a FormData-like object with get() - return { get: (key: string) => map.get(key) }; - } - } - - class MockNextResponse { - body: any; - status: number; - headers: Record; - - constructor(body?: any, init?: any) { - this.body = body; - this.status = init?.status || 200; - this.headers = init?.headers || {}; - } - - async json() { - return typeof this.body === 'string' ? JSON.parse(this.body) : this.body; - } - - static json(body: any, init?: any) { - const res = new MockNextResponse(body, init); - return res; - } - - static redirect(url: string | URL) { - const res = new MockNextResponse(null, { status: 307 }); - (res as any).redirectUrl = typeof url === 'string' ? url : url.toString(); - return res; - } - } - - return { NextRequest: MockNextRequest, NextResponse: MockNextResponse }; -}); - -import { NextRequest } from 'next/server'; -import { createRouteHandler, createDiscoveryHandler } from './index'; - -const mockKernel = { name: 'test-kernel' } as any; - -function makeReq(url: string, method = 'GET', body?: any) { - const init: any = { method }; - if (body) init.body = JSON.stringify(body); - return new (NextRequest as any)(url, init); -} - -describe('createRouteHandler', () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - describe('Discovery Endpoint', () => { - it('GET with empty segments returns discovery info', async () => { - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api'); - const res = await handler(req, { params: { objectstack: [] } }); - expect(res.status).toBe(200); - expect(res.body).toEqual({ data: { version: '1.0', endpoints: [] } }); - expect(mockDispatcher.getDiscoveryInfo).toHaveBeenCalledWith('/api'); - }); - - it('uses custom prefix for discovery', async () => { - const handler = createRouteHandler({ kernel: mockKernel, prefix: '/v2' }); - const req = makeReq('http://localhost/v2'); - const res = await handler(req, { params: { objectstack: [] } }); - expect(res.status).toBe(200); - expect(mockDispatcher.getDiscoveryInfo).toHaveBeenCalledWith('/v2'); - }); - }); - - describe('Auth Endpoint', () => { - it('POST auth/login calls handleAuth', async () => { - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/auth/login', 'POST', { email: 'a@b.com' }); - const res = await handler(req, { params: { objectstack: ['auth', 'login'] } }); - expect(res.status).toBe(200); - expect(res.body).toEqual({ ok: true }); - expect(mockDispatcher.handleAuth).toHaveBeenCalledWith( - 'login', - 'POST', - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - ); - }); - - it('GET auth/callback calls handleAuth with empty body', async () => { - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/auth/callback', 'GET'); - const res = await handler(req, { params: { objectstack: ['auth', 'callback'] } }); - expect(res.status).toBe(200); - expect(mockDispatcher.handleAuth).toHaveBeenCalledWith( - 'callback', - 'GET', - {}, - expect.objectContaining({ request: expect.anything() }), - ); - }); - - it('returns error on handleAuth exception', async () => { - mockDispatcher.handleAuth.mockRejectedValueOnce( - Object.assign(new Error('Unauthorized'), { statusCode: 401 }), - ); - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/auth/login', 'POST'); - const res = await handler(req, { params: { objectstack: ['auth', 'login'] } }); - expect(res.status).toBe(401); - expect(res.body.success).toBe(false); - expect(res.body.error.message).toBe('Unauthorized'); - }); - }); - - describe('Auth via AuthPlugin service', () => { - it('uses kernel.getService("auth") when available', async () => { - const mockHandleRequest = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ user: { id: '1' } }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }), - ); - const kernelWithAuth = { - ...mockKernel, - getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }), - }; - const handler = createRouteHandler({ kernel: kernelWithAuth }); - const req = makeReq('http://localhost/api/auth/sign-in/email', 'POST', { email: 'a@b.com', password: 'pass' }); - const res = await handler(req, { params: { objectstack: ['auth', 'sign-in', 'email'] } }); - expect(res.status).toBe(200); - expect(kernelWithAuth.getService).toHaveBeenCalledWith('auth'); - expect(mockHandleRequest).toHaveBeenCalled(); - expect(mockDispatcher.handleAuth).not.toHaveBeenCalled(); - }); - - it('falls back to dispatcher.handleAuth when auth service is not available', async () => { - const kernelWithoutAuth = { - ...mockKernel, - getService: vi.fn().mockReturnValue(null), - }; - const handler = createRouteHandler({ kernel: kernelWithoutAuth }); - const req = makeReq('http://localhost/api/auth/login', 'POST', { email: 'a@b.com' }); - const res = await handler(req, { params: { objectstack: ['auth', 'login'] } }); - expect(res.status).toBe(200); - expect(mockDispatcher.handleAuth).toHaveBeenCalled(); - }); - - it('forwards GET requests to auth service', async () => { - const mockHandleRequest = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ session: { token: 'abc' } }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }), - ); - const kernelWithAuth = { - ...mockKernel, - getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }), - }; - const handler = createRouteHandler({ kernel: kernelWithAuth }); - const req = makeReq('http://localhost/api/auth/get-session', 'GET'); - const res = await handler(req, { params: { objectstack: ['auth', 'get-session'] } }); - expect(res.status).toBe(200); - expect(mockHandleRequest).toHaveBeenCalled(); - }); - - it('returns error when auth service throws', async () => { - const mockHandleRequest = vi.fn().mockRejectedValue(new Error('Auth failed')); - const kernelWithAuth = { - ...mockKernel, - getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }), - }; - const handler = createRouteHandler({ kernel: kernelWithAuth }); - const req = makeReq('http://localhost/api/auth/sign-in/email', 'POST', { email: 'a@b.com' }); - const res = await handler(req, { params: { objectstack: ['auth', 'sign-in', 'email'] } }); - expect(res.status).toBe(500); - expect(res.body.success).toBe(false); - }); - - it('uses kernel.getServiceAsync("auth") when available (async factory)', async () => { - const mockHandleRequest = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ user: { id: '2' } }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }), - ); - const kernelWithAsyncAuth = { - ...mockKernel, - getServiceAsync: vi.fn().mockResolvedValue({ handleRequest: mockHandleRequest }), - }; - const handler = createRouteHandler({ kernel: kernelWithAsyncAuth }); - const req = makeReq('http://localhost/api/auth/sign-in/email', 'POST', { email: 'a@b.com', password: 'pass' }); - const res = await handler(req, { params: { objectstack: ['auth', 'sign-in', 'email'] } }); - expect(res.status).toBe(200); - expect(kernelWithAsyncAuth.getServiceAsync).toHaveBeenCalledWith('auth'); - expect(mockHandleRequest).toHaveBeenCalled(); - }); - - it('falls back to dispatcher when getServiceAsync throws', async () => { - const kernelWithFailingAsync = { - ...mockKernel, - getServiceAsync: vi.fn().mockRejectedValue(new Error("Service 'auth' not found")), - }; - const handler = createRouteHandler({ kernel: kernelWithFailingAsync }); - const req = makeReq('http://localhost/api/auth/login', 'POST', { email: 'a@b.com' }); - const res = await handler(req, { params: { objectstack: ['auth', 'login'] } }); - expect(res.status).toBe(200); - expect(mockDispatcher.handleAuth).toHaveBeenCalled(); - }); - }); - - describe('GraphQL Endpoint', () => { - it('POST graphql calls handleGraphQL', async () => { - const handler = createRouteHandler({ kernel: mockKernel }); - const body = { query: '{ objects { name } }' }; - const req = makeReq('http://localhost/api/graphql', 'POST', body); - const res = await handler(req, { params: { objectstack: ['graphql'] } }); - expect(res.status).toBe(200); - expect(res.body).toEqual({ data: {} }); - expect(mockDispatcher.handleGraphQL).toHaveBeenCalledWith( - body, - expect.objectContaining({ request: expect.anything() }), - ); - }); - - it('returns error on handleGraphQL exception', async () => { - mockDispatcher.handleGraphQL.mockRejectedValueOnce(new Error('Parse error')); - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/graphql', 'POST', { query: 'bad' }); - const res = await handler(req, { params: { objectstack: ['graphql'] } }); - expect(res.status).toBe(500); - expect(res.body.success).toBe(false); - expect(res.body.error.message).toBe('Parse error'); - }); - }); - - describe('Metadata Endpoint', () => { - it('GET meta/objects delegates to dispatch()', async () => { - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/meta/objects', 'GET'); - const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } }); - expect(res.status).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/objects', - undefined, - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('PUT meta/objects parses JSON body', async () => { - const handler = createRouteHandler({ kernel: mockKernel }); - const body = { name: 'test_object' }; - const req = makeReq('http://localhost/api/meta/objects', 'PUT', body); - const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } }); - expect(res.status).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'PUT', - '/meta/objects', - body, - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('POST meta/objects parses JSON body', async () => { - const handler = createRouteHandler({ kernel: mockKernel }); - const body = { label: 'My Object' }; - const req = makeReq('http://localhost/api/meta/objects', 'POST', body); - const res = await handler(req, { params: { objectstack: ['meta', 'objects'] } }); - expect(res.status).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'POST', - '/meta/objects', - body, - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - }); - - describe('Data Endpoint', () => { - it('GET data/account delegates to dispatch()', async () => { - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/data/account', 'GET'); - const res = await handler(req, { params: { objectstack: ['data', 'account'] } }); - expect(res.status).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/data/account', - undefined, - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('POST data/account parses JSON body', async () => { - const handler = createRouteHandler({ kernel: mockKernel }); - const body = { name: 'Acme' }; - const req = makeReq('http://localhost/api/data/account', 'POST', body); - const res = await handler(req, { params: { objectstack: ['data', 'account'] } }); - expect(res.status).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'POST', - '/data/account', - body, - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('PATCH data/account parses JSON body', async () => { - const handler = createRouteHandler({ kernel: mockKernel }); - const body = { name: 'Updated' }; - const req = makeReq('http://localhost/api/data/account', 'PATCH', body); - const res = await handler(req, { params: { objectstack: ['data', 'account'] } }); - expect(res.status).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'PATCH', - '/data/account', - body, - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('returns 404 when result is not handled', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ handled: false }); - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/data/missing', 'GET'); - const res = await handler(req, { params: { objectstack: ['data', 'missing'] } }); - expect(res.status).toBe(404); - expect(res.body.success).toBe(false); - }); - }); - - describe('Storage Endpoint', () => { - it('GET storage/files calls handleStorage', async () => { - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/storage/files', 'GET'); - const res = await handler(req, { params: { objectstack: ['storage', 'files'] } }); - expect(res.status).toBe(200); - expect(mockDispatcher.handleStorage).toHaveBeenCalledWith( - 'files', - 'GET', - undefined, - expect.objectContaining({ request: expect.anything() }), - ); - }); - - it('POST storage/upload calls handleStorage with file', async () => { - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/storage/upload', 'POST'); - const res = await handler(req, { params: { objectstack: ['storage', 'upload'] } }); - expect(res.status).toBe(200); - expect(mockDispatcher.handleStorage).toHaveBeenCalledWith( - 'upload', - 'POST', - expect.objectContaining({ name: 'test.txt' }), - expect.objectContaining({ request: expect.anything() }), - ); - }); - }); - - describe('Error Handling', () => { - it('returns 404 for unknown route segments', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ handled: false }); - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/unknown/path', 'GET'); - const res = await handler(req, { params: { objectstack: ['unknown', 'path'] } }); - expect(res.status).toBe(404); - expect(res.body.success).toBe(false); - expect(res.body.error.message).toBe('Not Found'); - }); - - it('returns 500 with default message on generic error', async () => { - mockDispatcher.dispatch.mockRejectedValueOnce(new Error()); - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/data/account', 'GET'); - const res = await handler(req, { params: { objectstack: ['data', 'account'] } }); - expect(res.status).toBe(500); - expect(res.body.error.message).toBe('Internal Server Error'); - }); - - it('uses custom statusCode from error', async () => { - mockDispatcher.dispatch.mockRejectedValueOnce( - Object.assign(new Error('Forbidden'), { statusCode: 403 }), - ); - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/data/account', 'GET'); - const res = await handler(req, { params: { objectstack: ['data', 'account'] } }); - expect(res.status).toBe(403); - expect(res.body.error.message).toBe('Forbidden'); - }); - }); - - describe('toResponse', () => { - it('handles redirect result', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - result: { type: 'redirect', url: 'https://example.com' }, - }); - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/data/redir', 'GET'); - const res = await handler(req, { params: { objectstack: ['data', 'redir'] } }); - expect((res as any).redirectUrl).toBe('https://example.com'); - }); - - it('handles stream result', async () => { - const stream = 'mock-stream'; - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - result: { type: 'stream', stream, headers: { 'Content-Type': 'text/plain' } }, - }); - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/data/stream', 'GET'); - const res = await handler(req, { params: { objectstack: ['data', 'stream'] } }); - expect(res.body).toBe('mock-stream'); - expect(res.status).toBe(200); - }); - - it('returns raw result when handled but no response/redirect/stream', async () => { - const rawResult = { type: 'custom', data: 'test' }; - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - result: rawResult, - }); - const handler = createRouteHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/api/data/custom', 'GET'); - const res = await handler(req, { params: { objectstack: ['data', 'custom'] } }); - expect(res).toBe(rawResult); - }); - }); -}); - -describe('createDiscoveryHandler', () => { - it('redirects to default /api path', async () => { - const handler = createDiscoveryHandler({ kernel: mockKernel }); - const req = makeReq('http://localhost/.well-known/objectstack', 'GET'); - const res = await handler(req); - expect((res as any).redirectUrl).toBe('http://localhost/api'); - }); - - it('redirects to custom prefix', async () => { - const handler = createDiscoveryHandler({ kernel: mockKernel, prefix: '/v2/api' }); - const req = makeReq('http://localhost/.well-known/objectstack', 'GET'); - const res = await handler(req); - expect((res as any).redirectUrl).toBe('http://localhost/v2/api'); - }); -}); - -// --- Server Actions Tests --- -import { createServerActions } from './index'; - -describe('createServerActions', () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - it('query returns data on success', async () => { - const actions = createServerActions({ kernel: mockKernel }); - const result = await actions.query('account'); - expect(result.success).toBe(true); - expect(result.data).toEqual({ records: [] }); - expect(mockDispatcher.handleData).toHaveBeenCalledWith( - '/account', 'GET', {}, {}, - expect.anything(), - ); - }); - - it('query passes params', async () => { - const actions = createServerActions({ kernel: mockKernel }); - await actions.query('account', { limit: 10 }); - expect(mockDispatcher.handleData).toHaveBeenCalledWith( - '/account', 'GET', {}, { limit: 10 }, - expect.anything(), - ); - }); - - it('getById returns single record', async () => { - const actions = createServerActions({ kernel: mockKernel }); - const result = await actions.getById('account', '123'); - expect(result.success).toBe(true); - expect(mockDispatcher.handleData).toHaveBeenCalledWith( - '/account/123', 'GET', {}, {}, - expect.anything(), - ); - }); - - it('create sends data', async () => { - const actions = createServerActions({ kernel: mockKernel }); - const result = await actions.create('account', { name: 'Acme' }); - expect(result.success).toBe(true); - expect(mockDispatcher.handleData).toHaveBeenCalledWith( - '/account', 'POST', { name: 'Acme' }, {}, - expect.anything(), - ); - }); - - it('update sends data with id', async () => { - const actions = createServerActions({ kernel: mockKernel }); - const result = await actions.update('account', '123', { name: 'Updated' }); - expect(result.success).toBe(true); - expect(mockDispatcher.handleData).toHaveBeenCalledWith( - '/account/123', 'PATCH', { name: 'Updated' }, {}, - expect.anything(), - ); - }); - - it('remove deletes record', async () => { - const actions = createServerActions({ kernel: mockKernel }); - const result = await actions.remove('account', '123'); - expect(result.success).toBe(true); - expect(mockDispatcher.handleData).toHaveBeenCalledWith( - '/account/123', 'DELETE', {}, {}, - expect.anything(), - ); - }); - - it('getMetadata returns metadata', async () => { - const actions = createServerActions({ kernel: mockKernel }); - const result = await actions.getMetadata('/objects'); - expect(result.success).toBe(true); - expect(mockDispatcher.handleMetadata).toHaveBeenCalledWith( - '/objects', expect.anything(), 'GET', - ); - }); - - it('returns error on exception', async () => { - mockDispatcher.handleData.mockRejectedValueOnce(new Error('Server down')); - const actions = createServerActions({ kernel: mockKernel }); - const result = await actions.query('account'); - expect(result.success).toBe(false); - expect(result.error?.message).toBe('Server down'); - }); - - it('returns error when not handled', async () => { - mockDispatcher.handleData.mockResolvedValueOnce({ handled: false }); - const actions = createServerActions({ kernel: mockKernel }); - const result = await actions.query('missing'); - expect(result.success).toBe(false); - expect(result.error?.code).toBe(404); - }); -}); diff --git a/packages/adapters/nextjs/tsconfig.json b/packages/adapters/nextjs/tsconfig.json deleted file mode 100644 index e8dfb8f584..0000000000 --- a/packages/adapters/nextjs/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": "../../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "esModuleInterop": true, - "skipLibCheck": true, - "lib": [ - "ES2020", - "DOM", - "DOM.Iterable" - ], - "types": [ - "node" - ] - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "dist" - ] -} diff --git a/packages/adapters/nextjs/vitest.config.ts b/packages/adapters/nextjs/vitest.config.ts deleted file mode 100644 index e2051df210..0000000000 --- a/packages/adapters/nextjs/vitest.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { defineConfig } from 'vitest/config'; -import path from 'node:path'; - -export default defineConfig({ - test: { - globals: true, - environment: 'node', - }, - resolve: { - alias: { - '@objectstack/runtime': path.resolve(__dirname, 'src/__mocks__/runtime.ts'), - }, - }, -}); diff --git a/packages/adapters/nuxt/CHANGELOG.md b/packages/adapters/nuxt/CHANGELOG.md deleted file mode 100644 index 08f8ff9ddd..0000000000 --- a/packages/adapters/nuxt/CHANGELOG.md +++ /dev/null @@ -1,473 +0,0 @@ -# @objectstack/nuxt - -## 11.0.0 - -### Patch Changes - -- Updated dependencies [4d99a5c] -- Updated dependencies [61d441f] -- Updated dependencies [c224e18] -- Updated dependencies [6c4fbd9] -- Updated dependencies [aa33b02] - - @objectstack/runtime@11.0.0 - -## 10.3.0 - -### Patch Changes - -- Updated dependencies [8cf4f7c] -- Updated dependencies [f2063f3] - - @objectstack/runtime@10.3.0 - -## 10.2.0 - -### Patch Changes - -- @objectstack/runtime@10.2.0 - -## 10.1.0 - -### Patch Changes - -- Updated dependencies [ac79f16] -- Updated dependencies [94d2161] - - @objectstack/runtime@10.1.0 - -## 10.0.0 - -### Patch Changes - -- Updated dependencies [e16f2a8] -- Updated dependencies [47d978a] -- Updated dependencies [220ce5b] - - @objectstack/runtime@10.0.0 - -## 9.11.0 - -### Patch Changes - -- Updated dependencies [2afb612] - - @objectstack/runtime@9.11.0 - -## 9.10.0 - -### Patch Changes - -- Updated dependencies [1f88fd9] -- Updated dependencies [e2b5324] - - @objectstack/runtime@9.10.0 - -## 9.9.1 - -### Patch Changes - -- @objectstack/runtime@9.9.1 - -## 9.9.0 - -### Patch Changes - -- Updated dependencies [11af299] -- Updated dependencies [83fd318] -- Updated dependencies [9afeb2d] - - @objectstack/runtime@9.9.0 - -## 9.8.0 - -### Patch Changes - -- @objectstack/runtime@9.8.0 - -## 9.7.0 - -### Patch Changes - -- @objectstack/runtime@9.7.0 - -## 9.6.0 - -### Patch Changes - -- Updated dependencies [71578f2] - - @objectstack/runtime@9.6.0 - -## 9.5.1 - -### Patch Changes - -- @objectstack/runtime@9.5.1 - -## 9.5.0 - -### Patch Changes - -- @objectstack/runtime@9.5.0 - -## 9.4.0 - -### Patch Changes - -- Updated dependencies [0856476] - - @objectstack/runtime@9.4.0 - -## 9.3.0 - -### Patch Changes - -- Updated dependencies [1ada658] - - @objectstack/runtime@9.3.0 - -## 9.2.0 - -### Patch Changes - -- @objectstack/runtime@9.2.0 - -## 9.1.0 - -### Patch Changes - -- @objectstack/runtime@9.1.0 - -## 9.0.1 - -### Patch Changes - -- @objectstack/runtime@9.0.1 - -## 9.0.0 - -### Patch Changes - -- @objectstack/runtime@9.0.0 - -## 8.0.1 - -### Patch Changes - -- @objectstack/runtime@8.0.1 - -## 8.0.0 - -### Patch Changes - -- Updated dependencies [f68be58] -- Updated dependencies [bc0d85b] -- Updated dependencies [2537e28] -- Updated dependencies [0ec7717] -- Updated dependencies [c262301] - - @objectstack/runtime@8.0.0 - -## 7.9.0 - -### Patch Changes - -- Updated dependencies [ac1fc4c] -- Updated dependencies [ac1fc4c] - - @objectstack/runtime@7.9.0 - -## 7.8.0 - -### Patch Changes - -- Updated dependencies [a75823a] -- Updated dependencies [4fbb86a] -- Updated dependencies [e631f1e] -- Updated dependencies [424ab26] - - @objectstack/runtime@7.8.0 - -## 7.7.0 - -### Patch Changes - -- @objectstack/runtime@7.7.0 - -## 7.6.0 - -### Patch Changes - -- 3377e38: fix(release): stop the fixed-group major cascade caused by internal `@objectstack/*` peerDependencies. - - These packages declared workspace peerDependencies on other framework packages - in the changesets `fixed` group. Inside a fixed group, changesets rewrites those - peer ranges on every release and treats a peer-range change as breaking → major, - which cascaded to **all 69 packages → 8.0.0** on _any_ minor changeset. Required - internal peers are now regular `dependencies`; optional ones move to - `devDependencies` (kept for in-workspace tests, no longer a published peer edge). - Releases now bump correctly (patch/minor) instead of a spurious major. - -- Updated dependencies [8e539cc] - - @objectstack/runtime@7.6.0 - -## 7.5.0 - -## 7.4.1 - -## 7.4.0 - -## 7.3.0 - -## 7.2.1 - -## 7.2.0 - -## 7.1.0 - -## 7.0.0 - -### Patch Changes - -- Updated dependencies [dc72172] -- Updated dependencies [3a630b6] - - @objectstack/runtime@7.0.0 - -## 6.9.0 - -## 6.8.1 - -## 6.8.0 - -## 6.7.1 - -## 6.7.0 - -## 6.6.0 - -## 6.5.1 - -## 6.5.0 - -## 6.4.0 - -## 6.3.0 - -## 6.2.0 - -## 6.1.1 - -## 6.1.0 - -## 6.0.0 - -### Patch Changes - -- Updated dependencies [944f187] - - @objectstack/runtime@6.0.0 - -## 5.2.0 - -## 5.1.0 - -## 5.0.0 - -### Patch Changes - -- Updated dependencies [5e9dcb4] -- Updated dependencies [96ad4df] -- Updated dependencies [df18ae9] - - @objectstack/runtime@5.0.0 - -## 4.2.0 - -## 4.1.1 - -## 4.1.0 - -## 4.0.5 - -### Patch Changes - -- 15e0df6: chore: unify all package versions to a single patch release - -## 4.0.4 - -### Patch Changes - -- @objectstack/runtime@4.0.4 - -## 4.0.3 - -### Patch Changes - -- @objectstack/runtime@4.0.3 - -## 4.0.2 - -### Patch Changes - -- @objectstack/runtime@4.0.2 - -## 4.0.0 - -### Patch Changes - -- f08ffc3: Fix discovery API endpoint routing and protocol consistency. - - **Discovery route standardization:** - - - All adapters (Express, Fastify, Hono, NestJS, Next.js, Nuxt, SvelteKit) now mount the discovery endpoint at `{prefix}/discovery` instead of `{prefix}` root. - - `.well-known/objectstack` redirects now point to `{prefix}/discovery`. - - Client `connect()` fallback URL changed from `/api/v1` to `/api/v1/discovery`. - - Runtime dispatcher handles both `/discovery` (standard) and `/` (legacy) for backward compatibility. - - **Schema & route alignment:** - - - Added `storage` (service: `file-storage`) and `feed` (service: `data`) routes to `DEFAULT_DISPATCHER_ROUTES`. - - Added `feed` and `discovery` fields to `ApiRoutesSchema`. - - Unified `GetDiscoveryResponseSchema` with `DiscoverySchema` as single source of truth. - - Client `getRoute('feed')` fallback updated from `/api/v1/data` to `/api/v1/feed`. - - **Type safety:** - - - Extracted `ApiRouteType` from `ApiRoutes` keys for type-safe client route resolution. - - Removed `as any` type casting in client route access. - -- Updated dependencies [f08ffc3] -- Updated dependencies [e0b0a78] - - @objectstack/runtime@4.0.0 - -## 3.3.1 - -### Patch Changes - -- @objectstack/runtime@3.3.1 - -## 3.3.0 - -### Patch Changes - -- @objectstack/runtime@3.3.0 - -## 3.2.9 - -### Patch Changes - -- @objectstack/runtime@3.2.9 - -## 3.2.8 - -### Patch Changes - -- @objectstack/runtime@3.2.8 - -## 3.2.8 - -### Patch Changes - -- fix: unified catch-all dispatch pattern — `createH3Router()` now delegates all non-framework-specific routes to `HttpDispatcher.dispatch()`, automatically supporting packages, analytics, automation, i18n, ui, openapi, custom endpoints, and any future routes -- Only auth (service check), storage (multipart), GraphQL (raw result), and discovery (response wrapper) remain as explicit routes - -## 3.2.7 - -### Patch Changes - -- @objectstack/runtime@3.2.7 - -## 3.2.6 - -### Patch Changes - -- @objectstack/runtime@3.2.6 - -## 3.2.5 - -### Patch Changes - -- @objectstack/runtime@3.2.5 - -## 3.2.4 - -### Patch Changes - -- @objectstack/runtime@3.2.4 - -## 3.2.3 - -### Patch Changes - -- @objectstack/runtime@3.2.3 - -## 3.2.2 - -### Patch Changes - -- @objectstack/runtime@3.2.2 - -## 3.2.1 - -### Patch Changes - -- @objectstack/runtime@3.2.1 - -## 3.2.0 - -### Patch Changes - -- @objectstack/runtime@3.2.0 - -## 3.1.1 - -### Patch Changes - -- @objectstack/runtime@3.1.1 - -## 3.1.0 - -### Patch Changes - -- @objectstack/runtime@3.1.0 - -## 3.0.11 - -### Patch Changes - -- @objectstack/runtime@3.0.11 - -## 3.0.10 - -### Patch Changes - -- @objectstack/runtime@3.0.10 - -## 3.0.9 - -### Patch Changes - -- @objectstack/runtime@3.0.9 - -## 3.0.8 - -### Patch Changes - -- @objectstack/runtime@3.0.8 - -## 3.0.7 - -### Patch Changes - -- @objectstack/runtime@3.0.7 - -## 3.0.6 - -### Patch Changes - -- @objectstack/runtime@3.0.6 - -## 3.0.5 - -### Patch Changes - -- @objectstack/runtime@3.0.5 - -## 3.0.4 - -### Patch Changes - -- @objectstack/runtime@3.0.4 - -## 3.0.3 - -### Patch Changes - -- Updated dependencies [c7267f6] - - @objectstack/runtime@3.0.3 diff --git a/packages/adapters/nuxt/LICENSE b/packages/adapters/nuxt/LICENSE deleted file mode 100644 index 16bc23f404..0000000000 --- a/packages/adapters/nuxt/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute - must include a readable copy of the attribution notices - contained within such NOTICE file, excluding those notices - that do not pertain to any part of the Derivative Works, - in at least one of the following places: within a NOTICE - text file distributed as part of the Derivative Works; within - the Source form or documentation, if provided along with - the Derivative Works; or, within a display generated by the - Derivative Works, if and wherever such third-party notices - normally appear. The contents of the NOTICE file are for - informational purposes only and do not modify the License. - You may add Your own attribution notices within Derivative - Works that You distribute, alongside or as an addendum to - the NOTICE text from the Work, provided that such additional - attribution notices cannot be construed as modifying the - License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2026 ObjectStack - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/adapters/nuxt/README.md b/packages/adapters/nuxt/README.md deleted file mode 100644 index 2ef388e9b3..0000000000 --- a/packages/adapters/nuxt/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# @objectstack/nuxt - -> Nuxt adapter for ObjectStack — Nitro/h3 server routes for the auto-generated REST API. - -[![npm](https://img.shields.io/npm/v/@objectstack/nuxt.svg)](https://www.npmjs.com/package/@objectstack/nuxt) -[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) - -## Overview - -Provides an h3 router factory that exposes all ObjectStack dispatcher routes, mountable inside `server/api/[...].ts` of any Nuxt 3 application (or directly in a standalone Nitro app). - -## Installation - -```bash -pnpm add @objectstack/nuxt -``` - -## Quick Start - -```typescript -// server/api/[...objectstack].ts -import { createH3Router } from '@objectstack/nuxt'; -import { kernel } from '~/server/kernel'; - -export default createH3Router({ kernel, prefix: '/api' }).handler; -``` - -## Key Exports - -| Export | Kind | Description | -|:---|:---|:---| -| `createH3Router(options)` | function | h3 `Router` with dispatcher routes installed. | -| `NuxtAdapterOptions` | interface | `{ kernel, prefix? }`. | - -## Middleware order - -Register Nuxt server middleware that rewrites or compresses responses **before** this router. Do not wrap ObjectStack routes with `defineEventHandler` validators. - -## When to use - -- ✅ Nuxt 3 / Nitro applications. -- ✅ Any h3-based server (used directly via `createH3Router`). - -## When not to use - -- ❌ Nuxt 2 is not supported. - -## Related Packages - -- [`@objectstack/runtime`](../../runtime), [`@objectstack/rest`](../../rest). - -## Links - -- 📖 Docs: -- 📚 API Reference: - -## License - -Apache-2.0 © ObjectStack diff --git a/packages/adapters/nuxt/package.json b/packages/adapters/nuxt/package.json deleted file mode 100644 index dfb131f431..0000000000 --- a/packages/adapters/nuxt/package.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "name": "@objectstack/nuxt", - "version": "11.0.0", - "license": "Apache-2.0", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "require": "./dist/index.js" - } - }, - "scripts": { - "build": "tsup --config ../../../tsup.config.ts", - "test": "vitest run", - "test:watch": "vitest" - }, - "peerDependencies": { - "h3": "^1.15.11" - }, - "devDependencies": { - "@objectstack/runtime": "workspace:*", - "h3": "^1.15.11", - "typescript": "^6.0.3", - "vitest": "^4.1.9" - }, - "description": "Nuxt adapter for ObjectStack — Nitro server routes for the ObjectStack REST API.", - "keywords": [ - "objectstack", - "nuxt", - "adapter", - "nitro", - "rest" - ], - "author": "ObjectStack", - "repository": { - "type": "git", - "url": "https://github.com/objectstack-ai/framework.git", - "directory": "packages/adapters/nuxt" - }, - "homepage": "https://objectstack.ai/docs", - "bugs": "https://github.com/objectstack-ai/framework/issues", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist", - "README.md" - ], - "engines": { - "node": ">=18.0.0" - }, - "dependencies": { - "@objectstack/runtime": "workspace:^" - } -} diff --git a/packages/adapters/nuxt/src/__mocks__/runtime.ts b/packages/adapters/nuxt/src/__mocks__/runtime.ts deleted file mode 100644 index 7961fd466c..0000000000 --- a/packages/adapters/nuxt/src/__mocks__/runtime.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Stub for @objectstack/runtime - replaced by vi.mock in tests -export const HttpDispatcher = class {}; -export type ObjectKernel = any; -export type HttpDispatcherResult = any; diff --git a/packages/adapters/nuxt/src/index.ts b/packages/adapters/nuxt/src/index.ts deleted file mode 100644 index f7ac09e8ca..0000000000 --- a/packages/adapters/nuxt/src/index.ts +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { - createRouter, - defineEventHandler, - getQuery, - readBody, - sendRedirect, - setResponseHeader, - setResponseStatus, - type H3Event, - type Router, -} from 'h3'; -import { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime'; - -export interface NuxtAdapterOptions { - kernel: ObjectKernel; - prefix?: string; -} - -/** - * Auth service interface with handleRequest method - */ -interface AuthService { - handleRequest(request: Request): Promise; -} - -/** - * Creates an h3 router with all ObjectStack route dispatchers. - * Designed for use in Nuxt server routes or standalone h3 apps. - * - * Only routes that need framework-specific handling (auth service, storage - * file upload, GraphQL raw result, discovery wrapper) are registered explicitly. - * All other routes are handled by a catch-all that delegates to - * `HttpDispatcher.dispatch()`, making the adapter automatically support - * new routes added to the dispatcher. - * - * @example - * ```ts - * // server/api/[...].ts - * import { createH3Router } from '@objectstack/nuxt'; - * import { kernel } from '../kernel'; - * - * const router = createH3Router({ kernel }); - * export default defineEventHandler(router.handler); - * ``` - */ -export function createH3Router(options: NuxtAdapterOptions): Router { - const router = createRouter(); - const dispatcher = new HttpDispatcher(options.kernel); - const prefix = options.prefix || '/api'; - - const errorJson = (event: H3Event, message: string, code: number = 500) => { - setResponseStatus(event, code); - return { success: false, error: { message, code } }; - }; - - const toResponse = (event: H3Event, result: HttpDispatcherResult) => { - if (result.handled) { - if (result.response) { - if (result.response.headers) { - Object.entries(result.response.headers).forEach(([k, v]) => - setResponseHeader(event, k, v as string), - ); - } - setResponseStatus(event, result.response.status); - return result.response.body; - } - if (result.result) { - const res = result.result; - if (res.type === 'redirect' && res.url) { - return sendRedirect(event, res.url); - } - if (res.type === 'stream' && res.stream) { - if (res.headers) { - Object.entries(res.headers).forEach(([k, v]) => - setResponseHeader(event, k, v as string), - ); - } - return res.stream; - } - return res; - } - } - return errorJson(event, 'Not Found', 404); - }; - - // ─── Explicit routes (framework-specific handling required) ──────────────── - - // --- Discovery --- - router.get( - prefix, - defineEventHandler(async () => { - return { data: await dispatcher.getDiscoveryInfo(prefix) }; - }), - ); - - router.get( - `${prefix}/discovery`, - defineEventHandler(async () => { - return { data: await dispatcher.getDiscoveryInfo(prefix) }; - }), - ); - - // --- .well-known --- - router.get( - '/.well-known/objectstack', - defineEventHandler((event) => { - return sendRedirect(event, prefix); - }), - ); - - // --- Auth (needs auth service integration) --- - router.use( - `${prefix}/auth/**`, - defineEventHandler(async (event) => { - try { - const urlPath = event.path || event.node.req.url || ''; - const path = urlPath.substring(`${prefix}/auth/`.length).split('?')[0]; - const method = event.method; - - // Try AuthPlugin service first (prefer async to support factory-based services) - let authService: AuthService | null = null; - try { - if (typeof options.kernel.getServiceAsync === 'function') { - authService = await options.kernel.getServiceAsync('auth'); - } else if (typeof options.kernel.getService === 'function') { - authService = options.kernel.getService('auth'); - } - } catch { - // Service not registered — fall through to dispatcher - authService = null; - } - - if (authService && typeof authService.handleRequest === 'function') { - const host = event.node.req.headers.host || 'localhost'; - const protocol = (event.node.req.socket as any)?.encrypted ? 'https' : 'http'; - const url = `${protocol}://${host}${urlPath}`; - const headers = new Headers(); - if (event.node.req.headers) { - Object.entries(event.node.req.headers).forEach(([k, v]) => { - if (typeof v === 'string') headers.set(k, v); - else if (Array.isArray(v)) headers.set(k, v.join(', ')); - }); - } - const init: RequestInit = { method, headers }; - if (method !== 'GET' && method !== 'HEAD') { - const body = await readBody(event); - if (body) { - init.body = typeof body === 'string' ? body : JSON.stringify(body); - if (!headers.has('content-type')) { - headers.set('content-type', 'application/json'); - } - } - } - const webRequest = new Request(url, init); - const response = await authService.handleRequest(webRequest); - setResponseStatus(event, response.status); - response.headers.forEach((v: string, k: string) => setResponseHeader(event, k, v)); - return await response.text(); - } - - // Fallback to dispatcher - const body = method === 'GET' || method === 'HEAD' - ? {} - : await readBody(event).catch(() => ({})); - const result = await dispatcher.handleAuth(path, method, body, { request: event.node.req }); - return toResponse(event, result); - } catch (err: any) { - return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500); - } - }), - ); - - // --- GraphQL (returns raw result, not HttpDispatcherResult) --- - router.post( - `${prefix}/graphql`, - defineEventHandler(async (event) => { - try { - const body = await readBody(event); - const result = await dispatcher.handleGraphQL(body, { request: event.node.req }); - return result; - } catch (err: any) { - return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500); - } - }), - ); - - // --- Storage (needs multipart form parsing) --- - router.use( - `${prefix}/storage/**`, - defineEventHandler(async (event) => { - try { - const urlPath = (event.path || event.node.req.url || '').split('?')[0]; - const subPath = urlPath.substring(`${prefix}/storage`.length); - const method = event.method; - const file = undefined; // File upload requires multipart parsing (e.g., formidable) - const result = await dispatcher.handleStorage(subPath, method, file, { request: event.node.req }); - return toResponse(event, result); - } catch (err: any) { - return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500); - } - }), - ); - - // ─── Catch-all: delegate to dispatcher.dispatch() ───────────────────────── - // Handles meta, data, packages, analytics, automation, i18n, ui, openapi, - // custom API endpoints, and any future routes added to HttpDispatcher. - router.use( - `${prefix}/**`, - defineEventHandler(async (event) => { - try { - const urlPath = (event.path || event.node.req.url || '').split('?')[0]; - const subPath = urlPath.substring(prefix.length); - const method = event.method; - const body = (method === 'POST' || method === 'PUT' || method === 'PATCH') - ? await readBody(event) - : undefined; - const query = getQuery(event); - const result = await dispatcher.dispatch(method, subPath, body, query, { request: event.node.req }, prefix); - return toResponse(event, result); - } catch (err: any) { - return errorJson(event, err.message || 'Internal Server Error', err.statusCode || 500); - } - }), - ); - - return router; -} diff --git a/packages/adapters/nuxt/src/nuxt.test.ts b/packages/adapters/nuxt/src/nuxt.test.ts deleted file mode 100644 index d4efb3b733..0000000000 --- a/packages/adapters/nuxt/src/nuxt.test.ts +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { describe, it, expect, vi, beforeEach } from 'vitest'; - -// Mock dispatcher instance -const mockDispatcher = { - getDiscoveryInfo: vi.fn().mockReturnValue({ version: '1.0', endpoints: [] }), - handleAuth: vi.fn().mockResolvedValue({ handled: true, response: { body: { ok: true }, status: 200 } }), - handleGraphQL: vi.fn().mockResolvedValue({ data: {} }), - handleStorage: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }), - dispatch: vi.fn().mockResolvedValue({ handled: true, response: { body: { success: true }, status: 200 } }), -}; - -vi.mock('@objectstack/runtime', () => { - return { - HttpDispatcher: function HttpDispatcher() { - return mockDispatcher; - }, - }; -}); - -import { createApp, toNodeListener, createEvent } from 'h3'; -import { createH3Router } from './index'; -import http from 'node:http'; - -const mockKernel = { name: 'test-kernel' } as any; - -function createTestApp() { - const app = createApp(); - const router = createH3Router({ kernel: mockKernel }); - app.use(router); - return app; -} - -async function makeRequest(app: any, url: string, method = 'GET', body?: any) { - const listener = toNodeListener(app); - - return new Promise<{ status: number; body: any }>((resolve, reject) => { - const server = http.createServer(listener); - server.listen(0, () => { - const addr = server.address() as any; - const reqOpts: http.RequestOptions = { - hostname: 'localhost', - port: addr.port, - path: url, - method, - headers: body ? { 'Content-Type': 'application/json' } : {}, - }; - const req = http.request(reqOpts, (res) => { - let data = ''; - res.on('data', (chunk) => { data += chunk; }); - res.on('end', () => { - server.close(); - try { - resolve({ status: res.statusCode || 500, body: JSON.parse(data) }); - } catch { - resolve({ status: res.statusCode || 500, body: data }); - } - }); - }); - req.on('error', (err) => { server.close(); reject(err); }); - if (body) req.write(JSON.stringify(body)); - req.end(); - }); - }); -} - -describe('createH3Router', () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - it('handles discovery endpoint', async () => { - const app = createTestApp(); - const res = await makeRequest(app, '/api'); - expect(res.status).toBe(200); - expect(res.body.data).toBeDefined(); - expect(res.body.data.version).toBe('1.0'); - expect(mockDispatcher.getDiscoveryInfo).toHaveBeenCalledWith('/api'); - }); - - it('handles auth route', async () => { - const app = createTestApp(); - const res = await makeRequest(app, '/api/auth/login', 'POST', { email: 'a@b.com' }); - expect(res.status).toBe(200); - expect(res.body.ok).toBe(true); - expect(mockDispatcher.handleAuth).toHaveBeenCalled(); - }); - - it('handles graphql route', async () => { - const app = createTestApp(); - const body = { query: '{ objects { name } }' }; - const res = await makeRequest(app, '/api/graphql', 'POST', body); - expect(res.status).toBe(200); - expect(res.body.data).toBeDefined(); - expect(mockDispatcher.handleGraphQL).toHaveBeenCalled(); - }); - - it('handles metadata route', async () => { - const app = createTestApp(); - const res = await makeRequest(app, '/api/meta/objects'); - expect(res.status).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/objects', - undefined, - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('handles data route', async () => { - const app = createTestApp(); - const res = await makeRequest(app, '/api/data/account'); - expect(res.status).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/data/account', - undefined, - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('handles storage route', async () => { - const app = createTestApp(); - const res = await makeRequest(app, '/api/storage/files'); - expect(res.status).toBe(200); - expect(mockDispatcher.handleStorage).toHaveBeenCalled(); - }); - - it('handles errors', async () => { - mockDispatcher.dispatch.mockRejectedValueOnce( - Object.assign(new Error('Forbidden'), { statusCode: 403 }), - ); - const app = createTestApp(); - const res = await makeRequest(app, '/api/data/account'); - expect(res.status).toBe(403); - expect(res.body.success).toBe(false); - expect(res.body.error.message).toBe('Forbidden'); - }); -}); diff --git a/packages/adapters/nuxt/tsconfig.json b/packages/adapters/nuxt/tsconfig.json deleted file mode 100644 index e8dfb8f584..0000000000 --- a/packages/adapters/nuxt/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": "../../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "esModuleInterop": true, - "skipLibCheck": true, - "lib": [ - "ES2020", - "DOM", - "DOM.Iterable" - ], - "types": [ - "node" - ] - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "dist" - ] -} diff --git a/packages/adapters/nuxt/vitest.config.ts b/packages/adapters/nuxt/vitest.config.ts deleted file mode 100644 index e2051df210..0000000000 --- a/packages/adapters/nuxt/vitest.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { defineConfig } from 'vitest/config'; -import path from 'node:path'; - -export default defineConfig({ - test: { - globals: true, - environment: 'node', - }, - resolve: { - alias: { - '@objectstack/runtime': path.resolve(__dirname, 'src/__mocks__/runtime.ts'), - }, - }, -}); diff --git a/packages/adapters/sveltekit/CHANGELOG.md b/packages/adapters/sveltekit/CHANGELOG.md deleted file mode 100644 index 3d47adb1b1..0000000000 --- a/packages/adapters/sveltekit/CHANGELOG.md +++ /dev/null @@ -1,473 +0,0 @@ -# @objectstack/sveltekit - -## 11.0.0 - -### Patch Changes - -- Updated dependencies [4d99a5c] -- Updated dependencies [61d441f] -- Updated dependencies [c224e18] -- Updated dependencies [6c4fbd9] -- Updated dependencies [aa33b02] - - @objectstack/runtime@11.0.0 - -## 10.3.0 - -### Patch Changes - -- Updated dependencies [8cf4f7c] -- Updated dependencies [f2063f3] - - @objectstack/runtime@10.3.0 - -## 10.2.0 - -### Patch Changes - -- @objectstack/runtime@10.2.0 - -## 10.1.0 - -### Patch Changes - -- Updated dependencies [ac79f16] -- Updated dependencies [94d2161] - - @objectstack/runtime@10.1.0 - -## 10.0.0 - -### Patch Changes - -- Updated dependencies [e16f2a8] -- Updated dependencies [47d978a] -- Updated dependencies [220ce5b] - - @objectstack/runtime@10.0.0 - -## 9.11.0 - -### Patch Changes - -- Updated dependencies [2afb612] - - @objectstack/runtime@9.11.0 - -## 9.10.0 - -### Patch Changes - -- Updated dependencies [1f88fd9] -- Updated dependencies [e2b5324] - - @objectstack/runtime@9.10.0 - -## 9.9.1 - -### Patch Changes - -- @objectstack/runtime@9.9.1 - -## 9.9.0 - -### Patch Changes - -- Updated dependencies [11af299] -- Updated dependencies [83fd318] -- Updated dependencies [9afeb2d] - - @objectstack/runtime@9.9.0 - -## 9.8.0 - -### Patch Changes - -- @objectstack/runtime@9.8.0 - -## 9.7.0 - -### Patch Changes - -- @objectstack/runtime@9.7.0 - -## 9.6.0 - -### Patch Changes - -- Updated dependencies [71578f2] - - @objectstack/runtime@9.6.0 - -## 9.5.1 - -### Patch Changes - -- @objectstack/runtime@9.5.1 - -## 9.5.0 - -### Patch Changes - -- @objectstack/runtime@9.5.0 - -## 9.4.0 - -### Patch Changes - -- Updated dependencies [0856476] - - @objectstack/runtime@9.4.0 - -## 9.3.0 - -### Patch Changes - -- Updated dependencies [1ada658] - - @objectstack/runtime@9.3.0 - -## 9.2.0 - -### Patch Changes - -- @objectstack/runtime@9.2.0 - -## 9.1.0 - -### Patch Changes - -- @objectstack/runtime@9.1.0 - -## 9.0.1 - -### Patch Changes - -- @objectstack/runtime@9.0.1 - -## 9.0.0 - -### Patch Changes - -- @objectstack/runtime@9.0.0 - -## 8.0.1 - -### Patch Changes - -- @objectstack/runtime@8.0.1 - -## 8.0.0 - -### Patch Changes - -- Updated dependencies [f68be58] -- Updated dependencies [bc0d85b] -- Updated dependencies [2537e28] -- Updated dependencies [0ec7717] -- Updated dependencies [c262301] - - @objectstack/runtime@8.0.0 - -## 7.9.0 - -### Patch Changes - -- Updated dependencies [ac1fc4c] -- Updated dependencies [ac1fc4c] - - @objectstack/runtime@7.9.0 - -## 7.8.0 - -### Patch Changes - -- Updated dependencies [a75823a] -- Updated dependencies [4fbb86a] -- Updated dependencies [e631f1e] -- Updated dependencies [424ab26] - - @objectstack/runtime@7.8.0 - -## 7.7.0 - -### Patch Changes - -- @objectstack/runtime@7.7.0 - -## 7.6.0 - -### Patch Changes - -- 3377e38: fix(release): stop the fixed-group major cascade caused by internal `@objectstack/*` peerDependencies. - - These packages declared workspace peerDependencies on other framework packages - in the changesets `fixed` group. Inside a fixed group, changesets rewrites those - peer ranges on every release and treats a peer-range change as breaking → major, - which cascaded to **all 69 packages → 8.0.0** on _any_ minor changeset. Required - internal peers are now regular `dependencies`; optional ones move to - `devDependencies` (kept for in-workspace tests, no longer a published peer edge). - Releases now bump correctly (patch/minor) instead of a spurious major. - -- Updated dependencies [8e539cc] - - @objectstack/runtime@7.6.0 - -## 7.5.0 - -## 7.4.1 - -## 7.4.0 - -## 7.3.0 - -## 7.2.1 - -## 7.2.0 - -## 7.1.0 - -## 7.0.0 - -### Patch Changes - -- Updated dependencies [dc72172] -- Updated dependencies [3a630b6] - - @objectstack/runtime@7.0.0 - -## 6.9.0 - -## 6.8.1 - -## 6.8.0 - -## 6.7.1 - -## 6.7.0 - -## 6.6.0 - -## 6.5.1 - -## 6.5.0 - -## 6.4.0 - -## 6.3.0 - -## 6.2.0 - -## 6.1.1 - -## 6.1.0 - -## 6.0.0 - -### Patch Changes - -- Updated dependencies [944f187] - - @objectstack/runtime@6.0.0 - -## 5.2.0 - -## 5.1.0 - -## 5.0.0 - -### Patch Changes - -- Updated dependencies [5e9dcb4] -- Updated dependencies [96ad4df] -- Updated dependencies [df18ae9] - - @objectstack/runtime@5.0.0 - -## 4.2.0 - -## 4.1.1 - -## 4.1.0 - -## 4.0.5 - -### Patch Changes - -- 15e0df6: chore: unify all package versions to a single patch release - -## 4.0.4 - -### Patch Changes - -- @objectstack/runtime@4.0.4 - -## 4.0.3 - -### Patch Changes - -- @objectstack/runtime@4.0.3 - -## 4.0.2 - -### Patch Changes - -- @objectstack/runtime@4.0.2 - -## 4.0.0 - -### Patch Changes - -- f08ffc3: Fix discovery API endpoint routing and protocol consistency. - - **Discovery route standardization:** - - - All adapters (Express, Fastify, Hono, NestJS, Next.js, Nuxt, SvelteKit) now mount the discovery endpoint at `{prefix}/discovery` instead of `{prefix}` root. - - `.well-known/objectstack` redirects now point to `{prefix}/discovery`. - - Client `connect()` fallback URL changed from `/api/v1` to `/api/v1/discovery`. - - Runtime dispatcher handles both `/discovery` (standard) and `/` (legacy) for backward compatibility. - - **Schema & route alignment:** - - - Added `storage` (service: `file-storage`) and `feed` (service: `data`) routes to `DEFAULT_DISPATCHER_ROUTES`. - - Added `feed` and `discovery` fields to `ApiRoutesSchema`. - - Unified `GetDiscoveryResponseSchema` with `DiscoverySchema` as single source of truth. - - Client `getRoute('feed')` fallback updated from `/api/v1/data` to `/api/v1/feed`. - - **Type safety:** - - - Extracted `ApiRouteType` from `ApiRoutes` keys for type-safe client route resolution. - - Removed `as any` type casting in client route access. - -- Updated dependencies [f08ffc3] -- Updated dependencies [e0b0a78] - - @objectstack/runtime@4.0.0 - -## 3.3.1 - -### Patch Changes - -- @objectstack/runtime@3.3.1 - -## 3.3.0 - -### Patch Changes - -- @objectstack/runtime@3.3.0 - -## 3.2.9 - -### Patch Changes - -- @objectstack/runtime@3.2.9 - -## 3.2.8 - -### Patch Changes - -- @objectstack/runtime@3.2.8 - -## 3.2.8 - -### Patch Changes - -- fix: unified catch-all dispatch pattern — `createRequestHandler()` now delegates all non-framework-specific routes to `HttpDispatcher.dispatch()`, automatically supporting packages, analytics, automation, i18n, ui, openapi, custom endpoints, and any future routes -- Only auth (service check), storage (formData), GraphQL (raw result), and discovery (response wrapper) remain as explicit routes - -## 3.2.7 - -### Patch Changes - -- @objectstack/runtime@3.2.7 - -## 3.2.6 - -### Patch Changes - -- @objectstack/runtime@3.2.6 - -## 3.2.5 - -### Patch Changes - -- @objectstack/runtime@3.2.5 - -## 3.2.4 - -### Patch Changes - -- @objectstack/runtime@3.2.4 - -## 3.2.3 - -### Patch Changes - -- @objectstack/runtime@3.2.3 - -## 3.2.2 - -### Patch Changes - -- @objectstack/runtime@3.2.2 - -## 3.2.1 - -### Patch Changes - -- @objectstack/runtime@3.2.1 - -## 3.2.0 - -### Patch Changes - -- @objectstack/runtime@3.2.0 - -## 3.1.1 - -### Patch Changes - -- @objectstack/runtime@3.1.1 - -## 3.1.0 - -### Patch Changes - -- @objectstack/runtime@3.1.0 - -## 3.0.11 - -### Patch Changes - -- @objectstack/runtime@3.0.11 - -## 3.0.10 - -### Patch Changes - -- @objectstack/runtime@3.0.10 - -## 3.0.9 - -### Patch Changes - -- @objectstack/runtime@3.0.9 - -## 3.0.8 - -### Patch Changes - -- @objectstack/runtime@3.0.8 - -## 3.0.7 - -### Patch Changes - -- @objectstack/runtime@3.0.7 - -## 3.0.6 - -### Patch Changes - -- @objectstack/runtime@3.0.6 - -## 3.0.5 - -### Patch Changes - -- @objectstack/runtime@3.0.5 - -## 3.0.4 - -### Patch Changes - -- @objectstack/runtime@3.0.4 - -## 3.0.3 - -### Patch Changes - -- Updated dependencies [c7267f6] - - @objectstack/runtime@3.0.3 diff --git a/packages/adapters/sveltekit/LICENSE b/packages/adapters/sveltekit/LICENSE deleted file mode 100644 index 16bc23f404..0000000000 --- a/packages/adapters/sveltekit/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute - must include a readable copy of the attribution notices - contained within such NOTICE file, excluding those notices - that do not pertain to any part of the Derivative Works, - in at least one of the following places: within a NOTICE - text file distributed as part of the Derivative Works; within - the Source form or documentation, if provided along with - the Derivative Works; or, within a display generated by the - Derivative Works, if and wherever such third-party notices - normally appear. The contents of the NOTICE file are for - informational purposes only and do not modify the License. - You may add Your own attribution notices within Derivative - Works that You distribute, alongside or as an addendum to - the NOTICE text from the Work, provided that such additional - attribution notices cannot be construed as modifying the - License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2026 ObjectStack - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/adapters/sveltekit/README.md b/packages/adapters/sveltekit/README.md deleted file mode 100644 index 295aeca372..0000000000 --- a/packages/adapters/sveltekit/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# @objectstack/sveltekit - -> SvelteKit adapter for ObjectStack — server endpoints and `handle` hook for the auto-generated REST API. - -[![npm](https://img.shields.io/npm/v/@objectstack/sveltekit.svg)](https://www.npmjs.com/package/@objectstack/sveltekit) -[![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) - -## Overview - -Bridges SvelteKit request events to `HttpDispatcher`. Ship ObjectStack either as a catch-all `+server.ts` route or as a `handle` hook for global interception. - -## Installation - -```bash -pnpm add @objectstack/sveltekit -``` - -## Quick Start — catch-all endpoint - -```typescript -// src/routes/api/[...path]/+server.ts -import { createRequestHandler } from '@objectstack/sveltekit'; -import { kernel } from '$lib/server/kernel'; - -const handler = createRequestHandler({ kernel }); - -export const GET = handler; -export const POST = handler; -export const PUT = handler; -export const PATCH = handler; -export const DELETE = handler; -``` - -### Global `handle` hook - -```typescript -// src/hooks.server.ts -import { sequence } from '@sveltejs/kit/hooks'; -import { createHandle } from '@objectstack/sveltekit'; -import { kernel } from '$lib/server/kernel'; - -export const handle = sequence(createHandle({ kernel, prefix: '/api' })); -``` - -## Key Exports - -| Export | Kind | Description | -|:---|:---|:---| -| `createRequestHandler(options)` | function | Returns a `RequestHandler` for catch-all routes. | -| `createHandle(options)` | function | Returns a SvelteKit `Handle` hook. | -| `SvelteKitAdapterOptions` | interface | `{ kernel, prefix? }`. | - -## When to use - -- ✅ SvelteKit 2.x applications. -- ✅ Projects preferring the `handle` hook over route files. - -## When not to use - -- ❌ Sapper and SvelteKit 1.x are not supported. - -## Related Packages - -- [`@objectstack/runtime`](../../runtime), [`@objectstack/rest`](../../rest). - -## Links - -- 📖 Docs: -- 📚 API Reference: - -## License - -Apache-2.0 © ObjectStack diff --git a/packages/adapters/sveltekit/package.json b/packages/adapters/sveltekit/package.json deleted file mode 100644 index cb713e7022..0000000000 --- a/packages/adapters/sveltekit/package.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "name": "@objectstack/sveltekit", - "version": "11.0.0", - "license": "Apache-2.0", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "require": "./dist/index.js" - } - }, - "scripts": { - "build": "tsup --config ../../../tsup.config.ts", - "test": "vitest run", - "test:watch": "vitest" - }, - "peerDependencies": { - "@sveltejs/kit": "^2.58.0" - }, - "devDependencies": { - "@objectstack/runtime": "workspace:*", - "@sveltejs/kit": "^2.66.0", - "typescript": "^6.0.3", - "vitest": "^4.1.9" - }, - "description": "SvelteKit adapter for ObjectStack — server endpoints for the ObjectStack REST API.", - "keywords": [ - "objectstack", - "sveltekit", - "adapter", - "rest" - ], - "author": "ObjectStack", - "repository": { - "type": "git", - "url": "https://github.com/objectstack-ai/framework.git", - "directory": "packages/adapters/sveltekit" - }, - "homepage": "https://objectstack.ai/docs", - "bugs": "https://github.com/objectstack-ai/framework/issues", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist", - "README.md" - ], - "engines": { - "node": ">=18.0.0" - }, - "dependencies": { - "@objectstack/runtime": "workspace:^" - } -} diff --git a/packages/adapters/sveltekit/src/__mocks__/runtime.ts b/packages/adapters/sveltekit/src/__mocks__/runtime.ts deleted file mode 100644 index 7961fd466c..0000000000 --- a/packages/adapters/sveltekit/src/__mocks__/runtime.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Stub for @objectstack/runtime - replaced by vi.mock in tests -export const HttpDispatcher = class {}; -export type ObjectKernel = any; -export type HttpDispatcherResult = any; diff --git a/packages/adapters/sveltekit/src/index.ts b/packages/adapters/sveltekit/src/index.ts deleted file mode 100644 index e2efcf64bb..0000000000 --- a/packages/adapters/sveltekit/src/index.ts +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { type ObjectKernel, HttpDispatcher, HttpDispatcherResult } from '@objectstack/runtime'; - -export interface SvelteKitAdapterOptions { - kernel: ObjectKernel; - prefix?: string; -} - -/** - * Auth service interface with handleRequest method - */ -interface AuthService { - handleRequest(request: Request): Promise; -} - -/** - * SvelteKit request event type (minimal interface to avoid hard dependency on @sveltejs/kit types at runtime) - */ -interface RequestEvent { - request: Request; - url: URL; - params: Record; -} - -/** - * Creates a SvelteKit request handler for ObjectStack API routes. - * Use in a catch-all `+server.ts` route like `src/routes/api/[...path]/+server.ts`. - * - * Only auth, GraphQL, storage, and discovery need explicit handling. - * All other routes delegate to `HttpDispatcher.dispatch()` automatically. - * - * @example - * ```ts - * // src/routes/api/[...path]/+server.ts - * import { createRequestHandler } from '@objectstack/sveltekit'; - * import { kernel } from '$lib/kernel'; - * - * const handler = createRequestHandler({ kernel }); - * - * export const GET = handler; - * export const POST = handler; - * export const PUT = handler; - * export const PATCH = handler; - * export const DELETE = handler; - * ``` - */ -export function createRequestHandler(options: SvelteKitAdapterOptions) { - const dispatcher = new HttpDispatcher(options.kernel); - const prefix = options.prefix || '/api'; - - const errorJson = (message: string, code: number = 500) => { - return new Response(JSON.stringify({ success: false, error: { message, code } }), { - status: code, - headers: { 'Content-Type': 'application/json' }, - }); - }; - - const toResponse = (result: HttpDispatcherResult): Response => { - if (result.handled) { - if (result.response) { - const headers = new Headers({ 'Content-Type': 'application/json' }); - if (result.response.headers) { - Object.entries(result.response.headers).forEach(([k, v]) => headers.set(k, v as string)); - } - return new Response(JSON.stringify(result.response.body), { - status: result.response.status, - headers, - }); - } - if (result.result) { - const res = result.result; - if (res.type === 'redirect' && res.url) { - return new Response(null, { - status: 302, - headers: { Location: res.url }, - }); - } - if (res.type === 'stream' && res.stream) { - const headers = new Headers(); - if (res.headers) { - Object.entries(res.headers).forEach(([k, v]) => headers.set(k, v as string)); - } - return new Response(res.stream, { status: 200, headers }); - } - return new Response(JSON.stringify(res), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } - } - return errorJson('Not Found', 404); - }; - - return async function handler(event: RequestEvent): Promise { - const { request, url } = event; - const method = request.method; - const path = url.pathname.substring(prefix.length); - const segments = path.split('/').filter(Boolean); - - // --- Discovery --- - if (segments.length === 0 && method === 'GET') { - return new Response(JSON.stringify({ data: await dispatcher.getDiscoveryInfo(prefix) }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } - - if (segments.length === 1 && segments[0] === 'discovery' && method === 'GET') { - return new Response(JSON.stringify({ data: await dispatcher.getDiscoveryInfo(prefix) }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } - - try { - // --- Auth (needs auth service integration) --- - if (segments[0] === 'auth') { - const subPath = segments.slice(1).join('/'); - - // Try AuthPlugin service first (prefer async to support factory-based services) - let authService: AuthService | null = null; - try { - if (typeof options.kernel.getServiceAsync === 'function') { - authService = await options.kernel.getServiceAsync('auth'); - } else if (typeof options.kernel.getService === 'function') { - authService = options.kernel.getService('auth'); - } - } catch { - // Service not registered — fall through to dispatcher - authService = null; - } - - if (authService && typeof authService.handleRequest === 'function') { - return await authService.handleRequest(request); - } - - // Fallback to dispatcher - const body = method === 'GET' || method === 'HEAD' - ? {} - : await request.json().catch(() => ({})); - const result = await dispatcher.handleAuth(subPath, method, body, { request }); - return toResponse(result); - } - - // --- GraphQL (returns raw result, not HttpDispatcherResult) --- - if (segments[0] === 'graphql' && method === 'POST') { - const body = await request.json() as { query: string; variables?: any }; - const result = await dispatcher.handleGraphQL(body, { request }); - return new Response(JSON.stringify(result), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } - - // --- Storage (needs formData parsing) --- - if (segments[0] === 'storage') { - const subPath = segments.slice(1).join('/'); - let file: any = undefined; - if (method === 'POST' && subPath === 'upload') { - const formData = await request.formData(); - file = formData.get('file'); - } - const result = await dispatcher.handleStorage( - subPath ? `/${subPath}` : '', - method, - file, - { request }, - ); - return toResponse(result); - } - - // --- Catch-all: delegate to dispatcher.dispatch() --- - // Handles meta, data, packages, analytics, automation, i18n, ui, - // openapi, custom API endpoints, and any future routes. - const subPath = path || ''; - - let body: any = undefined; - if (method === 'POST' || method === 'PUT' || method === 'PATCH') { - body = await request.json().catch(() => ({})); - } - - const queryParams: Record = {}; - url.searchParams.forEach((val, key) => { queryParams[key] = val; }); - - const result = await dispatcher.dispatch(method, subPath, body, queryParams, { request }, prefix); - return toResponse(result); - } catch (err: any) { - return errorJson(err.message || 'Internal Server Error', err.statusCode || 500); - } - }; -} - -/** - * Creates a SvelteKit handle hook that attaches the kernel to event.locals. - * - * @example - * ```ts - * // src/hooks.server.ts - * import { createHandle } from '@objectstack/sveltekit'; - * export const handle = createHandle({ kernel }); - * ``` - */ -export function createHandle(options: SvelteKitAdapterOptions) { - return async function handle({ event, resolve }: { event: any; resolve: (event: any) => Promise }) { - event.locals.objectStack = options.kernel; - return resolve(event); - }; -} diff --git a/packages/adapters/sveltekit/src/sveltekit.test.ts b/packages/adapters/sveltekit/src/sveltekit.test.ts deleted file mode 100644 index 7593fc798d..0000000000 --- a/packages/adapters/sveltekit/src/sveltekit.test.ts +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { describe, it, expect, vi, beforeEach } from 'vitest'; - -// Mock dispatcher instance -const mockDispatcher = { - getDiscoveryInfo: vi.fn().mockReturnValue({ version: '1.0', endpoints: [] }), - handleAuth: vi.fn().mockResolvedValue({ handled: true, response: { body: { ok: true }, status: 200 } }), - handleGraphQL: vi.fn().mockResolvedValue({ data: {} }), - handleStorage: vi.fn().mockResolvedValue({ handled: true, response: { body: {}, status: 200 } }), - dispatch: vi.fn().mockResolvedValue({ handled: true, response: { body: { success: true }, status: 200 } }), -}; - -vi.mock('@objectstack/runtime', () => { - return { - HttpDispatcher: function HttpDispatcher() { - return mockDispatcher; - }, - }; -}); - -import { createRequestHandler, createHandle } from './index'; - -const mockKernel = { name: 'test-kernel' } as any; - -function makeEvent(url: string, method = 'GET', body?: any): any { - const parsedUrl = new URL(url, 'http://localhost'); - const init: RequestInit = { method }; - if (body) { - init.body = JSON.stringify(body); - init.headers = { 'Content-Type': 'application/json' }; - } - return { - request: new Request(parsedUrl, init), - url: parsedUrl, - params: {}, - }; -} - -describe('createRequestHandler', () => { - let handler: ReturnType; - - beforeEach(() => { - vi.clearAllMocks(); - handler = createRequestHandler({ kernel: mockKernel }); - }); - - describe('Discovery', () => { - it('GET /api returns discovery info', async () => { - const event = makeEvent('http://localhost/api'); - const res = await handler(event); - expect(res.status).toBe(200); - const json = await res.json(); - expect(json.data.version).toBe('1.0'); - expect(mockDispatcher.getDiscoveryInfo).toHaveBeenCalledWith('/api'); - }); - - it('uses custom prefix', async () => { - const customHandler = createRequestHandler({ kernel: mockKernel, prefix: '/v2' }); - const event = makeEvent('http://localhost/v2'); - const res = await customHandler(event); - expect(res.status).toBe(200); - expect(mockDispatcher.getDiscoveryInfo).toHaveBeenCalledWith('/v2'); - }); - }); - - describe('Auth', () => { - it('POST /api/auth/login calls handleAuth', async () => { - const event = makeEvent('http://localhost/api/auth/login', 'POST', { email: 'a@b.com' }); - const res = await handler(event); - expect(res.status).toBe(200); - const json = await res.json(); - expect(json.ok).toBe(true); - expect(mockDispatcher.handleAuth).toHaveBeenCalledWith( - 'login', 'POST', { email: 'a@b.com' }, - expect.objectContaining({ request: expect.anything() }), - ); - }); - - it('GET /api/auth/callback calls handleAuth with empty body', async () => { - const event = makeEvent('http://localhost/api/auth/callback'); - const res = await handler(event); - expect(res.status).toBe(200); - expect(mockDispatcher.handleAuth).toHaveBeenCalledWith( - 'callback', 'GET', {}, - expect.objectContaining({ request: expect.anything() }), - ); - }); - - it('returns error on exception', async () => { - mockDispatcher.handleAuth.mockRejectedValueOnce( - Object.assign(new Error('Unauthorized'), { statusCode: 401 }), - ); - const event = makeEvent('http://localhost/api/auth/login', 'POST', {}); - const res = await handler(event); - expect(res.status).toBe(401); - const json = await res.json(); - expect(json.success).toBe(false); - }); - }); - - describe('Auth via AuthPlugin service', () => { - it('uses kernel.getService("auth") when available', async () => { - const mockHandleRequest = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ user: { id: '1' } }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }), - ); - const kernelWithAuth = { - ...mockKernel, - getService: vi.fn().mockReturnValue({ handleRequest: mockHandleRequest }), - }; - const authHandler = createRequestHandler({ kernel: kernelWithAuth }); - const event = makeEvent('http://localhost/api/auth/sign-in/email', 'POST', { email: 'a@b.com' }); - const res = await authHandler(event); - expect(res.status).toBe(200); - expect(kernelWithAuth.getService).toHaveBeenCalledWith('auth'); - expect(mockHandleRequest).toHaveBeenCalled(); - }); - - it('uses kernel.getServiceAsync("auth") when available (async factory)', async () => { - const mockHandleRequest = vi.fn().mockResolvedValue( - new Response(JSON.stringify({ user: { id: '2' } }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }), - ); - const kernelWithAsyncAuth = { - ...mockKernel, - getServiceAsync: vi.fn().mockResolvedValue({ handleRequest: mockHandleRequest }), - }; - const authHandler = createRequestHandler({ kernel: kernelWithAsyncAuth }); - const event = makeEvent('http://localhost/api/auth/sign-in/email', 'POST', { email: 'a@b.com' }); - const res = await authHandler(event); - expect(res.status).toBe(200); - expect(kernelWithAsyncAuth.getServiceAsync).toHaveBeenCalledWith('auth'); - expect(mockHandleRequest).toHaveBeenCalled(); - }); - - it('falls back to dispatcher when getServiceAsync throws', async () => { - const kernelWithFailingAsync = { - ...mockKernel, - getServiceAsync: vi.fn().mockRejectedValue(new Error("Service 'auth' not found")), - }; - const authHandler = createRequestHandler({ kernel: kernelWithFailingAsync }); - const event = makeEvent('http://localhost/api/auth/login', 'POST', { email: 'a@b.com' }); - const res = await authHandler(event); - expect(res.status).toBe(200); - expect(mockDispatcher.handleAuth).toHaveBeenCalled(); - }); - }); - - describe('GraphQL', () => { - it('POST /api/graphql calls handleGraphQL', async () => { - const body = { query: '{ objects { name } }' }; - const event = makeEvent('http://localhost/api/graphql', 'POST', body); - const res = await handler(event); - expect(res.status).toBe(200); - const json = await res.json(); - expect(json.data).toBeDefined(); - expect(mockDispatcher.handleGraphQL).toHaveBeenCalledWith( - body, expect.objectContaining({ request: expect.anything() }), - ); - }); - }); - - describe('Metadata', () => { - it('GET /api/meta/objects delegates to dispatch()', async () => { - const event = makeEvent('http://localhost/api/meta/objects'); - const res = await handler(event); - expect(res.status).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/meta/objects', - undefined, - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - }); - - describe('Data', () => { - it('GET /api/data/account delegates to dispatch()', async () => { - const event = makeEvent('http://localhost/api/data/account'); - const res = await handler(event); - expect(res.status).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'GET', - '/data/account', - undefined, - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('POST /api/data/account parses body', async () => { - const body = { name: 'Acme' }; - const event = makeEvent('http://localhost/api/data/account', 'POST', body); - const res = await handler(event); - expect(res.status).toBe(200); - expect(mockDispatcher.dispatch).toHaveBeenCalledWith( - 'POST', - '/data/account', - body, - expect.any(Object), - expect.objectContaining({ request: expect.anything() }), - '/api', - ); - }); - - it('returns 404 when not handled', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ handled: false }); - const event = makeEvent('http://localhost/api/data/missing'); - const res = await handler(event); - expect(res.status).toBe(404); - }); - }); - - describe('Storage', () => { - it('GET /api/storage/files calls handleStorage', async () => { - const event = makeEvent('http://localhost/api/storage/files'); - const res = await handler(event); - expect(res.status).toBe(200); - expect(mockDispatcher.handleStorage).toHaveBeenCalledWith( - '/files', 'GET', undefined, - expect.objectContaining({ request: expect.anything() }), - ); - }); - }); - - describe('Error handling', () => { - it('returns 404 for unknown routes', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ handled: false }); - const event = makeEvent('http://localhost/api/unknown'); - const res = await handler(event); - expect(res.status).toBe(404); - }); - - it('returns 500 on generic error', async () => { - mockDispatcher.dispatch.mockRejectedValueOnce(new Error()); - const event = makeEvent('http://localhost/api/data/account'); - const res = await handler(event); - expect(res.status).toBe(500); - }); - }); - - describe('toResponse', () => { - it('handles redirect result', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - result: { type: 'redirect', url: 'https://example.com' }, - }); - const event = makeEvent('http://localhost/api/data/redir'); - const res = await handler(event); - expect(res.status).toBe(302); - expect(res.headers.get('location')).toBe('https://example.com'); - }); - - it('handles generic result objects', async () => { - mockDispatcher.dispatch.mockResolvedValueOnce({ - handled: true, - result: { foo: 'bar' }, - }); - const event = makeEvent('http://localhost/api/data/custom'); - const res = await handler(event); - expect(res.status).toBe(200); - const json = await res.json(); - expect(json.foo).toBe('bar'); - }); - }); -}); - -describe('createHandle', () => { - it('attaches kernel to event.locals', async () => { - const handle = createHandle({ kernel: mockKernel }); - const event: any = { locals: {} }; - const resolve = vi.fn().mockResolvedValue(new Response('ok')); - - await handle({ event, resolve }); - - expect(event.locals.objectStack).toBe(mockKernel); - expect(resolve).toHaveBeenCalledWith(event); - }); -}); diff --git a/packages/adapters/sveltekit/tsconfig.json b/packages/adapters/sveltekit/tsconfig.json deleted file mode 100644 index e8dfb8f584..0000000000 --- a/packages/adapters/sveltekit/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": "../../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "esModuleInterop": true, - "skipLibCheck": true, - "lib": [ - "ES2020", - "DOM", - "DOM.Iterable" - ], - "types": [ - "node" - ] - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "dist" - ] -} diff --git a/packages/adapters/sveltekit/vitest.config.ts b/packages/adapters/sveltekit/vitest.config.ts deleted file mode 100644 index e2051df210..0000000000 --- a/packages/adapters/sveltekit/vitest.config.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { defineConfig } from 'vitest/config'; -import path from 'node:path'; - -export default defineConfig({ - test: { - globals: true, - environment: 'node', - }, - resolve: { - alias: { - '@objectstack/runtime': path.resolve(__dirname, 'src/__mocks__/runtime.ts'), - }, - }, -}); diff --git a/packages/client/package.json b/packages/client/package.json index 5c5d1469cd..c0b4a9b23d 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -27,9 +27,7 @@ "@objectstack/hono": "workspace:*", "@objectstack/objectql": "workspace:*", "@objectstack/plugin-hono-server": "workspace:*", - "@objectstack/plugin-msw": "workspace:*", "@objectstack/runtime": "workspace:*", - "msw": "^2.14.6", "typescript": "^6.0.3", "vitest": "^4.1.9" }, diff --git a/packages/client/src/client.msw.test.ts b/packages/client/src/client.msw.test.ts deleted file mode 100644 index 2ce9b2185f..0000000000 --- a/packages/client/src/client.msw.test.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest'; -import { setupServer } from 'msw/node'; -import { http, HttpResponse } from 'msw'; -import { LiteKernel } from '@objectstack/core'; -import { ObjectQLPlugin } from '@objectstack/objectql'; -import { InMemoryDriver } from '@objectstack/driver-memory'; -import { MSWPlugin } from '@objectstack/plugin-msw'; -import { ObjectStackClient } from './index'; - -// 127.0.0.1 usage logic remains -const BASE_URL = 'http://127.0.0.1'; - -describe('ObjectStackClient (with MSW Plugin)', () => { - let kernel: LiteKernel; - let mswPlugin: MSWPlugin; - let server: any; - - beforeAll(async () => { - // 1. Setup Kernel - kernel = new LiteKernel(); - kernel.use(new ObjectQLPlugin()); - - // 2. Setup MSW Plugin (headless mode) - mswPlugin = new MSWPlugin({ - enableBrowser: false, - // baseUrl: '/api/v1' // Default is /api/v1 - }); - kernel.use(mswPlugin); - - // --- BROKER SHIM START --- - // HttpDispatcher requires a broker to function. We inject a simple shim. - (kernel as any).broker = { - call: async (action: string, params: any, opts: any) => { - const parts = action.split('.'); - const service = parts[0]; - const method = parts[1]; - - if (service === 'data') { - const ql = kernel.getService('objectql'); - // Delegate to protocol service when available for proper expand/populate support - let protocol: any; - try { protocol = kernel.getService('protocol'); } catch { /* not registered */ } - if (method === 'create') { - const res = await ql.insert(params.object, params.data); - const record = { ...params.data, ...res }; - return { object: params.object, id: record.id, record }; - } - if (method === 'get') { - if (protocol) { - return await protocol.getData({ object: params.object, id: params.id, expand: params.expand, select: params.select }); - } - const record = await ql.findOne(params.object, { where: { id: params.id } }); - return record ? { object: params.object, id: params.id, record } : null; - } - if (method === 'query') { - if (protocol) { - return await protocol.findData({ object: params.object, query: params.query }); - } - const queryOpts = params.query || {}; - const records = await ql.find(params.object, { where: queryOpts.filters || queryOpts.filter || queryOpts.where }); - return { object: params.object, records, total: records.length }; - } - if (method === 'find') { - if (protocol) { - return await protocol.findData({ object: params.object, query: params.query }); - } - const queryOpts = params.query || {}; - const records = await ql.find(params.object, { where: queryOpts.filters || queryOpts.filter || queryOpts.where }); - return { object: params.object, records, total: records.length }; - } - } - - if (service === 'metadata') { - const ql = kernel.getService('objectql'); - if (method === 'getObject') return ql.registry.getObject(params.objectName); - if (method === 'objects') return ql.registry.getAllObjects(); - } - - console.warn(`[BrokerShim] Action not implemented: ${action}`); - return null; - } - }; - // --- BROKER SHIM END --- - - await kernel.bootstrap(); - - // 3. Setup Driver & Schema - const ql = kernel.getService('objectql'); - ql.registerDriver(new InMemoryDriver(), true); - - ql.registerObject({ - name: 'customer', - fields: { - name: { type: 'text' } - } - }); - - // Add some data - await ql.insert('customer', { id: '101', name: 'Alice' }); - await ql.insert('customer', { id: '102', name: 'Bob' }); - - // 4. Get handlers and start MSW Node Server - const handlers = mswPlugin.getHandlers(); - - // Manual handlers to cover gaps in MSWPlugin generation - handlers.push( - http.get('http://127.0.0.1/api/v1', () => { - return HttpResponse.json({ - name: 'ObjectOS', - version: '1.0.0', - routes: { - data: '/api/v1/data', - metadata: '/api/v1/meta', - auth: '/api/v1/auth' - }, - capabilities: ['data', 'metadata'], - features: {} - }); - }), - - http.get('http://127.0.0.1/api/v1/meta/object/:name', async ({ params }) => { - try { - const res = await (kernel as any).broker.call('metadata.getObject', { objectName: params.name }); - return HttpResponse.json({ success: true, data: res }); - } catch (e: any) { return HttpResponse.json({ success: false, error: e.message }, { status: 404 }); } - }), - - http.get('http://127.0.0.1/api/v1/data/:object', async ({ params }) => { - try { - // Simplifying: ignoring query filters for this test - const res = await (kernel as any).broker.call('data.find', { object: params.object, filters: {} }); - return HttpResponse.json({ success: true, data: res }); - } catch (e: any) { return HttpResponse.json({ success: false, error: e.message }, { status: 500 }); } - }), - - http.post('http://127.0.0.1/api/v1/data/:object', async ({ params, request }) => { - try { - const body = await request.json(); - const res = await (kernel as any).broker.call('data.create', { object: params.object, data: body }); - return HttpResponse.json({ success: true, data: res }, { status: 201 }); - } catch (e: any) { return HttpResponse.json({ success: false, error: e.message }, { status: 500 }); } - }), - - http.get('http://127.0.0.1/api/v1/data/:object/:id', async ({ params }) => { - try { - const res = await (kernel as any).broker.call('data.get', { object: params.object, id: params.id }); - return HttpResponse.json({ success: true, data: res }); - } catch (e: any) { return HttpResponse.json({ success: false, error: e.message }, { status: 404 }); } - }) - ); - - server = setupServer(...handlers); - server.listen({ onUnhandledRequest: 'error' }); - }); - - // Reset handlers after each test to ensure clean state - afterEach(() => server.resetHandlers()); - - afterAll(async () => { - if (server) server.close(); - if (kernel) await kernel.shutdown(); - }); - - it('should connect and discover endpoints', async () => { - // MSWPlugin configured with baseUrl: '' creates handlers at root. - // Client connects to /api/v1. - // To make them match in THIS test file where I used baseUrl: '', - // I should have configured MSWPlugin with baseUrl: '/api/v1' or left default. - - // RE-FIXING SETUP in beforeAll (via edit). - // If I change MSWPlugin config to default ('/api/v1'), - // then Client(BASE_URL).connect() -> fetches BASE_URL/api/v1 -> matches MSW handler /api/v1. - - const client = new ObjectStackClient({ baseUrl: BASE_URL }); - await client.connect(); - - expect(client['discoveryInfo']).toBeDefined(); - }); - - it('should fetch object metadata', async () => { - const client = new ObjectStackClient({ baseUrl: BASE_URL }); - await client.connect(); - - // Spec: GetMetaItemResponse = { type, name, item } - const customerRes: any = await client.meta.getItem('object', 'customer'); - expect(customerRes).toBeDefined(); - // After unwrapResponse, we get the protocol-level response - // The manual handler wraps as { success, data: schema }, so unwrap yields the schema - const schema = customerRes.item || customerRes; - expect(schema.name).toBe('customer'); - }); - - it('should find data records', async () => { - const client = new ObjectStackClient({ baseUrl: BASE_URL }); - await client.connect(); - - // Spec: FindDataResponse = { object, records, total? } - const resultsRes = await client.data.find('customer'); - expect(resultsRes.records).toBeDefined(); - expect(resultsRes.records.length).toBe(2); - expect(resultsRes.records[0].name).toBe('Alice'); - }); - - it('should get single record', async () => { - const client = new ObjectStackClient({ baseUrl: BASE_URL }); - await client.connect(); - - // Spec: GetDataResponse = { object, id, record } - const recordRes = await client.data.get('customer', '101'); - expect(recordRes.record).toBeDefined(); - expect(recordRes.record.name).toBe('Alice'); - }); - - it('should create record', async () => { - const client = new ObjectStackClient({ baseUrl: BASE_URL }); - await client.connect(); - - // Spec: CreateDataResponse = { object, id, record } - const newRecordRes = await client.data.create('customer', { name: 'Charlie' }); - expect(newRecordRes.record).toBeDefined(); - expect(newRecordRes.record.name).toBe('Charlie'); - expect(newRecordRes.id).toBeDefined(); - }); -}); diff --git a/packages/plugins/plugin-msw/CHANGELOG.md b/packages/plugins/plugin-msw/CHANGELOG.md deleted file mode 100644 index 8dbf475c44..0000000000 --- a/packages/plugins/plugin-msw/CHANGELOG.md +++ /dev/null @@ -1,1531 +0,0 @@ -# @objectstack/plugin-msw - -## 11.0.0 - -### Patch Changes - -- Updated dependencies [4d99a5c] -- Updated dependencies [ab5718a] -- Updated dependencies [61d441f] -- Updated dependencies [c224e18] -- Updated dependencies [d616e1d] -- Updated dependencies [4845c12] -- Updated dependencies [c1a754a] -- Updated dependencies [6fbe91f] -- Updated dependencies [715d667] -- Updated dependencies [5eef4cf] -- Updated dependencies [72759e1] -- Updated dependencies [6c4fbd9] -- Updated dependencies [ef3ed67] -- Updated dependencies [359c0aa] -- Updated dependencies [cd51229] -- Updated dependencies [7697a0e] -- Updated dependencies [e7e04f1] -- Updated dependencies [cfd5ac4] -- Updated dependencies [2be5c1f] -- Updated dependencies [ad143ce] -- Updated dependencies [5c4a8c8] -- Updated dependencies [3afaeed] -- Updated dependencies [795b6d1] -- Updated dependencies [8801c02] -- Updated dependencies [3d04e06] -- Updated dependencies [4a84c98] -- Updated dependencies [c715d25] -- Updated dependencies [aa33b02] -- Updated dependencies [d980f0d] -- Updated dependencies [a658523] -- Updated dependencies [82ff91c] -- Updated dependencies [638f472] - - @objectstack/objectql@11.0.0 - - @objectstack/runtime@11.0.0 - - @objectstack/spec@11.0.0 - - @objectstack/types@11.0.0 - - @objectstack/core@11.0.0 - -## 10.3.0 - -### Patch Changes - -- Updated dependencies [211425e] -- Updated dependencies [8cf4f7c] -- Updated dependencies [f2063f3] - - @objectstack/objectql@10.3.0 - - @objectstack/runtime@10.3.0 - - @objectstack/spec@10.3.0 - - @objectstack/core@10.3.0 - - @objectstack/types@10.3.0 - -## 10.2.0 - -### Patch Changes - -- Updated dependencies [b496498] - - @objectstack/spec@10.2.0 - - @objectstack/core@10.2.0 - - @objectstack/objectql@10.2.0 - - @objectstack/runtime@10.2.0 - - @objectstack/types@10.2.0 - -## 10.1.0 - -### Patch Changes - -- Updated dependencies [49da36e] -- Updated dependencies [ac79f16] -- Updated dependencies [94d2161] - - @objectstack/spec@10.1.0 - - @objectstack/runtime@10.1.0 - - @objectstack/core@10.1.0 - - @objectstack/objectql@10.1.0 - - @objectstack/types@10.1.0 - -## 10.0.0 - -### Patch Changes - -- Updated dependencies [d7ff626] -- Updated dependencies [2a1b16b] -- Updated dependencies [e16f2a8] -- Updated dependencies [e411a82] -- Updated dependencies [a581385] -- Updated dependencies [47d978a] -- Updated dependencies [d5f6d29] -- Updated dependencies [220ce5b] -- Updated dependencies [3efe334] -- Updated dependencies [feead7e] -- Updated dependencies [6ca20b3] -- Updated dependencies [5f875fe] -- Updated dependencies [b469950] - - @objectstack/spec@10.0.0 - - @objectstack/objectql@10.0.0 - - @objectstack/runtime@10.0.0 - - @objectstack/core@10.0.0 - - @objectstack/types@10.0.0 - -## 9.11.0 - -### Patch Changes - -- Updated dependencies [e7f6539] -- Updated dependencies [2365d07] -- Updated dependencies [6595b53] -- Updated dependencies [fa8964d] -- Updated dependencies [36138c7] -- Updated dependencies [a8e4f3b] -- Updated dependencies [4c213c2] -- Updated dependencies [2afb612] - - @objectstack/spec@9.11.0 - - @objectstack/objectql@9.11.0 - - @objectstack/runtime@9.11.0 - - @objectstack/core@9.11.0 - - @objectstack/types@9.11.0 - -## 9.10.0 - -### Patch Changes - -- Updated dependencies [db02bd5] -- Updated dependencies [641675d] -- Updated dependencies [94e9040] -- Updated dependencies [1f88fd9] -- Updated dependencies [1f88fd9] -- Updated dependencies [e2b5324] -- Updated dependencies [fd07027] - - @objectstack/spec@9.10.0 - - @objectstack/objectql@9.10.0 - - @objectstack/runtime@9.10.0 - - @objectstack/core@9.10.0 - - @objectstack/types@9.10.0 - -## 9.9.1 - -### Patch Changes - -- @objectstack/spec@9.9.1 -- @objectstack/core@9.9.1 -- @objectstack/types@9.9.1 -- @objectstack/objectql@9.9.1 -- @objectstack/runtime@9.9.1 - -## 9.9.0 - -### Patch Changes - -- Updated dependencies [84249a4] -- Updated dependencies [44c5348] -- Updated dependencies [11af299] -- Updated dependencies [d5774b5] -- Updated dependencies [bfa3102] -- Updated dependencies [83fd318] -- Updated dependencies [134043a] -- Updated dependencies [67c29ee] -- Updated dependencies [90108e0] -- Updated dependencies [9afeb2d] -- Updated dependencies [6bec07e] -- Updated dependencies [601cc11] -- Updated dependencies [d99a75a] -- Updated dependencies [575448d] - - @objectstack/spec@9.9.0 - - @objectstack/objectql@9.9.0 - - @objectstack/runtime@9.9.0 - - @objectstack/core@9.9.0 - - @objectstack/types@9.9.0 - -## 9.8.0 - -### Patch Changes - -- Updated dependencies [76ac582] -- Updated dependencies [97c55b3] -- Updated dependencies [1b1f490] -- Updated dependencies [884bf2f] - - @objectstack/objectql@9.8.0 - - @objectstack/spec@9.8.0 - - @objectstack/runtime@9.8.0 - - @objectstack/core@9.8.0 - - @objectstack/types@9.8.0 - -## 9.7.0 - -### Patch Changes - -- @objectstack/objectql@9.7.0 -- @objectstack/runtime@9.7.0 -- @objectstack/spec@9.7.0 -- @objectstack/core@9.7.0 -- @objectstack/types@9.7.0 - -## 9.6.0 - -### Patch Changes - -- Updated dependencies [d1e930a] -- Updated dependencies [71578f2] -- Updated dependencies [5e3a301] -- Updated dependencies [5db2742] -- Updated dependencies [b04b7e3] -- Updated dependencies [d13df3f] - - @objectstack/spec@9.6.0 - - @objectstack/objectql@9.6.0 - - @objectstack/runtime@9.6.0 - - @objectstack/core@9.6.0 - - @objectstack/types@9.6.0 - -## 9.5.1 - -### Patch Changes - -- Updated dependencies [ee72aae] - - @objectstack/spec@9.5.1 - - @objectstack/core@9.5.1 - - @objectstack/objectql@9.5.1 - - @objectstack/runtime@9.5.1 - - @objectstack/types@9.5.1 - -## 9.5.0 - -### Patch Changes - -- Updated dependencies [d08551c] -- Updated dependencies [707aeed] -- Updated dependencies [7a103d4] -- Updated dependencies [4b01250] - - @objectstack/spec@9.5.0 - - @objectstack/core@9.5.0 - - @objectstack/objectql@9.5.0 - - @objectstack/runtime@9.5.0 - - @objectstack/types@9.5.0 - -## 9.4.0 - -### Patch Changes - -- Updated dependencies [060467a] -- Updated dependencies [c1dfe34] -- Updated dependencies [0856476] -- Updated dependencies [fef38ec] -- Updated dependencies [3e675f6] -- Updated dependencies [b678d8c] -- Updated dependencies [b678d8c] -- Updated dependencies [b678d8c] -- Updated dependencies [b678d8c] -- Updated dependencies [b678d8c] -- Updated dependencies [b678d8c] - - @objectstack/spec@9.4.0 - - @objectstack/objectql@9.4.0 - - @objectstack/runtime@9.4.0 - - @objectstack/core@9.4.0 - - @objectstack/types@9.4.0 - -## 9.3.0 - -### Patch Changes - -- Updated dependencies [1ada658] -- Updated dependencies [6259882] -- Updated dependencies [3219191] -- Updated dependencies [290f631] -- Updated dependencies [50b7b47] -- Updated dependencies [f15d6f6] -- Updated dependencies [f8684ea] -- Updated dependencies [b4765be] -- Updated dependencies [b10aa78] -- Updated dependencies [2796a1f] - - @objectstack/spec@9.3.0 - - @objectstack/objectql@9.3.0 - - @objectstack/runtime@9.3.0 - - @objectstack/core@9.3.0 - - @objectstack/types@9.3.0 - -## 9.2.0 - -### Patch Changes - -- Updated dependencies [2f57b75] -- Updated dependencies [2f57b75] - - @objectstack/spec@9.2.0 - - @objectstack/core@9.2.0 - - @objectstack/objectql@9.2.0 - - @objectstack/runtime@9.2.0 - - @objectstack/types@9.2.0 - -## 9.1.0 - -### Patch Changes - -- Updated dependencies [b9062c9] - - @objectstack/spec@9.1.0 - - @objectstack/core@9.1.0 - - @objectstack/objectql@9.1.0 - - @objectstack/runtime@9.1.0 - - @objectstack/types@9.1.0 - -## 9.0.1 - -### Patch Changes - -- Updated dependencies [1817845] - - @objectstack/spec@9.0.1 - - @objectstack/core@9.0.1 - - @objectstack/objectql@9.0.1 - - @objectstack/runtime@9.0.1 - - @objectstack/types@9.0.1 - -## 9.0.0 - -### Patch Changes - -- Updated dependencies [4c3f693] -- Updated dependencies [0bf39f1] -- Updated dependencies [f533f42] -- Updated dependencies [1c83ee8] - - @objectstack/spec@9.0.0 - - @objectstack/core@9.0.0 - - @objectstack/objectql@9.0.0 - - @objectstack/runtime@9.0.0 - - @objectstack/types@9.0.0 - -## 8.0.1 - -### Patch Changes - -- @objectstack/spec@8.0.1 -- @objectstack/core@8.0.1 -- @objectstack/types@8.0.1 -- @objectstack/objectql@8.0.1 -- @objectstack/runtime@8.0.1 - -## 8.0.0 - -### Patch Changes - -- Updated dependencies [a46c017] -- Updated dependencies [f68be58] -- Updated dependencies [b990b89] -- Updated dependencies [99111ec] -- Updated dependencies [d5a8161] -- Updated dependencies [5cf1f1b] -- Updated dependencies [9ef89d4] -- Updated dependencies [bc0d85b] -- Updated dependencies [2537e28] -- Updated dependencies [0ec7717] -- Updated dependencies [e6374b5] -- Updated dependencies [3306d2f] -- Updated dependencies [c262301] -- Updated dependencies [bc44195] -- Updated dependencies [9e2e229] -- Updated dependencies [345e189] - - @objectstack/spec@8.0.0 - - @objectstack/runtime@8.0.0 - - @objectstack/objectql@8.0.0 - - @objectstack/core@8.0.0 - - @objectstack/types@8.0.0 - -## 7.9.0 - -### Patch Changes - -- Updated dependencies [ac1fc4c] -- Updated dependencies [ac1fc4c] -- Updated dependencies [ac1fc4c] - - @objectstack/objectql@7.9.0 - - @objectstack/runtime@7.9.0 - - @objectstack/spec@7.9.0 - - @objectstack/core@7.9.0 - - @objectstack/types@7.9.0 - -## 7.8.0 - -### Patch Changes - -- Updated dependencies [06f2bbb] -- Updated dependencies [a75823a] -- Updated dependencies [4fbb86a] -- Updated dependencies [e631f1e] -- Updated dependencies [6fc2678] -- Updated dependencies [36719db] -- Updated dependencies [424ab26] - - @objectstack/spec@7.8.0 - - @objectstack/objectql@7.8.0 - - @objectstack/runtime@7.8.0 - - @objectstack/core@7.8.0 - - @objectstack/types@7.8.0 - -## 7.7.0 - -### Patch Changes - -- Updated dependencies [b391955] -- Updated dependencies [f06b64e] -- Updated dependencies [023bf93] -- Updated dependencies [764c747] - - @objectstack/spec@7.7.0 - - @objectstack/objectql@7.7.0 - - @objectstack/core@7.7.0 - - @objectstack/runtime@7.7.0 - - @objectstack/types@7.7.0 - -## 7.6.0 - -### Patch Changes - -- 3377e38: fix(release): stop the fixed-group major cascade caused by internal `@objectstack/*` peerDependencies. - - These packages declared workspace peerDependencies on other framework packages - in the changesets `fixed` group. Inside a fixed group, changesets rewrites those - peer ranges on every release and treats a peer-range change as breaking → major, - which cascaded to **all 69 packages → 8.0.0** on _any_ minor changeset. Required - internal peers are now regular `dependencies`; optional ones move to - `devDependencies` (kept for in-workspace tests, no longer a published peer edge). - Releases now bump correctly (patch/minor) instead of a spurious major. - -- Updated dependencies [955d4c8] -- Updated dependencies [c4a4cbd] -- Updated dependencies [b046ec2] -- Updated dependencies [2170ad9] -- Updated dependencies [02d6359] -- Updated dependencies [7648242] -- Updated dependencies [8fa1e7f] -- Updated dependencies [55866f5] -- Updated dependencies [8e539cc] -- Updated dependencies [60f9c45] - - @objectstack/spec@7.6.0 - - @objectstack/objectql@7.6.0 - - @objectstack/runtime@7.6.0 - - @objectstack/core@7.6.0 - - @objectstack/types@7.6.0 - -## 7.5.0 - -### Patch Changes - -- @objectstack/spec@7.5.0 -- @objectstack/core@7.5.0 -- @objectstack/types@7.5.0 -- @objectstack/objectql@7.5.0 - -## 7.4.1 - -### Patch Changes - -- @objectstack/spec@7.4.1 -- @objectstack/core@7.4.1 -- @objectstack/types@7.4.1 -- @objectstack/objectql@7.4.1 - -## 7.4.0 - -### Patch Changes - -- Updated dependencies [23c7107] -- Updated dependencies [c72daad] -- Updated dependencies [eea3f1b] -- Updated dependencies [f115182] -- Updated dependencies [2faf9f2] -- Updated dependencies [2faf9f2] -- Updated dependencies [2faf9f2] -- Updated dependencies [2faf9f2] -- Updated dependencies [a6d4cbb] -- Updated dependencies [58b450b] -- Updated dependencies [82eb6cf] -- Updated dependencies [13d8653] -- Updated dependencies [ff3d006] -- Updated dependencies [5e831de] - - @objectstack/spec@7.4.0 - - @objectstack/objectql@7.4.0 - - @objectstack/core@7.4.0 - - @objectstack/types@7.4.0 - -## 7.3.0 - -### Patch Changes - -- Updated dependencies [5e7c554] - - @objectstack/spec@7.3.0 - - @objectstack/core@7.3.0 - - @objectstack/objectql@7.3.0 - - @objectstack/types@7.3.0 - -## 7.2.1 - -### Patch Changes - -- Updated dependencies [9096dfe] - - @objectstack/types@7.2.1 - - @objectstack/objectql@7.2.1 - - @objectstack/spec@7.2.1 - - @objectstack/core@7.2.1 - -## 7.2.0 - -### Patch Changes - -- @objectstack/spec@7.2.0 -- @objectstack/core@7.2.0 -- @objectstack/types@7.2.0 -- @objectstack/objectql@7.2.0 - -## 7.1.0 - -### Patch Changes - -- Updated dependencies [47a92f4] - - @objectstack/spec@7.1.0 - - @objectstack/objectql@7.1.0 - - @objectstack/core@7.1.0 - - @objectstack/types@7.1.0 - -## 7.0.0 - -### Patch Changes - -- Updated dependencies [74470ad] -- Updated dependencies [d29617e] -- Updated dependencies [dc72172] -- Updated dependencies [3a630b6] - - @objectstack/spec@7.0.0 - - @objectstack/runtime@7.0.0 - - @objectstack/core@7.0.0 - - @objectstack/objectql@7.0.0 - - @objectstack/types@7.0.0 - -## 6.9.0 - -### Patch Changes - -- @objectstack/spec@6.9.0 -- @objectstack/core@6.9.0 -- @objectstack/types@6.9.0 -- @objectstack/objectql@6.9.0 - -## 6.8.1 - -### Patch Changes - -- @objectstack/spec@6.8.1 -- @objectstack/core@6.8.1 -- @objectstack/types@6.8.1 -- @objectstack/objectql@6.8.1 - -## 6.8.0 - -### Patch Changes - -- Updated dependencies [6e88f77] -- Updated dependencies [c8b9f57] - - @objectstack/spec@6.8.0 - - @objectstack/objectql@6.8.0 - - @objectstack/core@6.8.0 - - @objectstack/types@6.8.0 - -## 6.7.1 - -### Patch Changes - -- @objectstack/spec@6.7.1 -- @objectstack/core@6.7.1 -- @objectstack/types@6.7.1 -- @objectstack/objectql@6.7.1 - -## 6.7.0 - -### Patch Changes - -- Updated dependencies [430067b] -- Updated dependencies [4f9e9d4] - - @objectstack/spec@6.7.0 - - @objectstack/core@6.7.0 - - @objectstack/objectql@6.7.0 - - @objectstack/types@6.7.0 - -## 6.6.0 - -### Patch Changes - -- Updated dependencies [a49cfc2] - - @objectstack/spec@6.6.0 - - @objectstack/core@6.6.0 - - @objectstack/objectql@6.6.0 - - @objectstack/types@6.6.0 - -## 6.5.1 - -### Patch Changes - -- @objectstack/spec@6.5.1 -- @objectstack/core@6.5.1 -- @objectstack/types@6.5.1 -- @objectstack/objectql@6.5.1 - -## 6.5.0 - -### Patch Changes - -- @objectstack/spec@6.5.0 -- @objectstack/core@6.5.0 -- @objectstack/types@6.5.0 -- @objectstack/objectql@6.5.0 - -## 6.4.0 - -### Patch Changes - -- Updated dependencies [f8651cc] -- Updated dependencies [f8651cc] -- Updated dependencies [0bf6f9a] - - @objectstack/spec@6.4.0 - - @objectstack/core@6.4.0 - - @objectstack/objectql@6.4.0 - - @objectstack/types@6.4.0 - -## 6.3.0 - -### Patch Changes - -- @objectstack/spec@6.3.0 -- @objectstack/core@6.3.0 -- @objectstack/types@6.3.0 -- @objectstack/objectql@6.3.0 - -## 6.2.0 - -### Patch Changes - -- Updated dependencies [b4c74a9] - - @objectstack/spec@6.2.0 - - @objectstack/core@6.2.0 - - @objectstack/objectql@6.2.0 - - @objectstack/types@6.2.0 - -## 6.1.1 - -### Patch Changes - -- @objectstack/spec@6.1.1 -- @objectstack/core@6.1.1 -- @objectstack/types@6.1.1 -- @objectstack/objectql@6.1.1 - -## 6.1.0 - -### Patch Changes - -- Updated dependencies [93c0589] - - @objectstack/spec@6.1.0 - - @objectstack/core@6.1.0 - - @objectstack/objectql@6.1.0 - - @objectstack/types@6.1.0 - -## 6.0.0 - -### Patch Changes - -- Updated dependencies [629a716] -- Updated dependencies [dbc4f7d] -- Updated dependencies [944f187] - - @objectstack/spec@6.0.0 - - @objectstack/runtime@6.0.0 - - @objectstack/core@6.0.0 - - @objectstack/objectql@6.0.0 - - @objectstack/types@6.0.0 - -## 5.2.0 - -### Patch Changes - -- Updated dependencies [bab2b20] -- Updated dependencies [fa011d8] -- Updated dependencies [b806f58] - - @objectstack/spec@5.2.0 - - @objectstack/core@5.2.0 - - @objectstack/objectql@5.2.0 - - @objectstack/types@5.2.0 - -## 5.1.0 - -### Patch Changes - -- Updated dependencies [75f4ee6] -- Updated dependencies [823d559] - - @objectstack/spec@5.1.0 - - @objectstack/objectql@5.1.0 - - @objectstack/core@5.1.0 - - @objectstack/types@5.1.0 - -## 5.0.0 - -### Patch Changes - -- Updated dependencies [5e9dcb4] -- Updated dependencies [f139a24] -- Updated dependencies [4eb9f8c] -- Updated dependencies [2f7e42a] -- Updated dependencies [602cce7] -- Updated dependencies [1e625b8] -- Updated dependencies [6ee42b8] -- Updated dependencies [888a5c1] -- Updated dependencies [5cfdc85] -- Updated dependencies [09f005a] -- Updated dependencies [7825394] -- Updated dependencies [96ad4df] -- Updated dependencies [df18ae9] -- Updated dependencies [2f9073a] - - @objectstack/objectql@5.0.0 - - @objectstack/runtime@5.0.0 - - @objectstack/spec@5.0.0 - - @objectstack/core@5.0.0 - - @objectstack/types@5.0.0 - -## 4.2.0 - -### Patch Changes - -- Updated dependencies [2869891] - - @objectstack/spec@4.2.0 - - @objectstack/objectql@4.2.0 - - @objectstack/core@4.2.0 - - @objectstack/types@4.2.0 - -## 4.1.1 - -### Patch Changes - -- @objectstack/spec@4.1.1 -- @objectstack/core@4.1.1 -- @objectstack/types@4.1.1 -- @objectstack/objectql@4.1.1 - -## 4.1.0 - -### Patch Changes - -- Updated dependencies [2108c30] -- Updated dependencies [23db640] -- Updated dependencies [5683206] -- Updated dependencies [f0b3972] -- Updated dependencies [0e63f2f] - - @objectstack/spec@4.1.0 - - @objectstack/objectql@4.1.0 - - @objectstack/core@4.1.0 - - @objectstack/types@4.1.0 - -## 4.0.5 - -### Patch Changes - -- 15e0df6: chore: unify all package versions to a single patch release -- Updated dependencies [15e0df6] - - @objectstack/spec@4.0.5 - - @objectstack/core@4.0.5 - - @objectstack/types@4.0.5 - - @objectstack/objectql@4.0.5 - -## 4.0.4 - -### Patch Changes - -- Updated dependencies [326b66b] - - @objectstack/spec@4.0.4 - - @objectstack/core@4.0.4 - - @objectstack/objectql@4.0.4 - - @objectstack/runtime@4.0.4 - - @objectstack/types@4.0.4 - -## 4.0.3 - -### Patch Changes - -- @objectstack/spec@4.0.3 -- @objectstack/core@4.0.3 -- @objectstack/types@4.0.3 -- @objectstack/objectql@4.0.3 -- @objectstack/runtime@4.0.3 - -## 4.0.2 - -### Patch Changes - -- Updated dependencies [5f659e9] - - @objectstack/spec@4.0.2 - - @objectstack/core@4.0.2 - - @objectstack/objectql@4.0.2 - - @objectstack/runtime@4.0.2 - - @objectstack/types@4.0.2 - -## 4.0.0 - -### Patch Changes - -- Updated dependencies [f08ffc3] -- Updated dependencies [e0b0a78] - - @objectstack/spec@4.0.0 - - @objectstack/runtime@4.0.0 - - @objectstack/core@4.0.0 - - @objectstack/objectql@4.0.0 - - @objectstack/types@4.0.0 - -## 3.3.1 - -### Patch Changes - -- @objectstack/spec@3.3.1 -- @objectstack/core@3.3.1 -- @objectstack/types@3.3.1 -- @objectstack/objectql@3.3.1 -- @objectstack/runtime@3.3.1 - -## 3.3.0 - -### Patch Changes - -- @objectstack/spec@3.3.0 -- @objectstack/core@3.3.0 -- @objectstack/types@3.3.0 -- @objectstack/objectql@3.3.0 -- @objectstack/runtime@3.3.0 - -## 3.2.9 - -### Patch Changes - -- Updated dependencies [c3065dd] - - @objectstack/objectql@3.2.9 - - @objectstack/spec@3.2.9 - - @objectstack/core@3.2.9 - - @objectstack/types@3.2.9 - - @objectstack/runtime@3.2.9 - -## 3.2.8 - -### Patch Changes - -- @objectstack/spec@3.2.8 -- @objectstack/core@3.2.8 -- @objectstack/types@3.2.8 -- @objectstack/objectql@3.2.8 -- @objectstack/runtime@3.2.8 - -## 3.2.7 - -### Patch Changes - -- @objectstack/spec@3.2.7 -- @objectstack/core@3.2.7 -- @objectstack/types@3.2.7 -- @objectstack/objectql@3.2.7 -- @objectstack/runtime@3.2.7 - -## 3.2.6 - -### Patch Changes - -- @objectstack/spec@3.2.6 -- @objectstack/core@3.2.6 -- @objectstack/types@3.2.6 -- @objectstack/objectql@3.2.6 -- @objectstack/runtime@3.2.6 - -## 3.2.5 - -### Patch Changes - -- @objectstack/spec@3.2.5 -- @objectstack/core@3.2.5 -- @objectstack/types@3.2.5 -- @objectstack/objectql@3.2.5 -- @objectstack/runtime@3.2.5 - -## 3.2.4 - -### Patch Changes - -- @objectstack/spec@3.2.4 -- @objectstack/core@3.2.4 -- @objectstack/types@3.2.4 -- @objectstack/objectql@3.2.4 -- @objectstack/runtime@3.2.4 - -## 3.2.3 - -### Patch Changes - -- @objectstack/spec@3.2.3 -- @objectstack/core@3.2.3 -- @objectstack/types@3.2.3 -- @objectstack/objectql@3.2.3 -- @objectstack/runtime@3.2.3 - -## 3.2.2 - -### Patch Changes - -- Updated dependencies [46defbb] - - @objectstack/spec@3.2.2 - - @objectstack/core@3.2.2 - - @objectstack/objectql@3.2.2 - - @objectstack/runtime@3.2.2 - - @objectstack/types@3.2.2 - -## 3.2.1 - -### Patch Changes - -- Updated dependencies [850b546] - - @objectstack/spec@3.2.1 - - @objectstack/core@3.2.1 - - @objectstack/objectql@3.2.1 - - @objectstack/runtime@3.2.1 - - @objectstack/types@3.2.1 - -## 3.2.0 - -### Patch Changes - -- Updated dependencies [5901c29] - - @objectstack/spec@3.2.0 - - @objectstack/core@3.2.0 - - @objectstack/objectql@3.2.0 - - @objectstack/runtime@3.2.0 - - @objectstack/types@3.2.0 - -## 3.1.1 - -### Patch Changes - -- Updated dependencies [953d667] - - @objectstack/spec@3.1.1 - - @objectstack/core@3.1.1 - - @objectstack/objectql@3.1.1 - - @objectstack/runtime@3.1.1 - - @objectstack/types@3.1.1 - -## 3.1.0 - -### Patch Changes - -- Updated dependencies [0088830] - - @objectstack/spec@3.1.0 - - @objectstack/core@3.1.0 - - @objectstack/objectql@3.1.0 - - @objectstack/runtime@3.1.0 - - @objectstack/types@3.1.0 - -## 3.0.11 - -### Patch Changes - -- Updated dependencies [92d9d99] - - @objectstack/spec@3.0.11 - - @objectstack/core@3.0.11 - - @objectstack/objectql@3.0.11 - - @objectstack/runtime@3.0.11 - - @objectstack/types@3.0.11 - -## 3.0.10 - -### Patch Changes - -- Updated dependencies [d1e5d31] - - @objectstack/spec@3.0.10 - - @objectstack/core@3.0.10 - - @objectstack/objectql@3.0.10 - - @objectstack/runtime@3.0.10 - - @objectstack/types@3.0.10 - -## 3.0.9 - -### Patch Changes - -- Updated dependencies [15e0df6] - - @objectstack/spec@3.0.9 - - @objectstack/core@3.0.9 - - @objectstack/objectql@3.0.9 - - @objectstack/runtime@3.0.9 - - @objectstack/types@3.0.9 - -## 3.0.8 - -### Patch Changes - -- Updated dependencies [5a968a2] - - @objectstack/spec@3.0.8 - - @objectstack/core@3.0.8 - - @objectstack/objectql@3.0.8 - - @objectstack/runtime@3.0.8 - - @objectstack/types@3.0.8 - -## 3.0.7 - -### Patch Changes - -- Updated dependencies [0119bd7] -- Updated dependencies [5426bdf] - - @objectstack/spec@3.0.7 - - @objectstack/core@3.0.7 - - @objectstack/objectql@3.0.7 - - @objectstack/runtime@3.0.7 - - @objectstack/types@3.0.7 - -## 3.0.6 - -### Patch Changes - -- Updated dependencies [5df254c] - - @objectstack/spec@3.0.6 - - @objectstack/core@3.0.6 - - @objectstack/objectql@3.0.6 - - @objectstack/runtime@3.0.6 - - @objectstack/types@3.0.6 - -## 3.0.5 - -### Patch Changes - -- Updated dependencies [23a4a68] - - @objectstack/spec@3.0.5 - - @objectstack/core@3.0.5 - - @objectstack/objectql@3.0.5 - - @objectstack/runtime@3.0.5 - - @objectstack/types@3.0.5 - -## 3.0.4 - -### Patch Changes - -- Updated dependencies [d738987] -- Updated dependencies [437b0b8] - - @objectstack/spec@3.0.4 - - @objectstack/objectql@3.0.4 - - @objectstack/core@3.0.4 - - @objectstack/runtime@3.0.4 - - @objectstack/types@3.0.4 - -## 3.0.3 - -### Patch Changes - -- c7267f6: Patch release for maintenance updates and improvements. -- Updated dependencies [c7267f6] - - @objectstack/spec@3.0.3 - - @objectstack/core@3.0.3 - - @objectstack/types@3.0.3 - - @objectstack/objectql@3.0.3 - - @objectstack/runtime@3.0.3 - -## 3.0.2 - -### Patch Changes - -- Updated dependencies [28985f5] - - @objectstack/spec@3.0.2 - - @objectstack/core@3.0.2 - - @objectstack/objectql@3.0.2 - - @objectstack/runtime@3.0.2 - - @objectstack/types@3.0.2 - -## 3.0.1 - -### Patch Changes - -- Updated dependencies [389725a] - - @objectstack/spec@3.0.1 - - @objectstack/core@3.0.1 - - @objectstack/objectql@3.0.1 - - @objectstack/runtime@3.0.1 - - @objectstack/types@3.0.1 - -## 3.0.0 - -### Major Changes - -- Release v3.0.0 — unified version bump for all ObjectStack packages. - -### Patch Changes - -- Updated dependencies - - @objectstack/spec@3.0.0 - - @objectstack/core@3.0.0 - - @objectstack/types@3.0.0 - - @objectstack/objectql@3.0.0 - - @objectstack/runtime@3.0.0 - -## 2.0.7 - -### Patch Changes - -- Updated dependencies - - @objectstack/spec@2.0.7 - - @objectstack/core@2.0.7 - - @objectstack/objectql@2.0.7 - - @objectstack/runtime@2.0.7 - - @objectstack/types@2.0.7 - -## 2.0.6 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/spec@2.0.6 - - @objectstack/core@2.0.6 - - @objectstack/types@2.0.6 - - @objectstack/objectql@2.0.6 - - @objectstack/runtime@2.0.6 - -## 2.0.5 - -### Patch Changes - -- Updated dependencies - - @objectstack/spec@2.0.5 - - @objectstack/core@2.0.5 - - @objectstack/objectql@2.0.5 - - @objectstack/runtime@2.0.5 - - @objectstack/types@2.0.5 - -## 2.0.4 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/spec@2.0.4 - - @objectstack/core@2.0.4 - - @objectstack/types@2.0.4 - - @objectstack/objectql@2.0.4 - - @objectstack/runtime@2.0.4 - -## 2.0.3 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/spec@2.0.3 - - @objectstack/core@2.0.3 - - @objectstack/types@2.0.3 - - @objectstack/objectql@2.0.3 - - @objectstack/runtime@2.0.3 - -## 2.0.2 - -### Patch Changes - -- Updated dependencies [1db8559] - - @objectstack/spec@2.0.2 - - @objectstack/core@2.0.2 - - @objectstack/objectql@2.0.2 - - @objectstack/runtime@2.0.2 - - @objectstack/types@2.0.2 - -## 2.0.1 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/spec@2.0.1 - - @objectstack/core@2.0.1 - - @objectstack/types@2.0.1 - - @objectstack/objectql@2.0.1 - - @objectstack/runtime@2.0.1 - -## 2.0.0 - -### Patch Changes - -- Updated dependencies [38e5dd5] -- Updated dependencies [38e5dd5] - - @objectstack/spec@2.0.0 - - @objectstack/core@2.0.0 - - @objectstack/objectql@2.0.0 - - @objectstack/runtime@2.0.0 - - @objectstack/types@2.0.0 - -## 1.0.12 - -### Patch Changes - -- Updated dependencies - - @objectstack/spec@1.0.12 - - @objectstack/core@1.0.12 - - @objectstack/runtime@1.0.12 - - @objectstack/objectql@1.0.12 - - @objectstack/types@1.0.12 - -## 1.0.11 - -### Patch Changes - -- 3a9b0f6: Fix metadata path compatibility by rebuilding with latest runtime changes. - - @objectstack/spec@1.0.11 - - @objectstack/core@1.0.11 - - @objectstack/types@1.0.11 - - @objectstack/objectql@1.0.11 - - @objectstack/runtime@1.0.11 - -## 1.0.10 - -### Patch Changes - -- Updated dependencies [10f52e1] - - @objectstack/core@1.0.10 - - @objectstack/objectql@1.0.10 - - @objectstack/runtime@1.0.10 - - @objectstack/spec@1.0.10 - - @objectstack/types@1.0.10 - -## 1.0.9 - -### Patch Changes - -- Updated dependencies [b9f8c68] - - @objectstack/objectql@1.0.9 - - @objectstack/spec@1.0.9 - - @objectstack/core@1.0.9 - - @objectstack/types@1.0.9 - - @objectstack/runtime@1.0.9 - -## 1.0.8 - -### Patch Changes - -- 8f2a3a2: fix: standardize discovery endpoint response to include 'data' wrapper - - @objectstack/spec@1.0.8 - - @objectstack/core@1.0.8 - - @objectstack/types@1.0.8 - - @objectstack/objectql@1.0.8 - - @objectstack/runtime@1.0.8 - -## 1.0.7 - -### Patch Changes - -- ebdf787: feat: implement standard service discovery via `/.well-known/objectstack` -- Updated dependencies [ebdf787] - - @objectstack/runtime@1.0.7 - - @objectstack/spec@1.0.7 - - @objectstack/core@1.0.7 - - @objectstack/types@1.0.7 - - @objectstack/objectql@1.0.7 - -## 1.0.6 - -### Patch Changes - -- Updated dependencies [a7f7b9d] - - @objectstack/spec@1.0.6 - - @objectstack/core@1.0.6 - - @objectstack/objectql@1.0.6 - - @objectstack/runtime@1.0.6 - - @objectstack/types@1.0.6 - -## 1.0.5 - -### Patch Changes - -- b1d24bd: refactor: migrate build system from tsc to tsup for faster builds - - Replaced `tsc` with `tsup` (using esbuild) across all packages - - Added shared `tsup.config.ts` in workspace root - - Added `tsup` as workspace dev dependency - - significantly improved build performance -- 877b864: fix: add SPA fallback to hono, fix msw context binding, improve runtime resilience, and fix client-react build types -- Updated dependencies [b1d24bd] -- Updated dependencies [877b864] - - @objectstack/core@1.0.5 - - @objectstack/objectql@1.0.5 - - @objectstack/runtime@1.0.5 - - @objectstack/types@1.0.5 - - @objectstack/spec@1.0.5 - -## 1.0.4 - -### Patch Changes - -- Updated dependencies [5d13533] - - @objectstack/objectql@1.0.4 - - @objectstack/spec@1.0.4 - - @objectstack/core@1.0.4 - - @objectstack/types@1.0.4 - - @objectstack/runtime@1.0.4 - -## 1.0.3 - -### Patch Changes - -- Updated dependencies [fb2eabd] -- Updated dependencies [22a48f0] - - @objectstack/core@1.0.3 - - @objectstack/runtime@1.0.3 - - @objectstack/objectql@1.0.3 - - @objectstack/spec@1.0.3 - - @objectstack/types@1.0.3 - -## 1.0.2 - -### Patch Changes - -- a0a6c85: Infrastructure and development tooling improvements - - - Add changeset configuration for automated version management - - Add comprehensive GitHub Actions workflows (CI, CodeQL, linting, releases) - - Add development configuration files (.cursorrules, .github/prompts) - - Add documentation files (ARCHITECTURE.md, CONTRIBUTING.md, workflows docs) - - Update test script configuration in package.json - - Add @objectstack/cli to devDependencies for better development experience - -- 109fc5b: Unified patch release to align all package versions. -- Updated dependencies [a0a6c85] -- Updated dependencies [109fc5b] - - @objectstack/spec@1.0.2 - - @objectstack/core@1.0.2 - - @objectstack/types@1.0.2 - - @objectstack/objectql@1.0.2 - - @objectstack/runtime@1.0.2 - -## 1.0.1 - -### Patch Changes - -- Updated dependencies - - @objectstack/runtime@1.0.1 - - @objectstack/spec@1.0.1 - - @objectstack/types@1.0.1 - - @objectstack/objectql@1.0.1 - -## 1.0.0 - -### Major Changes - -- Major version release for ObjectStack Protocol v1.0. - - Stabilized Protocol Definitions - - Enhanced Runtime Plugin Support - - Fixed Type Compliance across Monorepo - -### Patch Changes - -- Updated dependencies - - @objectstack/spec@1.0.0 - - @objectstack/runtime@1.0.0 - - @objectstack/objectql@1.0.0 - - @objectstack/types@1.0.0 - -## 0.9.2 - -### Patch Changes - -- Updated dependencies - - @objectstack/spec@0.9.2 - - @objectstack/objectql@0.9.2 - - @objectstack/runtime@0.9.2 - - @objectstack/types@0.9.2 - -## 0.9.1 - -### Patch Changes - -- Patch release for maintenance and stability improvements. All packages updated with unified versioning. -- Updated dependencies - - @objectstack/spec@0.9.1 - - @objectstack/types@0.9.1 - - @objectstack/objectql@0.9.1 - - @objectstack/runtime@0.9.1 - -## 0.8.2 - -### Patch Changes - -- 555e6a7: Refactor: Deprecated View Storage protocol in favor of Metadata Views. - - - **BREAKING**: Removed `view-storage.zod.ts` and `ViewStorage` related types from `@objectstack/spec`. - - **BREAKING**: Removed `createView`, `updateView`, `deleteView`, `listViews` from `ObjectStackProtocol` interface. - - **BREAKING**: Removed in-memory View Storage implementation from `@objectstack/objectql`. - - **UPDATE**: `@objectstack/plugin-msw` now dynamically loads `@objectstack/objectql` to avoid hard dependencies. - -- Updated dependencies [555e6a7] - - @objectstack/spec@0.8.2 - - @objectstack/objectql@0.8.2 - - @objectstack/runtime@0.8.2 - - @objectstack/types@0.8.2 - -## 0.8.1 - -### Patch Changes - -- @objectstack/spec@0.8.1 -- @objectstack/types@0.8.1 -- @objectstack/objectql@0.8.1 -- @objectstack/runtime@0.8.1 - -## 1.0.0 - -### Minor Changes - -- # Upgrade to Zod v4 and Protocol Improvements - - This release includes a major upgrade to the core validation engine (Zod v4) and aligns all protocol definitions with stricter type safety. - -### Patch Changes - -- Updated dependencies - - @objectstack/spec@1.0.0 - - @objectstack/types@1.0.0 - - @objectstack/objectql@1.0.0 - - @objectstack/runtime@1.0.0 - -## 0.7.2 - -### Patch Changes - -- fb41cc0: Patch release: Updated documentation and JSON schemas -- Updated dependencies [fb41cc0] - - @objectstack/spec@0.7.2 - - @objectstack/types@0.7.2 - - @objectstack/objectql@0.7.2 - - @objectstack/runtime@0.7.2 - -## 0.7.1 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/spec@0.7.1 - - @objectstack/objectql@0.7.1 - - @objectstack/runtime@0.7.1 - - @objectstack/types@0.7.1 - -## 0.6.1 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/spec@0.6.1 - - @objectstack/objectql@0.6.1 - - @objectstack/runtime@0.6.1 - - @objectstack/types@0.6.1 - -## 0.6.0 - -### Minor Changes - -- b2df5f7: Unified version bump to 0.5.0 - - - Standardized all package versions to 0.5.0 across the monorepo - - Fixed driver-memory package.json paths for proper module resolution - - Ensured all packages are in sync for the 0.5.0 release - -### Patch Changes - -- Updated dependencies [b2df5f7] - - @objectstack/spec@0.6.0 - - @objectstack/runtime@0.6.0 - - @objectstack/types@0.6.0 - -## 0.4.2 - -### Patch Changes - -- Unify all package versions to 0.4.2 -- Updated dependencies - - @objectstack/spec@0.4.2 - - @objectstack/runtime@0.4.2 - - @objectstack/types@0.4.2 - -## 0.4.1 - -### Patch Changes - -- Version synchronization and dependency updates - - - Synchronized plugin-msw version to 0.4.1 - - Updated runtime peer dependency versions to ^0.4.1 - - Fixed internal dependency version mismatches - -- Updated dependencies - - @objectstack/spec@0.4.1 - - @objectstack/types@0.4.1 - - @objectstack/runtime@0.4.1 - -## 0.3.3 - -### Patch Changes - -- Updated dependencies - - @objectstack/spec@0.3.3 - - @objectstack/runtime@0.3.3 - - @objectstack/types@0.3.3 - -## 0.3.2 - -### Patch Changes - -- Patch release for maintenance and stability improvements -- Updated dependencies - - @objectstack/runtime@0.3.2 - - @objectstack/spec@0.3.2 - - @objectstack/types@0.3.2 - -## 0.3.1 - -### Added - -- Initial release of MSW plugin for ObjectStack -- `MSWPlugin` class implementing RuntimePlugin interface -- `ObjectStackServer` mock server for handling API calls -- Automatic generation of MSW handlers for ObjectStack API endpoints -- Support for browser and Node.js environments -- Custom handler support -- Comprehensive documentation and examples -- TypeScript type definitions - -### Features - -- Discovery endpoint mocking -- Metadata endpoint mocking -- Data CRUD operation mocking -- UI protocol endpoint mocking -- Request logging support -- Configurable base URL -- Integration with ObjectStack Runtime Protocol diff --git a/packages/plugins/plugin-msw/LICENSE b/packages/plugins/plugin-msw/LICENSE deleted file mode 100644 index 16bc23f404..0000000000 --- a/packages/plugins/plugin-msw/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute - must include a readable copy of the attribution notices - contained within such NOTICE file, excluding those notices - that do not pertain to any part of the Derivative Works, - in at least one of the following places: within a NOTICE - text file distributed as part of the Derivative Works; within - the Source form or documentation, if provided along with - the Derivative Works; or, within a display generated by the - Derivative Works, if and wherever such third-party notices - normally appear. The contents of the NOTICE file are for - informational purposes only and do not modify the License. - You may add Your own attribution notices within Derivative - Works that You distribute, alongside or as an addendum to - the NOTICE text from the Work, provided that such additional - attribution notices cannot be construed as modifying the - License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2026 ObjectStack - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/plugins/plugin-msw/README.md b/packages/plugins/plugin-msw/README.md deleted file mode 100644 index ac89bd1e82..0000000000 --- a/packages/plugins/plugin-msw/README.md +++ /dev/null @@ -1,240 +0,0 @@ -# @objectstack/plugin-msw - -MSW (Mock Service Worker) Plugin for ObjectStack Runtime. This plugin enables seamless integration with [Mock Service Worker](https://mswjs.io/) for testing and development environments. - -## 🤖 AI Development Context - -**Role**: Test Mocking Adapter -**Usage**: -- Intercepts network requests in tests (Browser/Node). -- Simulates a real ObjectStack backend. - -## Plugin Capabilities - -This plugin implements the ObjectStack plugin capability protocol: -- **Protocol**: `com.objectstack.protocol.testing.mock.v1` (full conformance) -- **Protocol**: `com.objectstack.protocol.api.rest.v1` (full conformance) -- **Provides**: `ObjectStackServer` interface for mock API operations -- **Requires**: `com.objectstack.engine.objectql` for data operations - -See [objectstack.config.ts](./objectstack.config.ts) for the complete capability manifest. - -## Features - -- 🎯 **Unified Dispatcher**: Uses `@objectstack/runtime`'s `HttpDispatcher` to ensure mocks behave exactly like the real server. -- 🔄 **Full Protocol Support**: Mocks **all** Runtime endpoints: - - Auth (`/auth`) - - Metadata (`/metadata`) - - Data (`/data` - with filtering, batching, relations) - - Storage (`/storage`) - - Analytics (`/analytics`) - - Automation (`/automation`) -- 🌐 **Universal Support**: Works in Browser (Service Worker) and Node.js (Interceptor). -- 🎨 **Custom Handlers**: Easily inject custom MSW handlers that take precedence. -- 📝 **TypeScript First**: Fully typed configuration. - -## Installation - -```bash -pnpm add @objectstack/plugin-msw msw -``` - -## Usage - -### With ObjectStack Runtime - -```typescript -import { MSWPlugin } from '@objectstack/plugin-msw'; -import { ObjectKernel } from '@objectstack/runtime'; - -const kernel = new ObjectKernel(); - -// The MSW Plugin will initialize the HttpDispatcher and intercept requests -kernel.use(new MSWPlugin({ - enableBrowser: true, - baseUrl: '/api/v1', - logRequests: true -})); - -await kernel.start(); -``` - -### Architecture - -The plugin uses the `HttpDispatcher` from the Runtime to process requests intercepted by MSW. This means your mock server runs the **actual** ObjectStack business logic (permissions, validation, flow execution) in-memory, providing a high-fidelity simulation of the backend. -})); - -await kernel.bootstrap(); -``` - -### Standalone Usage (Browser) - -```typescript -import { setupWorker } from 'msw/browser'; -import { http, HttpResponse } from 'msw'; -import { ObjectStackServer } from '@objectstack/plugin-msw'; - -// 1. Initialize the mock server -ObjectStackServer.init(protocol); - -// 2. Define your handlers -const handlers = [ - // Intercept GET /api/user/:id - http.get('/api/user/:id', async ({ params }) => { - const result = await ObjectStackServer.getData('user', params.id as string); - return HttpResponse.json(result.data, { status: result.status }); - }), - - // Intercept POST /api/user - http.post('/api/user', async ({ request }) => { - const body = await request.json(); - const result = await ObjectStackServer.createData('user', body); - return HttpResponse.json(result.data, { status: result.status }); - }), -]; - -// 3. Create and start the worker -const worker = setupWorker(...handlers); -await worker.start(); -``` - -### With Custom Handlers - -```typescript -import { MSWPlugin } from '@objectstack/plugin-msw'; -import { http, HttpResponse } from 'msw'; - -const customHandlers = [ - http.get('/api/custom/:id', ({ params }) => { - return HttpResponse.json({ id: params.id, custom: true }); - }) -]; - -const plugin = new MSWPlugin({ - customHandlers, - baseUrl: '/api/v1' -}); -``` - -## API Reference - -### MSWPlugin - -The main plugin class that implements the ObjectStack Runtime Plugin interface. - -#### Options - -```typescript -interface MSWPluginOptions { - /** - * Enable MSW in the browser environment - * @default true - */ - enableBrowser?: boolean; - - /** - * Custom handlers to add to MSW - */ - customHandlers?: Array; - - /** - * Base URL for API endpoints - * @default '/api/v1' - */ - baseUrl?: string; - - /** - * Whether to log requests - * @default true - */ - logRequests?: boolean; -} -``` - -### ObjectStackServer - -The mock server that handles ObjectStack API calls. - -#### Static Methods - -- `init(protocol, logger?)` - Initialize the mock server with an ObjectStack protocol instance -- `findData(object, params?)` - Find records for an object -- `getData(object, id)` - Get a single record by ID -- `createData(object, data)` - Create a new record -- `updateData(object, id, data)` - Update an existing record -- `deleteData(object, id)` - Delete a record -- `getUser(id)` - Legacy method for getting user (alias for `getData('user', id)`) -- `createUser(data)` - Legacy method for creating user (alias for `createData('user', data)`) - -## Mocked Endpoints - -The plugin automatically mocks the following ObjectStack API endpoints: - -### Discovery -- `GET /api/v1` - Get API discovery information - -### Metadata -- `GET /api/v1/meta` - Get available metadata types -- `GET /api/v1/meta/:type` - Get metadata items for a type -- `GET /api/v1/meta/:type/:name` - Get specific metadata item - -### Data Operations -- `GET /api/v1/data/:object` - Find records -- `GET /api/v1/data/:object/:id` - Get record by ID -- `POST /api/v1/data/:object` - Create record -- `PATCH /api/v1/data/:object/:id` - Update record -- `DELETE /api/v1/data/:object/:id` - Delete record - -### UI Protocol -- `GET /api/v1/ui/view/:object` - Get UI view configuration - -## Example: Testing with Vitest - -```typescript -import { describe, it, expect, beforeAll, afterAll } from 'vitest'; -import { setupWorker } from 'msw/browser'; -import { ObjectStackServer } from '@objectstack/plugin-msw'; -import { http, HttpResponse } from 'msw'; - -describe('User API', () => { - let worker: any; - - beforeAll(async () => { - // Initialize mock server - ObjectStackServer.init(protocol); - - // Setup handlers - const handlers = [ - http.get('/api/user/:id', async ({ params }) => { - const result = await ObjectStackServer.getData('user', params.id as string); - return HttpResponse.json(result.data, { status: result.status }); - }) - ]; - - worker = setupWorker(...handlers); - await worker.start({ onUnhandledRequest: 'bypass' }); - }); - - afterAll(() => { - worker.stop(); - }); - - it('should get user by id', async () => { - const response = await fetch('/api/user/123'); - const data = await response.json(); - - expect(response.status).toBe(200); - expect(data).toBeDefined(); - }); -}); -``` - -## License - -Apache-2.0. See [LICENSING.md](../../../LICENSING.md). - -## Related Packages - -- [@objectstack/runtime](../runtime) - ObjectStack Runtime -- [@objectstack/spec](../spec) - ObjectStack Specifications -- [msw](https://mswjs.io/) - Mock Service Worker diff --git a/packages/plugins/plugin-msw/objectstack.config.ts b/packages/plugins/plugin-msw/objectstack.config.ts deleted file mode 100644 index 0b66841833..0000000000 --- a/packages/plugins/plugin-msw/objectstack.config.ts +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { ObjectStackManifest } from '@objectstack/spec/system'; - -/** - * MSW (Mock Service Worker) Plugin Manifest - * - * Browser-based mock server for testing and development. - * Intercepts HTTP requests and provides mock responses using ObjectStack runtime. - */ -const MSWPlugin: ObjectStackManifest = { - id: 'com.objectstack.plugin.msw', - name: 'Mock Service Worker Plugin', - version: '1.0.0', - type: 'plugin', - scope: 'project', - description: 'MSW (Mock Service Worker) integration for testing and development. Provides browser-based API mocking using ObjectStack runtime protocol.', - - configuration: { - title: 'MSW Plugin Configuration', - properties: { - enableBrowser: { - type: 'boolean', - default: true, - description: 'Enable MSW in browser environment', - }, - baseUrl: { - type: 'string', - default: '/api/v1', - description: 'Base URL for API endpoints', - }, - logRequests: { - type: 'boolean', - default: true, - description: 'Log all intercepted requests', - }, - }, - }, - - // Plugin Capability Declaration - capabilities: { - // Protocols This Plugin Implements - implements: [ - { - protocol: { - id: 'com.objectstack.protocol.testing.mock.v1', - label: 'Mock Service Protocol v1', - version: { major: 1, minor: 0, patch: 0 }, - description: 'HTTP request mocking for testing', - }, - conformance: 'full', - features: [ - { - name: 'browser_mocking', - enabled: true, - description: 'Browser-based request interception', - }, - { - name: 'api_simulation', - enabled: true, - description: 'Full ObjectStack API simulation', - }, - { - name: 'custom_handlers', - enabled: true, - description: 'Custom request handler registration', - }, - ], - certified: false, - }, - { - protocol: { - id: 'com.objectstack.protocol.api.rest.v1', - label: 'REST API Protocol v1', - version: { major: 1, minor: 0, patch: 0 }, - description: 'RESTful API endpoint mocking', - }, - conformance: 'full', - features: [ - { - name: 'meta_endpoints', - enabled: true, - description: 'Metadata discovery endpoints', - }, - { - name: 'data_endpoints', - enabled: true, - description: 'CRUD data operation endpoints', - }, - { - name: 'ui_endpoints', - enabled: true, - description: 'UI view metadata endpoints', - }, - ], - certified: false, - }, - ], - - // Interfaces This Plugin Provides - provides: [ - { - id: 'com.objectstack.plugin.msw.interface.mock_server', - name: 'ObjectStackServer', - description: 'Mock server interface for testing', - version: { major: 1, minor: 0, patch: 0 }, - stability: 'stable', - methods: [ - { - name: 'init', - description: 'Initialize mock server with protocol implementation', - parameters: [ - { - name: 'protocol', - type: 'IObjectStackProtocol', - required: true, - description: 'ObjectStack protocol implementation instance', - }, - { - name: 'logger', - type: 'Logger', - required: false, - description: 'Optional logger instance', - }, - ], - returnType: 'void', - async: false, - }, - { - name: 'findData', - description: 'Mock data find operation', - parameters: [ - { - name: 'object', - type: 'string', - required: true, - description: 'Object name', - }, - { - name: 'params', - type: 'any', - required: false, - description: 'Query parameters', - }, - ], - returnType: 'Promise<{ status: number; data: any }>', - async: true, - }, - { - name: 'getData', - description: 'Mock data get operation', - parameters: [ - { - name: 'object', - type: 'string', - required: true, - description: 'Object name', - }, - { - name: 'id', - type: 'string', - required: true, - description: 'Record ID', - }, - ], - returnType: 'Promise<{ status: number; data: any }>', - async: true, - }, - { - name: 'createData', - description: 'Mock data create operation', - parameters: [ - { - name: 'object', - type: 'string', - required: true, - description: 'Object name', - }, - { - name: 'data', - type: 'any', - required: true, - description: 'Record data', - }, - ], - returnType: 'Promise<{ status: number; data: any }>', - async: true, - }, - { - name: 'updateData', - description: 'Mock data update operation', - parameters: [ - { - name: 'object', - type: 'string', - required: true, - description: 'Object name', - }, - { - name: 'id', - type: 'string', - required: true, - description: 'Record ID', - }, - { - name: 'data', - type: 'any', - required: true, - description: 'Updated record data', - }, - ], - returnType: 'Promise<{ status: number; data: any }>', - async: true, - }, - { - name: 'deleteData', - description: 'Mock data delete operation', - parameters: [ - { - name: 'object', - type: 'string', - required: true, - description: 'Object name', - }, - { - name: 'id', - type: 'string', - required: true, - description: 'Record ID', - }, - ], - returnType: 'Promise<{ status: number; data: any }>', - async: true, - }, - ], - }, - ], - - // Dependencies on Other Plugins/Services - requires: [ - { - pluginId: 'com.objectstack.engine.objectql', - version: '^0.6.0', - optional: false, - reason: 'ObjectQL data engine for mock responses', - requiredCapabilities: [ - 'com.objectstack.protocol.storage.v1', - ], - }, - ], - - // Extension Points This Plugin Defines - extensionPoints: [ - { - id: 'com.objectstack.plugin.msw.extension.custom_handler', - name: 'Custom Request Handlers', - description: 'Register custom MSW request handlers', - type: 'action', - cardinality: 'multiple', - contract: { - input: 'MSWHandler', - description: 'MSW HTTP handler definition', - }, - }, - ], - - // No extensions contributed to other plugins - extensions: [], - }, - - contributes: { - // No specific contributions (runtime plugin) - }, -}; - -export default MSWPlugin; diff --git a/packages/plugins/plugin-msw/package.json b/packages/plugins/plugin-msw/package.json deleted file mode 100644 index af4145b2f7..0000000000 --- a/packages/plugins/plugin-msw/package.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "@objectstack/plugin-msw", - "version": "11.0.0", - "description": "MSW (Mock Service Worker) Plugin for ObjectStack Runtime", - "license": "Apache-2.0", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "require": "./dist/index.js" - } - }, - "scripts": { - "build": "tsup --config ../../../tsup.config.ts", - "test": "vitest run" - }, - "dependencies": { - "@objectstack/core": "workspace:*", - "@objectstack/objectql": "workspace:*", - "@objectstack/spec": "workspace:*", - "@objectstack/types": "workspace:*", - "msw": "^2.14.6", - "@objectstack/runtime": "workspace:^" - }, - "devDependencies": { - "@objectstack/runtime": "workspace:*", - "@types/node": "^26.0.0", - "typescript": "^6.0.3", - "vitest": "^4.1.9" - }, - "keywords": [ - "objectstack", - "plugin", - "msw", - "mock", - "testing" - ], - "author": "ObjectStack", - "repository": { - "type": "git", - "url": "https://github.com/objectstack-ai/framework.git", - "directory": "packages/plugins/plugin-msw" - }, - "homepage": "https://objectstack.ai/docs", - "bugs": "https://github.com/objectstack-ai/framework/issues", - "publishConfig": { - "access": "public" - }, - "files": [ - "dist", - "README.md" - ], - "engines": { - "node": ">=18.0.0" - } -} diff --git a/packages/plugins/plugin-msw/src/index.ts b/packages/plugins/plugin-msw/src/index.ts deleted file mode 100644 index 2299c651c1..0000000000 --- a/packages/plugins/plugin-msw/src/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -/** - * @objectstack/plugin-msw - * - * MSW (Mock Service Worker) Plugin for ObjectStack Runtime - * - * This plugin enables seamless integration with Mock Service Worker for - * testing and development environments. It automatically generates MSW - * handlers for all ObjectStack API endpoints. - * - * @example - * ```typescript - * import { MSWPlugin, ObjectStackServer } from '@objectstack/plugin-msw'; - * import { ObjectStackRuntime } from '@objectstack/runtime'; - * - * // Use with runtime - * const runtime = new ObjectStackRuntime({ - * plugins: [ - * new MSWPlugin({ - * enableBrowser: true, - * baseUrl: '/api/v1' - * }) - * ] - * }); - * - * // Or use standalone in browser - * import { setupWorker, http } from 'msw/browser'; - * - * ObjectStackServer.init(protocol); - * - * const handlers = [ - * http.get('/api/user/:id', async ({ params }) => { - * const result = await ObjectStackServer.getData('user', params.id); - * return HttpResponse.json(result.data, { status: result.status }); - * }) - * ]; - * - * const worker = setupWorker(...handlers); - * await worker.start(); - * ``` - */ - -export { MSWPlugin, ObjectStackServer } from './msw-plugin'; -export type { MSWPluginOptions } from './msw-plugin'; - -// Re-export MSW types for convenience -export type { HttpHandler, HttpResponse } from 'msw'; diff --git a/packages/plugins/plugin-msw/src/msw-plugin.test.ts b/packages/plugins/plugin-msw/src/msw-plugin.test.ts deleted file mode 100644 index c68cbd16c7..0000000000 --- a/packages/plugins/plugin-msw/src/msw-plugin.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { describe, it, expect, vi } from 'vitest'; -import { MSWPlugin, ObjectStackServer } from './msw-plugin'; - -describe('MSWPlugin', () => { - it('should initialize correctly', () => { - const plugin = new MSWPlugin({ enableBrowser: false }); - expect(plugin.name).toBe('com.objectstack.plugin.msw'); - expect(plugin.version).toBe('0.9.0'); - }); - - it('should handle protocol dynamic loading gracefully', async () => { - // Mock context - const context: any = { - logger: { info: vi.fn(), debug: vi.fn(), warn: vi.fn() }, - getService: vi.fn().mockReturnValue(null), // No protocol service initially - registerService: vi.fn() - }; - - const plugin = new MSWPlugin(); - await plugin.init(context); - // It should try to load ObjectStackProtocolImplementation dynamically - // Since we are in test environment, the dynamic import might fail or succeed depending on build - // But we expect it not to crash - }); -}); - -describe('ObjectStackServer', () => { - it('should throw if used before init', async () => { - await expect(ObjectStackServer.findData('test')).rejects.toThrow('ObjectStackServer not initialized'); - }); - - it('should delegate to protocol after init', async () => { - const protocolMock: any = { - findData: vi.fn().mockResolvedValue({ records: [] }) - }; - ObjectStackServer.init(protocolMock); - - await ObjectStackServer.findData('test'); - expect(protocolMock.findData).toHaveBeenCalled(); - }); -}); diff --git a/packages/plugins/plugin-msw/src/msw-plugin.ts b/packages/plugins/plugin-msw/src/msw-plugin.ts deleted file mode 100644 index be480f560e..0000000000 --- a/packages/plugins/plugin-msw/src/msw-plugin.ts +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license. - -import { http, HttpResponse, passthrough } from 'msw'; -import { setupWorker } from 'msw/browser'; -import { - Plugin, - PluginContext, - ObjectKernel, - HttpDispatcher, - HttpDispatcherResult -} from '@objectstack/runtime'; -// import { ObjectStackProtocolImplementation } from '@objectstack/objectql'; -import { ObjectStackProtocol } from '@objectstack/spec/api'; -import { IDataEngine } from '@objectstack/core'; - -// Helper for parsing query parameters -function parseQueryParams(url: URL): Record { - const params: Record = {}; - const keys = Array.from(new Set(url.searchParams.keys())); - - for (const key of keys) { - const values = url.searchParams.getAll(key); - // If single value, use it directly. If multiple, keep as array. - const rawValue = values.length === 1 ? values[0] : values; - - // Helper to parse individual value - const parseValue = (val: string) => { - if (val === 'true') return true; - if (val === 'false') return false; - if (val === 'null') return null; - if (val === 'undefined') return undefined; - - // Try number (integers only or floats) - // Safety check: Don't convert if it loses information (like leading zeros) - const num = Number(val); - if (!isNaN(num) && val.trim() !== '' && String(num) === val) { - return num; - } - - // Try JSON - if ((val.startsWith('{') && val.endsWith('}')) || (val.startsWith('[') && val.endsWith(']'))) { - try { - return JSON.parse(val); - } catch {} - } - - return val; - }; - - if (Array.isArray(rawValue)) { - params[key] = rawValue.map(parseValue); - } else { - params[key] = parseValue(rawValue as string); - } - } - - return params; -} - -export interface MSWPluginOptions { - /** - * Enable MSW in the browser environment - */ - enableBrowser?: boolean; - - /** - * Custom handlers to add to MSW - */ - customHandlers?: Array; - - /** - * Base URL for API endpoints - */ - baseUrl?: string; - - /** - * Whether to log requests - */ - logRequests?: boolean; -} - -/** - * MSW Plugin for ObjectStack - - * - * This plugin enables Mock Service Worker integration for testing and development. - * It automatically mocks API endpoints using the ObjectStack runtime protocol. - * - * @example - * ```typescript - * import { MSWPlugin } from '@objectstack/plugin-msw'; - * - * // With ObjectKernel - * const kernel = new ObjectKernel(); - * kernel.use(new MSWPlugin({ - * enableBrowser: true, - * baseUrl: '/api/v1' - * })); - * ``` - */ -export class MSWPlugin implements Plugin { - name = 'com.objectstack.plugin.msw'; - type = 'server'; - version = '0.9.0'; - - private options: MSWPluginOptions; - private worker: any; - private handlers: Array = []; - private protocol?: ObjectStackProtocol; - private dispatcher?: HttpDispatcher; - - constructor(options: MSWPluginOptions = {}) { - this.options = { - enableBrowser: true, - baseUrl: '/api/v1', - logRequests: true, - ...options - }; - } - - /** - * Init phase - */ - init = async (ctx: PluginContext) => { - ctx.logger.debug('Initializing MSW plugin', { - enableBrowser: this.options.enableBrowser, - baseUrl: this.options.baseUrl, - logRequests: this.options.logRequests - }); - // Protocol will be created in start phase - ctx.logger.info('MSW plugin initialized'); - } - - /** - * Start phase - */ - start = async (ctx: PluginContext) => { - ctx.logger.debug('Starting MSW plugin'); - - try { - // 1. Try to get existing protocol service - try { - this.protocol = ctx.getService('protocol'); - ctx.logger.debug('Protocol service found from context'); - } catch (e) { - // Ignore, will try to create default implementation - } - - // 2. If not found, try to instantiate default implementation dynamically - if (!this.protocol) { - try { - const dataEngine = ctx.getService('objectql'); - // Dynamically import ObjectStackProtocolImplementation to avoid hard dependency - const { ObjectStackProtocolImplementation } = await import('@objectstack/objectql'); - this.protocol = new ObjectStackProtocolImplementation(dataEngine); - ctx.logger.debug('Protocol implementation created dynamically'); - } catch (e: any) { - if (e.code === 'ERR_MODULE_NOT_FOUND') { - ctx.logger.warn('Module @objectstack/objectql not found. Protocol not initialized.'); - } else { - throw e; - } - } - } - - if (!this.protocol) { - // Without a protocol, MSW can't serve data APIs - ctx.logger.warn('No ObjectStackProtocol service available. MSW will only serve static/custom handlers if configured.'); - } - - } catch (e) { - ctx.logger.error('Failed to initialize protocol', e as Error); - throw new Error('[MSWPlugin] Failed to initialize protocol'); - } - - this.setupHandlers(ctx); - await this.startWorker(ctx); - } - - /** - * Destroy phase - */ - async destroy() { - await this.stopWorker(); - } - - /** - * Setup MSW handlers - */ - private setupHandlers(ctx: PluginContext) { - // Initialize HttpDispatcher - try { - this.dispatcher = new HttpDispatcher(ctx.getKernel()); - } catch (e) { - ctx.logger.warn('[MSWPlugin] Could not initialize HttpDispatcher via Kernel. Falling back to simple handlers.'); - } - - const baseUrl = this.options.baseUrl || '/api/v1'; - - // Custom handlers have priority - this.handlers = [ - ...(this.options.customHandlers || []) - ]; - - // Discovery Endpoint - this.handlers.push( - http.get('*/.well-known/objectstack', async () => { - if (this.dispatcher) { - return HttpResponse.json({ - data: await this.dispatcher.getDiscoveryInfo(baseUrl) - }); - } - return HttpResponse.json({ - data: { - version: 'v1', - apiName: 'ObjectStack API', - url: baseUrl, - capabilities: { - graphql: false, - search: false, - websockets: false, - files: false, - analytics: false, - hub: false - } - } - }); - }) - ); - - // Explicit /discovery endpoint — must be registered before catch-all - // so dispatch() is not called with an empty prefix. - this.handlers.push( - http.get(`*${baseUrl}`, async () => { - if (this.dispatcher) { - return HttpResponse.json({ data: await this.dispatcher.getDiscoveryInfo(baseUrl) }); - } - return HttpResponse.json({ data: { version: 'v1', url: baseUrl } }); - }), - http.get(`*${baseUrl}/discovery`, async () => { - if (this.dispatcher) { - return HttpResponse.json({ data: await this.dispatcher.getDiscoveryInfo(baseUrl) }); - } - return HttpResponse.json({ data: { version: 'v1', url: baseUrl } }); - }) - ); - - if (this.dispatcher) { - const dispatcher = this.dispatcher; - - // Catch-all handler for ObjectStack Runtime - // We use a wildcard to capture all methods and paths under baseUrl - const catchAll = async ({ request, params }: any) => { - const url = new URL(request.url); - // Calculate path relative to API prefix - // e.g. /api/v1/data/contacts -> /data/contacts - let path = url.pathname; - if (path.startsWith(baseUrl)) { - path = path.slice(baseUrl.length); - } - - if (this.options.logRequests) { - // eslint-disable-next-line no-console - console.log(`[MSW] Intercepted: ${request.method} ${url.pathname}`, { path }); - } - - // Parse Body if present - let body: any = undefined; - if (request.method !== 'GET' && request.method !== 'HEAD') { - try { - body = await request.clone().json(); - } catch (e) { - try { - // Try form data if json fails? - // Dispatcher expects objects usually. - // For file upload, body might be FormData logic needed? - // For now assume JSON or text - } catch (e2) {} - } - } - - // Parse Query - const query = parseQueryParams(url); - - // Dispatch - const result = await dispatcher.dispatch( - request.method, - path, - body, - query, - { request }, - baseUrl - ); - - if (result.handled) { - if (result.response) { - return HttpResponse.json(result.response.body, { - status: result.response.status, - headers: result.response.headers as any - }); - } - if (result.result) { - // Handle special results (streams/redirects - unlikely in MSW but possible) - if (result.result.type === 'redirect') { - return HttpResponse.redirect(result.result.url); - } - // Fallback for others - return HttpResponse.json(result.result); - } - } - - // Not handled by dispatcher (404 for this route subset) - return undefined; // Let MSW pass through or handle next - }; - - this.handlers.push( - http.all(`*${baseUrl}/*`, catchAll), - http.all(`*${baseUrl}`, catchAll) // Handle root if needed - ); - - ctx.logger.info('MSW handlers set up using HttpDispatcher', { baseUrl }); - } else { - ctx.logger.warn('[MSWPlugin] No dispatcher available. No API routes registered.'); - } - } - - - /** - * Start the MSW worker - */ - private async startWorker(ctx: PluginContext) { - if (this.options.enableBrowser && typeof window !== 'undefined') { - // Browser environment - ctx.logger.debug('Starting MSW in browser mode'); - this.worker = setupWorker(...this.handlers); - await this.worker.start({ - onUnhandledRequest: 'bypass', - }); - ctx.logger.info('MSW started in browser mode'); - } else { - ctx.logger.debug('MSW browser mode disabled or not in browser environment'); - } - } - - /** - * Stop the MSW worker - */ - private async stopWorker() { - if (this.worker) { - this.worker.stop(); - console.log('[MSWPlugin] Stopped MSW worker'); - } - } - - /** - * Get the MSW worker instance for advanced use cases - */ - getWorker() { - return this.worker; - } - - /** - * Get registered handlers - */ - getHandlers() { - return this.handlers; - } -} - -/** - * Static helper for interacting with ObjectStack protocol in MSW handlers - */ -export class ObjectStackServer { - private static protocol: ObjectStackProtocol; - - static init(protocol: ObjectStackProtocol) { - this.protocol = protocol; - } - - private static getProtocol(): ObjectStackProtocol { - if (!this.protocol) { - throw new Error('ObjectStackServer not initialized. Call ObjectStackServer.init(protocol) first.'); - } - return this.protocol; - } - - static async findData(objectName: string, query?: any) { - const body = await this.getProtocol().findData({ object: objectName, query }); - return { data: body, status: 200 }; - } - - static async getData(objectName: string, id: string, options?: { expand?: string | string[]; select?: string | string[] }) { - const normalize = (v?: string | string[]) => v == null ? undefined : Array.isArray(v) ? v : [v]; - const body = await this.getProtocol().getData({ - object: objectName, - id, - select: normalize(options?.select), - expand: normalize(options?.expand), - }); - return { data: body, status: 200 }; - } - - static async createData(objectName: string, data: any) { - const body = await this.getProtocol().createData({ object: objectName, data }); - return { data: body, status: 201 }; - } - - static async updateData(objectName: string, id: string, data: any) { - const body = await this.getProtocol().updateData({ object: objectName, id, data }); - return { data: body, status: 200 }; - } - - static async deleteData(objectName: string, id: string) { - const body = await this.getProtocol().deleteData({ object: objectName, id }); - return { data: body, status: 200 }; - } -} diff --git a/packages/plugins/plugin-msw/tsconfig.json b/packages/plugins/plugin-msw/tsconfig.json deleted file mode 100644 index 958dcf21b3..0000000000 --- a/packages/plugins/plugin-msw/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "module": "ES2020", - "moduleResolution": "bundler", - "declaration": true, - "outDir": "./dist", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "types": [ - "node" - ], - "ignoreDeprecations": "6.0", - "rootDir": "./src" - }, - "include": [ - "src/**/*" - ], - "exclude": [ - "node_modules", - "dist", - "**/*.test.ts" - ] -} diff --git a/packages/rest/README.md b/packages/rest/README.md index f2ad8de53f..265c193e8e 100644 --- a/packages/rest/README.md +++ b/packages/rest/README.md @@ -7,7 +7,7 @@ ## Overview -`@objectstack/rest` is registered as a kernel plugin and emits HTTP routes for every object defined in the protocol metadata. Framework adapters (`@objectstack/express`, `hono`, `fastify`, `nextjs`, `nuxt`, `nestjs`, `sveltekit`) invoke these routes through `HttpDispatcher` from `@objectstack/runtime`. +`@objectstack/rest` is registered as a kernel plugin and emits HTTP routes for every object defined in the protocol metadata. The Hono framework adapter (`@objectstack/hono`) invokes these routes through `HttpDispatcher` from `@objectstack/runtime`. ## Installation diff --git a/packages/runtime/src/http-dispatcher.ts b/packages/runtime/src/http-dispatcher.ts index 1b6cb3ef54..116e10a9f8 100644 --- a/packages/runtime/src/http-dispatcher.ts +++ b/packages/runtime/src/http-dispatcher.ts @@ -127,12 +127,16 @@ export interface HttpDispatcherOptions { } /** - * @deprecated Use `createDispatcherPlugin()` from `@objectstack/runtime` instead. - * This class will be removed in v2. Prefer the plugin-based approach: - * ```ts - * import { createDispatcherPlugin } from '@objectstack/runtime'; - * kernel.use(createDispatcherPlugin({ prefix: '/api/v1' })); - * ``` + * The HTTP dispatch engine — translates an inbound (method, path, body, ctx) + * request into a kernel response. Used directly by the framework's HTTP adapters + * (express / fastify / nextjs / nestjs / nuxt / sveltekit / hono) and plugin-msw, + * which need a *callable* dispatcher. + * + * NOTE: `createDispatcherPlugin()` is a different thing — a kernel plugin that + * registers routes on a kernel-hosted HTTP server. It is NOT a drop-in for + * adapters. Retiring this public class behind a `createHttpDispatcher()` factory + * is tracked in #2380 (a deliberate adapter-API change, not yet done) — so this + * is intentionally NOT marked `@deprecated` while no working replacement exists. */ export class HttpDispatcher { private kernel: any; // Casting to any to access dynamic props like services, graphql diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a08c48501f..0047b25815 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -209,41 +209,6 @@ importers: specifier: ^4.1.9 version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.2)(msw@2.14.6(@types/node@26.0.0)(typescript@6.0.3))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - packages/adapters/express: - dependencies: - '@objectstack/runtime': - specifier: workspace:^ - version: link:../../runtime - devDependencies: - '@types/express': - specifier: ^5.0.6 - version: 5.0.6 - express: - specifier: ^5.2.1 - version: 5.2.1 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.1.9 - version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.2)(msw@2.14.6(@types/node@26.0.0)(typescript@6.0.3))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - - packages/adapters/fastify: - dependencies: - '@objectstack/runtime': - specifier: workspace:^ - version: link:../../runtime - devDependencies: - fastify: - specifier: ^5.8.5 - version: 5.8.5 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.1.9 - version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.2)(msw@2.14.6(@types/node@26.0.0)(typescript@6.0.3))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - packages/adapters/hono: dependencies: '@objectstack/plugin-hono-server': @@ -266,79 +231,6 @@ importers: specifier: ^4.1.9 version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.2)(msw@2.14.6(@types/node@26.0.0)(typescript@6.0.3))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - packages/adapters/nestjs: - dependencies: - '@objectstack/runtime': - specifier: workspace:^ - version: link:../../runtime - devDependencies: - '@nestjs/common': - specifier: ^11.1.27 - version: 11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': - specifier: ^11.1.27 - version: 11.1.27(@nestjs/common@11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2) - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.1.9 - version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.2)(msw@2.14.6(@types/node@26.0.0)(typescript@6.0.3))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - - packages/adapters/nextjs: - dependencies: - '@objectstack/runtime': - specifier: workspace:^ - version: link:../../runtime - devDependencies: - next: - specifier: ^16.2.9 - version: 16.2.9(@opentelemetry/api@1.9.1)(@playwright/test@1.61.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react: - specifier: ^19.2.7 - version: 19.2.7 - react-dom: - specifier: ^19.2.7 - version: 19.2.7(react@19.2.7) - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.1.9 - version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.2)(msw@2.14.6(@types/node@26.0.0)(typescript@6.0.3))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - - packages/adapters/nuxt: - dependencies: - '@objectstack/runtime': - specifier: workspace:^ - version: link:../../runtime - devDependencies: - h3: - specifier: ^1.15.11 - version: 1.15.11 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.1.9 - version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.2)(msw@2.14.6(@types/node@26.0.0)(typescript@6.0.3))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - - packages/adapters/sveltekit: - dependencies: - '@objectstack/runtime': - specifier: workspace:^ - version: link:../../runtime - devDependencies: - '@sveltejs/kit': - specifier: ^2.66.0 - version: 2.66.0(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.3(@typescript-eslint/types@8.61.1))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.55.3(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.1.9 - version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.2)(msw@2.14.6(@types/node@26.0.0)(typescript@6.0.3))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - packages/apps/account: dependencies: '@objectstack/platform-objects': @@ -631,15 +523,9 @@ importers: '@objectstack/plugin-hono-server': specifier: workspace:* version: link:../plugins/plugin-hono-server - '@objectstack/plugin-msw': - specifier: workspace:* - version: link:../plugins/plugin-msw '@objectstack/runtime': specifier: workspace:* version: link:../runtime - msw: - specifier: ^2.14.6 - version: 2.14.6(@types/node@26.0.0)(typescript@6.0.3) typescript: specifier: ^6.0.3 version: 6.0.3 @@ -1495,37 +1381,6 @@ importers: specifier: ^4.1.9 version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.2)(msw@2.14.6(@types/node@26.0.0)(typescript@6.0.3))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - packages/plugins/plugin-msw: - dependencies: - '@objectstack/core': - specifier: workspace:* - version: link:../../core - '@objectstack/objectql': - specifier: workspace:* - version: link:../../objectql - '@objectstack/runtime': - specifier: workspace:^ - version: link:../../runtime - '@objectstack/spec': - specifier: workspace:* - version: link:../../spec - '@objectstack/types': - specifier: workspace:* - version: link:../../types - msw: - specifier: ^2.14.6 - version: 2.14.6(@types/node@26.0.0)(typescript@6.0.3) - devDependencies: - '@types/node': - specifier: ^26.0.0 - version: 26.0.0 - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vitest: - specifier: ^4.1.9 - version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.2)(msw@2.14.6(@types/node@26.0.0)(typescript@6.0.3))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - packages/plugins/plugin-org-scoping: dependencies: '@objectstack/core': @@ -2645,9 +2500,6 @@ packages: '@better-fetch/fetch@1.3.1': resolution: {integrity: sha512-ABkD1WhyfPZprKRQI3bhATjeiFuNWC9PXhfGWqL+sg/gKrM977oFrYkdb4msM3hgUGonr7KlOsOFT5TU2rht9g==} - '@borewit/text-codec@0.2.2': - resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} - '@changesets/apply-release-plan@7.1.1': resolution: {integrity: sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==} @@ -2904,24 +2756,6 @@ packages: resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@fastify/ajv-compiler@4.0.5': - resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==} - - '@fastify/error@4.2.0': - resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==} - - '@fastify/fast-json-stringify-compiler@5.0.3': - resolution: {integrity: sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==} - - '@fastify/forwarded@3.0.1': - resolution: {integrity: sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==} - - '@fastify/merge-json-schemas@0.2.1': - resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==} - - '@fastify/proxy-addr@5.1.0': - resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==} - '@floating-ui/core@1.7.5': resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} @@ -3234,10 +3068,6 @@ packages: '@js-joda/core@5.7.0': resolution: {integrity: sha512-WBu4ULVVxySLLzK1Ppq+OdfP+adRS4ntmDQT915rzDJ++i95gc2jZkM5B6LWEAwN3lGXpfie3yPABozdD3K3Vg==} - '@lukeed/csprng@1.1.0': - resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} - engines: {node: '>=8'} - '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -3275,37 +3105,6 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 - '@nestjs/common@11.1.27': - resolution: {integrity: sha512-kEGSzqM2lWr4whh4Ubflw+oPZSEzxvRMu9WL+LveZploJWTjec5bBlCiRVlVzTPg2kIwBiLwWSvCCW7Wnin1gg==} - peerDependencies: - class-transformer: '>=0.4.1' - class-validator: '>=0.13.2' - reflect-metadata: ^0.1.12 || ^0.2.0 - rxjs: ^7.1.0 - peerDependenciesMeta: - class-transformer: - optional: true - class-validator: - optional: true - - '@nestjs/core@11.1.27': - resolution: {integrity: sha512-K6DX7hcqmZdeXkv7tsPakKBRCgqL19a4mtbX4FluY0hWtFdtPKp6lbe+lb8gWPfvLdbOWr/CPScn7BSjBX+Ecg==} - engines: {node: '>= 20'} - peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/microservices': ^11.0.0 - '@nestjs/platform-express': ^11.0.0 - '@nestjs/websockets': ^11.0.0 - reflect-metadata: ^0.1.12 || ^0.2.0 - rxjs: ^7.1.0 - peerDependenciesMeta: - '@nestjs/microservices': - optional: true - '@nestjs/platform-express': - optional: true - '@nestjs/websockets': - optional: true - '@next/env@16.2.9': resolution: {integrity: sha512-ki5VxxXfzD/9TDe13wyeTKIjQTAwBVpnr8KhRDUr8ltMUq1/NBpWNT5tiPoxiGl+PHM4X2ahSOiPk6iAimIzPg==} @@ -3434,9 +3233,6 @@ packages: '@oxc-project/types@0.133.0': resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} - '@pinojs/redact@0.4.0': - resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} - '@playwright/test@1.61.0': resolution: {integrity: sha512-cKA5B6lpFEMyMGjxF54QihfYpB4FkEGH+qZhtArDEG+wezQAJY8Pq6C7T1SjWz+FFzt3TbyoXBQYk/0292TdJA==} engines: {node: '>=18'} @@ -4318,13 +4114,6 @@ packages: '@textlint/types@15.7.1': resolution: {integrity: sha512-Vye/GmFNBTgVzZFtIFJTmLB+s2A7oIADxNG6r9UhfPuY+Czv0z5G3xeyFZZudPlfxURsKUyPIU5XsjOFqVp33A==} - '@tokenizer/inflate@0.4.1': - resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} - engines: {node: '>=18'} - - '@tokenizer/token@0.3.0': - resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - '@tootallnate/once@1.1.2': resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} engines: {node: '>= 6'} @@ -4365,15 +4154,9 @@ packages: '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} - '@types/body-parser@1.19.6': - resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} - '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} @@ -4398,18 +4181,9 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} - '@types/express-serve-static-core@5.1.1': - resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} - - '@types/express@5.0.6': - resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} - '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - '@types/http-errors@2.0.5': - resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} - '@types/ioredis-mock@8.2.7': resolution: {integrity: sha512-YsGiaOIYBKeVvu/7GYziAD8qX3LJem5LK00d5PKykzsQJMLysAqXA61AkNuYWCekYl64tbMTqVOMF4SYoCPbQg==} peerDependencies: @@ -4442,12 +4216,6 @@ packages: '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} - '@types/qs@6.15.1': - resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} - - '@types/range-parser@1.2.7': - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -4462,12 +4230,6 @@ packages: '@types/sarif@2.1.7': resolution: {integrity: sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==} - '@types/send@1.2.1': - resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} - - '@types/serve-static@2.2.0': - resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} - '@types/set-cookie-parser@2.4.10': resolution: {integrity: sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==} @@ -4663,9 +4425,6 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} - abstract-logging@2.0.1: - resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} - accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -4803,13 +4562,6 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} - - avvio@9.2.0: - resolution: {integrity: sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ==} - aws-ssl-profiles@1.1.2: resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} engines: {node: '>= 6.0.0'} @@ -5222,9 +4974,6 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie-es@1.2.3: - resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} - cookie-es@3.1.1: resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} @@ -5256,9 +5005,6 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - crossws@0.3.5: - resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} - css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} @@ -5339,9 +5085,6 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - destr@2.0.5: - resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -5632,9 +5375,6 @@ packages: resolution: {integrity: sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg==} engines: {node: '>=12.17.0'} - fast-decode-uri-component@1.0.1: - resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -5648,18 +5388,9 @@ packages: fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - fast-json-stringify@6.4.0: - resolution: {integrity: sha512-ibRCQ0GZKJIQ+P3Et1h0LhPgp3PMTYk0MH8O+kW3lNYsvmaQww5Nn3f1jf73Q0jR1Yz3a1CDP4/NZD3vOajWJQ==} - fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-querystring@1.1.2: - resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} - - fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - fast-string-truncated-width@3.0.3: resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} @@ -5683,9 +5414,6 @@ packages: resolution: {integrity: sha512-brCNCeScma/kqa54J4PIDriSSSLssRkuYaUCpvHJulGc3HGI/xxKUCTDcYkAdqJsyb//ydpbxecjC3hB9+tb/g==} hasBin: true - fastify@5.8.5: - resolution: {integrity: sha512-Yqptv59pQzPgQUSIm87hMqHJmdkb1+GPxdE6vW6FRyVE9G86mt7rOghitiU4JHRaTyDUk9pfeKmDeu70lAwM4Q==} - fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -5710,10 +5438,6 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - file-type@21.3.4: - resolution: {integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==} - engines: {node: '>=20'} - file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} @@ -5732,10 +5456,6 @@ packages: resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} engines: {node: '>=8'} - find-my-way@9.6.0: - resolution: {integrity: sha512-Zf4Xve4RymLl7NgaavNebZ01joJ8MfVerOG43wy7SHLO+r+K0C6d/SE0BiR7AV5V1VOCFlOP7ecdo+I4qmiHrQ==} - engines: {node: '>=20'} - find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -6006,9 +5726,6 @@ packages: resolution: {integrity: sha512-BBvQ/406p+4CZbTpCbVPSxfzrZrbnuWSP1ELYgyS6B+hNeKzgrdB4JczCa5VZUBQrDa9hUngm0KnexY6pJRN5Q==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - h3@1.15.11: - resolution: {integrity: sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==} - happy-dom@20.10.2: resolution: {integrity: sha512-5p9Sxis3eowDJKqx90QCsgbNA02XXqJ59NOHvD4V6cxp+rP4d/xOyVx7uY3hS8hiUbY1VeiFH8lbJ81AyuDVLQ==} engines: {node: '>=20.0.0'} @@ -6183,13 +5900,6 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - ipaddr.js@2.4.0: - resolution: {integrity: sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==} - engines: {node: '>= 10'} - - iron-webcrypto@1.2.1: - resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} - is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -6302,10 +6012,6 @@ packages: resolution: {integrity: sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==} engines: {node: '>=4'} - iterare@1.2.1: - resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} - engines: {node: '>=6'} - jake@10.9.4: resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} engines: {node: '>=10'} @@ -6346,9 +6052,6 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-schema-ref-resolver@3.0.0: - resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -6441,9 +6144,6 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - light-my-request@6.6.0: - resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==} - lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} @@ -6528,10 +6228,6 @@ packages: linkify-it@5.0.1: resolution: {integrity: sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==} - load-esm@1.0.3: - resolution: {integrity: sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==} - engines: {node: '>=13.2.0'} - load-tsconfig@0.2.5: resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7082,9 +6778,6 @@ packages: engines: {node: '>= 10.12.0'} hasBin: true - node-mock-http@1.0.4: - resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} - node-rsa@1.1.1: resolution: {integrity: sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==} @@ -7204,10 +6897,6 @@ packages: resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} engines: {node: '>=12.20.0'} - on-exit-leak-free@2.1.2: - resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} - engines: {node: '>=14.0.0'} - on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -7399,16 +7088,6 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} - pino-abstract-transport@3.0.0: - resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} - - pino-std-serializers@7.1.0: - resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} - - pino@10.3.1: - resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==} - hasBin: true - pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} @@ -7502,12 +7181,6 @@ packages: resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - process-warning@4.0.1: - resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==} - - process-warning@5.0.0: - resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} - process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} @@ -7555,9 +7228,6 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - quickjs-emscripten-core@0.32.0: resolution: {integrity: sha512-QFnPfjFey8EqknSrSxe1hZrf1/8z7/6s1QzGOmKo6++02r7QRRX7ZoyNaZh7JuVjWsVW87KnQrbZqnHkOAzUyg==} @@ -7565,9 +7235,6 @@ packages: resolution: {integrity: sha512-So0Sqw869y/S2oE3Nuc0uT3Dhqgvsj8FSrwBdsuTosVsG8ME5/OcudU1GxsrIFdFABgy17GHnTVO9TYV/bLQcA==} engines: {node: '>=16.0.0'} - radix3@1.1.2: - resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} - range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -7668,13 +7335,6 @@ packages: resolution: {integrity: sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==} engines: {node: '>= 0.8.0'} - real-require@0.2.0: - resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} - engines: {node: '>= 12.13.0'} - - real-require@1.0.0: - resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==} - rechoir@0.8.0: resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} engines: {node: '>= 10.13.0'} @@ -7701,9 +7361,6 @@ packages: resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} engines: {node: '>=4'} - reflect-metadata@0.2.2: - resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} - regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} @@ -7754,10 +7411,6 @@ packages: engines: {node: '>= 0.4'} hasBin: true - ret@0.5.0: - resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} - engines: {node: '>=10'} - retry@0.12.0: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} @@ -7769,9 +7422,6 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -7801,20 +7451,9 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-regex2@5.1.1: - resolution: {integrity: sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA==} - hasBin: true - - safe-stable-stringify@2.5.0: - resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} - engines: {node: '>=10'} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -7836,9 +7475,6 @@ packages: engines: {node: '>=20.0.0'} hasBin: true - secure-json-parse@4.1.0: - resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} - semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true @@ -7968,9 +7604,6 @@ packages: resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - sonic-boom@4.2.1: - resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -8075,10 +7708,6 @@ packages: strnum@2.4.1: resolution: {integrity: sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==} - strtok3@10.3.5: - resolution: {integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==} - engines: {node: '>=18'} - structured-source@4.0.0: resolution: {integrity: sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==} @@ -8194,10 +7823,6 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - thread-stream@4.2.0: - resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==} - engines: {node: '>=20'} - tildify@2.0.0: resolution: {integrity: sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==} engines: {node: '>=8'} @@ -8246,18 +7871,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - toad-cache@3.7.1: - resolution: {integrity: sha512-5DXWzE4Vz7xNHsv+xQ+MGfJYyC78Aok3tEr0MNwHoRf7vZnga1mQXZ4/Nsodld4VR6Wd+VhfmqnNrsRJyYPfrQ==} - engines: {node: '>=20'} - toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - token-types@6.1.2: - resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} - engines: {node: '>=14.16'} - totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} @@ -8364,17 +7981,6 @@ packages: ufo@1.6.4: resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} - uid@2.0.2: - resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} - engines: {node: '>=8'} - - uint8array-extras@1.5.0: - resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} - engines: {node: '>=18'} - - uncrypto@0.1.3: - resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} - underscore@1.13.8: resolution: {integrity: sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==} @@ -9344,8 +8950,6 @@ snapshots: '@better-fetch/fetch@1.3.1': {} - '@borewit/text-codec@0.2.2': {} - '@changesets/apply-release-plan@7.1.1': dependencies: '@changesets/config': 3.1.4 @@ -9621,29 +9225,6 @@ snapshots: '@eslint/core': 1.2.1 levn: 0.4.1 - '@fastify/ajv-compiler@4.0.5': - dependencies: - ajv: 8.20.0 - ajv-formats: 3.0.1(ajv@8.20.0) - fast-uri: 3.1.2 - - '@fastify/error@4.2.0': {} - - '@fastify/fast-json-stringify-compiler@5.0.3': - dependencies: - fast-json-stringify: 6.4.0 - - '@fastify/forwarded@3.0.1': {} - - '@fastify/merge-json-schemas@0.2.1': - dependencies: - dequal: 2.0.3 - - '@fastify/proxy-addr@5.1.0': - dependencies: - '@fastify/forwarded': 3.0.1 - ipaddr.js: 2.4.0 - '@floating-ui/core@1.7.5': dependencies: '@floating-ui/utils': 0.2.11 @@ -9797,7 +9378,8 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@inquirer/ansi@2.0.6': {} + '@inquirer/ansi@2.0.6': + optional: true '@inquirer/confirm@6.1.0(@types/node@26.0.0)': dependencies: @@ -9805,6 +9387,7 @@ snapshots: '@inquirer/type': 4.0.6(@types/node@26.0.0) optionalDependencies: '@types/node': 26.0.0 + optional: true '@inquirer/core@11.2.0(@types/node@26.0.0)': dependencies: @@ -9817,6 +9400,7 @@ snapshots: signal-exit: 4.1.0 optionalDependencies: '@types/node': 26.0.0 + optional: true '@inquirer/external-editor@1.0.3(@types/node@26.0.0)': dependencies: @@ -9825,11 +9409,13 @@ snapshots: optionalDependencies: '@types/node': 26.0.0 - '@inquirer/figures@2.0.6': {} + '@inquirer/figures@2.0.6': + optional: true '@inquirer/type@4.0.6(@types/node@26.0.0)': optionalDependencies: '@types/node': 26.0.0 + optional: true '@ioredis/as-callback@3.0.0': {} @@ -9878,8 +9464,6 @@ snapshots: '@js-joda/core@5.7.0': {} - '@lukeed/csprng@1.1.0': {} - '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.29.7 @@ -9962,6 +9546,7 @@ snapshots: is-node-process: 1.2.0 outvariant: 1.4.3 strict-event-emitter: 0.5.1 + optional: true '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: @@ -9970,29 +9555,6 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true - '@nestjs/common@11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2)': - dependencies: - file-type: 21.3.4 - iterare: 1.2.1 - load-esm: 1.0.3 - reflect-metadata: 0.2.2 - rxjs: 7.8.2 - tslib: 2.8.1 - uid: 2.0.2 - transitivePeerDependencies: - - supports-color - - '@nestjs/core@11.1.27(@nestjs/common@11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)': - dependencies: - '@nestjs/common': 11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2) - fast-safe-stringify: 2.1.1 - iterare: 1.2.1 - path-to-regexp: 8.4.2 - reflect-metadata: 0.2.2 - rxjs: 7.8.2 - tslib: 2.8.1 - uid: 2.0.2 - '@next/env@16.2.9': {} '@next/swc-darwin-arm64@16.2.9': @@ -10092,16 +9654,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@open-draft/deferred-promise@2.2.0': {} + '@open-draft/deferred-promise@2.2.0': + optional: true - '@open-draft/deferred-promise@3.0.0': {} + '@open-draft/deferred-promise@3.0.0': + optional: true '@open-draft/logger@0.3.0': dependencies: is-node-process: 1.2.0 outvariant: 1.4.3 + optional: true - '@open-draft/until@2.1.0': {} + '@open-draft/until@2.1.0': + optional: true '@opentelemetry/api@1.9.1': {} @@ -10111,13 +9677,12 @@ snapshots: '@oxc-project/types@0.133.0': {} - '@pinojs/redact@0.4.0': {} - '@playwright/test@1.61.0': dependencies: playwright: 1.61.0 - '@polka/url@1.0.0-next.29': {} + '@polka/url@1.0.0-next.29': + optional: true '@radix-ui/number@1.1.2': {} @@ -10762,6 +10327,7 @@ snapshots: '@sveltejs/acorn-typescript@1.0.10(acorn@8.17.0)': dependencies: acorn: 8.17.0 + optional: true '@sveltejs/kit@2.66.0(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.3(@typescript-eslint/types@8.61.1))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.55.3(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: @@ -10783,6 +10349,7 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 typescript: 6.0.3 + optional: true '@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.3(@typescript-eslint/types@8.61.1))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: @@ -10792,6 +10359,7 @@ snapshots: svelte: 5.55.3(@typescript-eslint/types@8.61.1) vite: 8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) vitefu: 1.1.3(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + optional: true '@swc/helpers@0.5.15': dependencies: @@ -10927,15 +10495,6 @@ snapshots: dependencies: '@textlint/ast-node-types': 15.7.1 - '@tokenizer/inflate@0.4.1': - dependencies: - debug: 4.4.3(supports-color@8.1.1) - token-types: 6.1.2 - transitivePeerDependencies: - - supports-color - - '@tokenizer/token@0.3.0': {} - '@tootallnate/once@1.1.2': optional: true @@ -10968,21 +10527,13 @@ snapshots: tslib: 2.8.1 optional: true - '@types/body-parser@1.19.6': - dependencies: - '@types/connect': 3.4.38 - '@types/node': 26.0.0 - '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 assertion-error: 2.0.1 - '@types/connect@3.4.38': - dependencies: - '@types/node': 26.0.0 - - '@types/cookie@0.6.0': {} + '@types/cookie@0.6.0': + optional: true '@types/debug@4.1.13': dependencies: @@ -11002,25 +10553,10 @@ snapshots: '@types/estree@1.0.9': {} - '@types/express-serve-static-core@5.1.1': - dependencies: - '@types/node': 26.0.0 - '@types/qs': 6.15.1 - '@types/range-parser': 1.2.7 - '@types/send': 1.2.1 - - '@types/express@5.0.6': - dependencies: - '@types/body-parser': 1.19.6 - '@types/express-serve-static-core': 5.1.1 - '@types/serve-static': 2.2.0 - '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 - '@types/http-errors@2.0.5': {} - '@types/ioredis-mock@8.2.7(ioredis@5.11.1)': dependencies: ioredis: 5.11.1 @@ -11047,10 +10583,6 @@ snapshots: '@types/normalize-package-data@2.4.4': {} - '@types/qs@6.15.1': {} - - '@types/range-parser@1.2.7': {} - '@types/react-dom@19.2.3(@types/react@19.2.17)': dependencies: '@types/react': 19.2.17 @@ -11065,31 +10597,25 @@ snapshots: '@types/sarif@2.1.7': {} - '@types/send@1.2.1': - dependencies: - '@types/node': 26.0.0 - - '@types/serve-static@2.2.0': - dependencies: - '@types/http-errors': 2.0.5 - '@types/node': 26.0.0 - '@types/set-cookie-parser@2.4.10': dependencies: '@types/node': 26.0.0 + optional: true '@types/sql.js@1.4.11': dependencies: '@types/emscripten': 1.41.5 '@types/node': 26.0.0 - '@types/statuses@2.0.6': {} + '@types/statuses@2.0.6': + optional: true '@types/tar@7.0.87': dependencies: tar: 7.5.16 - '@types/trusted-types@2.0.7': {} + '@types/trusted-types@2.0.7': + optional: true '@types/unist@2.0.11': {} @@ -11325,8 +10851,6 @@ snapshots: dependencies: event-target-shim: 5.0.1 - abstract-logging@2.0.1: {} - accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -11427,7 +10951,8 @@ snapshots: dependencies: tslib: 2.8.1 - aria-query@5.3.1: {} + aria-query@5.3.1: + optional: true array-union@2.1.0: {} @@ -11455,16 +10980,10 @@ snapshots: asynckit@0.4.0: {} - atomic-sleep@1.0.0: {} - - avvio@9.2.0: - dependencies: - '@fastify/error': 4.2.0 - fastq: 1.20.1 - aws-ssl-profiles@1.1.2: {} - axobject-query@4.1.0: {} + axobject-query@4.1.0: + optional: true azure-devops-node-api@12.5.0: dependencies: @@ -11759,7 +11278,8 @@ snapshots: cli-spinners@2.9.2: {} - cli-width@4.1.0: {} + cli-width@4.1.0: + optional: true client-only@0.0.1: {} @@ -11768,6 +11288,7 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + optional: true clsx@2.1.1: {} @@ -11823,18 +11344,18 @@ snapshots: convert-source-map@2.0.0: {} - cookie-es@1.2.3: {} - cookie-es@3.1.1: optional: true cookie-signature@1.2.2: {} - cookie@0.6.0: {} + cookie@0.6.0: + optional: true cookie@0.7.2: {} - cookie@1.1.1: {} + cookie@1.1.1: + optional: true cors@2.8.6: dependencies: @@ -11849,10 +11370,6 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - crossws@0.3.5: - dependencies: - uncrypto: 0.1.3 - css-select@5.2.2: dependencies: boolbase: 1.0.0 @@ -11887,7 +11404,8 @@ snapshots: deep-is@0.1.4: {} - deepmerge@4.3.1: {} + deepmerge@4.3.1: + optional: true default-browser-id@5.0.1: {} @@ -11911,15 +11429,14 @@ snapshots: dequal@2.0.3: {} - destr@2.0.5: {} - detect-indent@6.1.0: {} detect-libc@2.1.2: {} detect-node-es@1.1.0: {} - devalue@5.8.1: {} + devalue@5.8.1: + optional: true devlop@1.1.0: dependencies: @@ -12131,7 +11648,8 @@ snapshots: transitivePeerDependencies: - supports-color - esm-env@1.2.2: {} + esm-env@1.2.2: + optional: true esm@3.2.25: {} @@ -12152,6 +11670,7 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.5 optionalDependencies: '@typescript-eslint/types': 8.61.1 + optional: true esrecurse@4.3.0: dependencies: @@ -12266,8 +11785,6 @@ snapshots: dependencies: pure-rand: 8.4.0 - fast-decode-uri-component@1.0.1: {} - fast-deep-equal@3.1.3: {} fast-fifo@1.3.2: {} @@ -12282,34 +11799,22 @@ snapshots: fast-json-stable-stringify@2.1.0: {} - fast-json-stringify@6.4.0: - dependencies: - '@fastify/merge-json-schemas': 0.2.1 - ajv: 8.20.0 - ajv-formats: 3.0.1(ajv@8.20.0) - fast-uri: 3.1.2 - json-schema-ref-resolver: 3.0.0 - rfdc: 1.4.1 - fast-levenshtein@2.0.6: {} - fast-querystring@1.1.2: - dependencies: - fast-decode-uri-component: 1.0.1 - - fast-safe-stringify@2.1.1: {} - - fast-string-truncated-width@3.0.3: {} + fast-string-truncated-width@3.0.3: + optional: true fast-string-width@3.0.2: dependencies: fast-string-truncated-width: 3.0.3 + optional: true fast-uri@3.1.2: {} fast-wrap-ansi@0.2.2: dependencies: fast-string-width: 3.0.2 + optional: true fast-xml-builder@1.2.0: dependencies: @@ -12332,24 +11837,6 @@ snapshots: strnum: 2.4.1 xml-naming: 0.1.0 - fastify@5.8.5: - dependencies: - '@fastify/ajv-compiler': 4.0.5 - '@fastify/error': 4.2.0 - '@fastify/fast-json-stringify-compiler': 5.0.3 - '@fastify/proxy-addr': 5.1.0 - abstract-logging: 2.0.1 - avvio: 9.2.0 - fast-json-stringify: 6.4.0 - find-my-way: 9.6.0 - light-my-request: 6.6.0 - pino: 10.3.1 - process-warning: 5.0.0 - rfdc: 1.4.1 - secure-json-parse: 4.1.0 - semver: 7.8.1 - toad-cache: 3.7.1 - fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -12372,15 +11859,6 @@ snapshots: dependencies: flat-cache: 4.0.1 - file-type@21.3.4: - dependencies: - '@tokenizer/inflate': 0.4.1 - strtok3: 10.3.5 - token-types: 6.1.2 - uint8array-extras: 1.5.0 - transitivePeerDependencies: - - supports-color - file-uri-to-path@1.0.0: {} filelist@1.0.6: @@ -12408,12 +11886,6 @@ snapshots: make-dir: 3.1.0 pkg-dir: 4.2.0 - find-my-way@9.6.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-querystring: 1.1.2 - safe-regex2: 5.1.1 - find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -12616,7 +12088,8 @@ snapshots: dependencies: is-property: 1.0.2 - get-caller-file@2.0.5: {} + get-caller-file@2.0.5: + optional: true get-intrinsic@1.3.0: dependencies: @@ -12692,19 +12165,8 @@ snapshots: graceful-fs@4.2.11: {} - graphql@16.14.0: {} - - h3@1.15.11: - dependencies: - cookie-es: 1.2.3 - crossws: 0.3.5 - defu: 6.1.7 - destr: 2.0.5 - iron-webcrypto: 1.2.1 - node-mock-http: 1.0.4 - radix3: 1.1.2 - ufo: 1.6.4 - uncrypto: 0.1.3 + graphql@16.14.0: + optional: true happy-dom@20.10.2: dependencies: @@ -12847,6 +12309,7 @@ snapshots: dependencies: '@types/set-cookie-parser': 2.4.10 set-cookie-parser: 3.1.0 + optional: true hono@4.12.26: {} @@ -12981,10 +12444,6 @@ snapshots: ipaddr.js@1.9.1: {} - ipaddr.js@2.4.0: {} - - iron-webcrypto@1.2.1: {} - is-alphabetical@2.0.1: {} is-alphanumerical@2.0.1: @@ -13019,7 +12478,8 @@ snapshots: is-lambda@1.0.1: optional: true - is-node-process@1.2.0: {} + is-node-process@1.2.0: + optional: true is-number@7.0.0: {} @@ -13032,6 +12492,7 @@ snapshots: is-reference@3.0.3: dependencies: '@types/estree': 1.0.9 + optional: true is-subdir@1.2.0: dependencies: @@ -13075,8 +12536,6 @@ snapshots: editions: 6.22.0 textextensions: 6.11.0 - iterare@1.2.1: {} - jake@10.9.4: dependencies: async: 3.2.6 @@ -13110,10 +12569,6 @@ snapshots: json-buffer@3.0.1: {} - json-schema-ref-resolver@3.0.0: - dependencies: - dequal: 2.0.3 - json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -13172,7 +12627,8 @@ snapshots: dependencies: json-buffer: 3.0.1 - kleur@4.1.5: {} + kleur@4.1.5: + optional: true knex@3.2.10(better-sqlite3@12.11.1)(mysql2@3.22.4(@types/node@26.0.0))(pg@8.21.0)(sqlite3@5.1.7)(tedious@18.6.2(@azure/core-client@1.10.2)): dependencies: @@ -13208,12 +12664,6 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - light-my-request@6.6.0: - dependencies: - cookie: 1.1.1 - process-warning: 4.0.1 - set-cookie-parser: 2.7.2 - lightningcss-android-arm64@1.32.0: optional: true @@ -13271,11 +12721,10 @@ snapshots: dependencies: uc.micro: 2.1.0 - load-esm@1.0.3: {} - load-tsconfig@0.2.5: {} - locate-character@3.0.0: {} + locate-character@3.0.0: + optional: true locate-path@5.0.0: dependencies: @@ -13980,7 +13429,8 @@ snapshots: mri@1.2.0: {} - mrmime@2.0.1: {} + mrmime@2.0.1: + optional: true ms@2.1.2: {} @@ -14010,10 +13460,12 @@ snapshots: typescript: 6.0.3 transitivePeerDependencies: - '@types/node' + optional: true mute-stream@0.0.8: {} - mute-stream@4.0.0: {} + mute-stream@4.0.0: + optional: true mysql2@3.22.4(@types/node@26.0.0): dependencies: @@ -14119,8 +13571,6 @@ snapshots: - supports-color optional: true - node-mock-http@1.0.4: {} - node-rsa@1.1.1: dependencies: asn1: 0.2.6 @@ -14174,8 +13624,6 @@ snapshots: obug@2.1.3: {} - on-exit-leak-free@2.1.2: {} - on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -14210,7 +13658,8 @@ snapshots: outdent@0.5.0: {} - outvariant@1.4.3: {} + outvariant@1.4.3: + optional: true p-filter@2.1.0: dependencies: @@ -14302,7 +13751,8 @@ snapshots: lru-cache: 11.5.0 minipass: 7.1.3 - path-to-regexp@6.3.0: {} + path-to-regexp@6.3.0: + optional: true path-to-regexp@8.4.2: {} @@ -14359,26 +13809,6 @@ snapshots: pify@4.0.1: {} - pino-abstract-transport@3.0.0: - dependencies: - split2: 4.2.0 - - pino-std-serializers@7.1.0: {} - - pino@10.3.1: - dependencies: - '@pinojs/redact': 0.4.0 - atomic-sleep: 1.0.0 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 3.0.0 - pino-std-serializers: 7.1.0 - process-warning: 5.0.0 - quick-format-unescaped: 4.0.4 - real-require: 0.2.0 - safe-stable-stringify: 2.5.0 - sonic-boom: 4.2.1 - thread-stream: 4.2.0 - pirates@4.0.7: {} pkce-challenge@5.0.1: {} @@ -14457,10 +13887,6 @@ snapshots: proc-log@4.2.0: {} - process-warning@4.0.1: {} - - process-warning@5.0.0: {} - process@0.11.10: {} promise-inflight@1.0.1: @@ -14498,8 +13924,6 @@ snapshots: queue-microtask@1.2.3: {} - quick-format-unescaped@4.0.4: {} - quickjs-emscripten-core@0.32.0: dependencies: '@jitl/quickjs-ffi-types': 0.32.0 @@ -14512,8 +13936,6 @@ snapshots: '@jitl/quickjs-wasmfile-release-sync': 0.32.0 quickjs-emscripten-core: 0.32.0 - radix3@1.1.2: {} - range-parser@1.2.1: {} raw-body@3.0.2: @@ -14623,10 +14045,6 @@ snapshots: readline-sync@1.4.10: {} - real-require@0.2.0: {} - - real-require@1.0.0: {} - rechoir@0.8.0: dependencies: resolve: 1.22.12 @@ -14666,8 +14084,6 @@ snapshots: dependencies: redis-errors: 1.2.0 - reflect-metadata@0.2.2: {} - regex-recursion@6.0.2: dependencies: regex-utilities: 2.3.0 @@ -14742,7 +14158,8 @@ snapshots: transitivePeerDependencies: - supports-color - require-directory@2.1.1: {} + require-directory@2.1.1: + optional: true require-from-string@2.0.2: {} @@ -14755,17 +14172,14 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - ret@0.5.0: {} - retry@0.12.0: optional: true - rettime@0.11.11: {} + rettime@0.11.11: + optional: true reusify@1.1.0: {} - rfdc@1.4.1: {} - rimraf@3.0.2: dependencies: glob: 7.2.3 @@ -14841,18 +14255,8 @@ snapshots: dependencies: queue-microtask: 1.2.3 - rxjs@7.8.2: - dependencies: - tslib: 2.8.1 - safe-buffer@5.2.1: {} - safe-regex2@5.1.1: - dependencies: - ret: 0.5.0 - - safe-stable-stringify@2.5.0: {} - safer-buffer@2.1.2: {} samlify@2.13.1: @@ -14885,8 +14289,6 @@ snapshots: transitivePeerDependencies: - supports-color - secure-json-parse@4.1.0: {} - semver@5.7.2: {} semver@6.3.1: {} @@ -14933,7 +14335,8 @@ snapshots: set-blocking@2.0.0: optional: true - set-cookie-parser@2.7.2: {} + set-cookie-parser@2.7.2: + optional: true set-cookie-parser@3.1.0: {} @@ -15036,6 +14439,7 @@ snapshots: '@polka/url': 1.0.0-next.29 mrmime: 2.0.1 totalist: 3.0.1 + optional: true slash@3.0.0: {} @@ -15065,10 +14469,6 @@ snapshots: smart-buffer: 4.2.0 optional: true - sonic-boom@4.2.1: - dependencies: - atomic-sleep: 1.0.0 - source-map-js@1.2.1: {} source-map@0.7.6: {} @@ -15142,7 +14542,8 @@ snapshots: - bare-abort-controller - react-native-b4a - strict-event-emitter@0.5.1: {} + strict-event-emitter@0.5.1: + optional: true string-width@4.2.3: dependencies: @@ -15177,10 +14578,6 @@ snapshots: dependencies: anynum: 1.0.1 - strtok3@10.3.5: - dependencies: - '@tokenizer/token': 0.3.0 - structured-source@4.0.0: dependencies: boundary: 2.0.0 @@ -15243,6 +14640,7 @@ snapshots: zimmerframe: 1.1.4 transitivePeerDependencies: - '@typescript-eslint/types' + optional: true table@6.9.0: dependencies: @@ -15252,7 +14650,8 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - tagged-tag@1.0.0: {} + tagged-tag@1.0.0: + optional: true tailwind-merge@3.6.0: {} @@ -15346,10 +14745,6 @@ snapshots: dependencies: any-promise: 1.3.0 - thread-stream@4.2.0: - dependencies: - real-require: 1.0.0 - tildify@2.0.0: {} tinybench@2.9.0: {} @@ -15372,7 +14767,8 @@ snapshots: tldts-core@6.1.86: {} - tldts-core@7.4.0: {} + tldts-core@7.4.0: + optional: true tldts@6.1.86: dependencies: @@ -15381,6 +14777,7 @@ snapshots: tldts@7.4.0: dependencies: tldts-core: 7.4.0 + optional: true tmp@0.2.7: {} @@ -15388,21 +14785,15 @@ snapshots: dependencies: is-number: 7.0.0 - toad-cache@3.7.1: {} - toidentifier@1.0.1: {} - token-types@6.1.2: - dependencies: - '@borewit/text-codec': 0.2.2 - '@tokenizer/token': 0.3.0 - ieee754: 1.2.1 - - totalist@3.0.1: {} + totalist@3.0.1: + optional: true tough-cookie@6.0.1: dependencies: tldts: 7.4.0 + optional: true tr46@5.1.1: dependencies: @@ -15487,6 +14878,7 @@ snapshots: type-fest@5.6.0: dependencies: tagged-tag: 1.0.0 + optional: true type-is@2.1.0: dependencies: @@ -15506,14 +14898,6 @@ snapshots: ufo@1.6.4: {} - uid@2.0.2: - dependencies: - '@lukeed/csprng': 1.1.0 - - uint8array-extras@1.5.0: {} - - uncrypto@0.1.3: {} - underscore@1.13.8: {} undici-types@8.3.0: {} @@ -15582,7 +14966,8 @@ snapshots: unpipe@1.0.0: {} - until-async@3.0.2: {} + until-async@3.0.2: + optional: true uri-js@4.4.1: dependencies: @@ -15656,6 +15041,7 @@ snapshots: vitefu@1.1.3(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)): optionalDependencies: vite: 8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + optional: true vitest@4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.0.0)(@vitest/coverage-v8@4.1.9)(happy-dom@20.10.2)(msw@2.14.6(@types/node@26.0.0)(typescript@6.0.3))(vite@8.0.16(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)): dependencies: @@ -15773,7 +15159,8 @@ snapshots: xtend@4.0.2: {} - y18n@5.0.8: {} + y18n@5.0.8: + optional: true yallist@4.0.0: {} @@ -15781,7 +15168,8 @@ snapshots: yaml@2.9.0: {} - yargs-parser@21.1.1: {} + yargs-parser@21.1.1: + optional: true yargs@17.7.2: dependencies: @@ -15792,6 +15180,7 @@ snapshots: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 + optional: true yarn@1.22.22: {} @@ -15805,7 +15194,8 @@ snapshots: yocto-queue@0.1.0: {} - zimmerframe@1.1.4: {} + zimmerframe@1.1.4: + optional: true zod-to-json-schema@3.25.2(zod@4.4.3): dependencies: