nuxt-auto-crud 1.31.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +197 -31
  2. package/dist/module.d.mts +16 -46
  3. package/dist/module.json +2 -2
  4. package/dist/module.mjs +49 -92
  5. package/dist/runtime/composables/{useAutoCrudSSE.d.ts → useNacAutoCrudSSE.d.ts} +1 -1
  6. package/dist/runtime/composables/useNacAutoCrudSSE.js +21 -0
  7. package/dist/runtime/server/api/_nac/[model]/[id].delete.js +22 -0
  8. package/dist/runtime/server/api/{[model] → _nac/[model]}/[id].get.d.ts +1 -1
  9. package/dist/runtime/server/api/_nac/[model]/[id].get.js +10 -0
  10. package/dist/runtime/server/api/{[model] → _nac/[model]}/[id].patch.d.ts +1 -1
  11. package/dist/runtime/server/api/_nac/[model]/[id].patch.js +25 -0
  12. package/dist/runtime/server/api/{_schema → _nac/[model]}/index.get.d.ts +1 -1
  13. package/dist/runtime/server/api/_nac/[model]/index.get.js +11 -0
  14. package/dist/runtime/server/api/{[model] → _nac/[model]}/index.post.d.ts +1 -1
  15. package/dist/runtime/server/api/_nac/[model]/index.post.js +25 -0
  16. package/dist/runtime/server/api/{_meta.get.d.ts → _nac/_meta.get.d.ts} +4 -4
  17. package/dist/runtime/server/api/_nac/_meta.get.js +72 -0
  18. package/dist/runtime/server/api/_nac/_schemas/[model].get.d.ts +2 -0
  19. package/dist/runtime/server/api/_nac/_schemas/[model].get.js +10 -0
  20. package/dist/runtime/server/api/_nac/_schemas/index.get.d.ts +2 -0
  21. package/dist/runtime/server/api/_nac/_schemas/index.get.js +5 -0
  22. package/dist/runtime/server/api/{sse.js → _nac/_sse.get.js} +17 -15
  23. package/dist/runtime/server/exceptions.d.ts +37 -4
  24. package/dist/runtime/server/exceptions.js +58 -6
  25. package/dist/runtime/server/middleware/nac-guard.d.ts +2 -0
  26. package/dist/runtime/server/middleware/nac-guard.js +43 -0
  27. package/dist/runtime/server/types.d.ts +4 -4
  28. package/dist/runtime/server/utils/constants.d.ts +18 -2
  29. package/dist/runtime/server/utils/constants.js +18 -22
  30. package/dist/runtime/server/utils/modelMapper.d.ts +36 -20
  31. package/dist/runtime/server/utils/modelMapper.js +99 -118
  32. package/dist/runtime/server/utils/queries.d.ts +39 -0
  33. package/dist/runtime/server/utils/queries.js +78 -0
  34. package/dist/runtime/server/utils/sse-bus.d.ts +2 -2
  35. package/dist/runtime/server/utils/sse-bus.js +18 -26
  36. package/dist/runtime/server/utils/validator.d.ts +43 -0
  37. package/dist/runtime/server/utils/validator.js +22 -0
  38. package/dist/runtime/shared/utils/helpers.d.ts +7 -0
  39. package/dist/runtime/shared/utils/helpers.js +6 -0
  40. package/dist/runtime/shared/utils/types.d.ts +13 -0
  41. package/dist/runtime/shared/utils/types.js +0 -0
  42. package/dist/runtime/types/index.d.ts +22 -0
  43. package/package.json +44 -38
  44. package/dist/runtime/auth.d.ts +0 -6
  45. package/dist/runtime/composables/useAutoCrudSSE.js +0 -13
  46. package/dist/runtime/composables/useRelationDisplay.d.ts +0 -13
  47. package/dist/runtime/composables/useRelationDisplay.js +0 -54
  48. package/dist/runtime/composables/useResourceSchemas.d.ts +0 -19
  49. package/dist/runtime/composables/useResourceSchemas.js +0 -17
  50. package/dist/runtime/server/api/[model]/[id].delete.d.ts +0 -2
  51. package/dist/runtime/server/api/[model]/[id].delete.js +0 -27
  52. package/dist/runtime/server/api/[model]/[id].get.js +0 -29
  53. package/dist/runtime/server/api/[model]/[id].patch.js +0 -49
  54. package/dist/runtime/server/api/[model]/index.get.js +0 -59
  55. package/dist/runtime/server/api/[model]/index.post.js +0 -48
  56. package/dist/runtime/server/api/_meta.get.js +0 -91
  57. package/dist/runtime/server/api/_relations.get.d.ts +0 -2
  58. package/dist/runtime/server/api/_relations.get.js +0 -7
  59. package/dist/runtime/server/api/_schema/[table].get.d.ts +0 -6
  60. package/dist/runtime/server/api/_schema/[table].get.js +0 -15
  61. package/dist/runtime/server/api/_schema/index.get.js +0 -7
  62. package/dist/runtime/server/stubs/auth.d.ts +0 -8
  63. package/dist/runtime/server/stubs/auth.js +0 -11
  64. package/dist/runtime/server/tsconfig.json +0 -3
  65. package/dist/runtime/server/utils/auth.d.ts +0 -3
  66. package/dist/runtime/server/utils/auth.js +0 -97
  67. package/dist/runtime/server/utils/config.d.ts +0 -2
  68. package/dist/runtime/server/utils/config.js +0 -4
  69. package/dist/runtime/server/utils/handler.d.ts +0 -4
  70. package/dist/runtime/server/utils/handler.js +0 -32
  71. package/dist/runtime/server/utils/jwt.d.ts +0 -2
  72. package/dist/runtime/server/utils/jwt.js +0 -19
  73. package/dist/runtime/server/utils/schema.d.ts +0 -19
  74. package/dist/runtime/server/utils/schema.js +0 -104
  75. package/src/runtime/auth.d.ts +0 -6
  76. package/src/runtime/composables/useAutoCrudSSE.ts +0 -24
  77. package/src/runtime/composables/useRelationDisplay.ts +0 -74
  78. package/src/runtime/composables/useResourceSchemas.ts +0 -42
  79. package/src/runtime/server/api/[model]/[id].delete.ts +0 -44
  80. package/src/runtime/server/api/[model]/[id].get.ts +0 -48
  81. package/src/runtime/server/api/[model]/[id].patch.ts +0 -83
  82. package/src/runtime/server/api/[model]/index.get.ts +0 -93
  83. package/src/runtime/server/api/[model]/index.post.ts +0 -72
  84. package/src/runtime/server/api/_meta.get.ts +0 -111
  85. package/src/runtime/server/api/_relations.get.ts +0 -9
  86. package/src/runtime/server/api/_schema/[table].get.ts +0 -20
  87. package/src/runtime/server/api/_schema/index.get.ts +0 -9
  88. package/src/runtime/server/api/sse.ts +0 -39
  89. package/src/runtime/server/exceptions.ts +0 -25
  90. package/src/runtime/server/stubs/auth.ts +0 -11
  91. package/src/runtime/server/tsconfig.json +0 -3
  92. package/src/runtime/server/types.ts +0 -5
  93. package/src/runtime/server/utils/auth.ts +0 -152
  94. package/src/runtime/server/utils/config.ts +0 -6
  95. package/src/runtime/server/utils/constants.ts +0 -25
  96. package/src/runtime/server/utils/handler.ts +0 -43
  97. package/src/runtime/server/utils/jwt.ts +0 -23
  98. package/src/runtime/server/utils/modelMapper.ts +0 -197
  99. package/src/runtime/server/utils/schema.ts +0 -160
  100. package/src/runtime/server/utils/sse-bus.ts +0 -44
  101. /package/dist/runtime/server/api/{[model]/index.get.d.ts → _nac/[model]/[id].delete.d.ts} +0 -0
  102. /package/dist/runtime/server/api/{sse.d.ts → _nac/_sse.get.d.ts} +0 -0
package/README.md CHANGED
@@ -1,44 +1,210 @@
1
- # Nuxt Auto CRUD
1
+ ## nuxt-auto-crud (nac 2.0)
2
2
 
3
- **Nuxt Auto CRUD is a headless, zero-codegen CRUD engine that transforms Drizzle ORM schemas into fully functional RESTful APIs for Nuxt 4.**
3
+ **Zero-Codegen Dynamic RESTful CRUD APIs** derived directly from schemas. It eliminates the need to manually write or generate boilerplate for CRUD operations.
4
4
 
5
- | Specification | Details |
6
- | :--- | :--- |
7
- | **Runtime** | Nuxt 4 (`app/` directory), Nitro |
8
- | **Persistence** | SQLite / libSQL (Optimized for Cloudflare D1) |
9
- | **ORM & SSOT** | Drizzle ORM (Schema-driven) |
10
- | **Validation** | `drizzle-zod` (Dynamic derivation) |
5
+ ---
6
+
7
+ ### 🚀 Core Features
8
+
9
+ * **Zero-Codegen Dynamic RESTful CRUD APIs**: nuxt-auto-crud leverages Drizzle ORM, Zod, Nuxt, and Nitro to eliminate the need for manual CRUD coding.
10
+ * **Single Source of Truth (SSOT)**: Your Drizzle schemas (`schema/db/schema`) define the entire API structure and validation.
11
+ * **Constant Bundle Size**: Since no code is generated, the bundle size remains virtually identical whether you have one table or one hundred (scaling only with your schema definitions).
12
+
13
+ ---
14
+
15
+ ### 🌐 Dynamic RESTful CRUD endpoints
16
+
17
+ Nb: Endpoints follow the pattern `/api/_nac/:model`.
18
+
19
+ | Method | Endpoint | Action |
20
+ | --- | --- | --- |
21
+ | **GET** | `/:model` | List records (supports filtering & pagination) |
22
+ | **POST** | `/:model` | Create record with Zod validation |
23
+ | **GET** | `/:model/:id` | Fetch single record |
24
+ | **PATCH** | `/:model/:id` | Partial update with validation |
25
+ | **DELETE** | `/:model/:id` | Hard delete |
26
+
27
+ **Example (`users` table):**
28
+
29
+ | Action | HTTP Method | Endpoint | Example Result |
30
+ | --- | --- | --- | --- |
31
+ | **Fetch All** | `GET` | `/api/_nac/users` | List of all users (paginated) |
32
+ | **Create** | `POST` | `/api/_nac/users` | New user record added |
33
+ | **Fetch One** | `GET` | `/api/_nac/users/1` | Details of user with `id: 1` |
34
+ | **Update** | `PATCH` | `/api/_nac/users/1` | Partial update to user `1` |
35
+ | **Delete** | `DELETE` | `/api/_nac/users/1` | User `1` hard deleted from DB |
36
+
37
+
38
+ ---
39
+ ### 🛠 Frontend Integration APIs
40
+
41
+ In addition to CRUD endpoints, **nac** provides metadata APIs to power dynamic forms and tables in your frontend.
42
+
43
+ * **List Resources**: `GET /api/_nac/_schemas` returns all tables (excluding system-protected tables).
44
+ * **Resource Metadata**: `GET /api/_nac/_schemas/:resource` returns the field definitions, validation rules, and relationship data for a specific table.
45
+
46
+ ---
47
+
48
+ ### Schema Interface
49
+
50
+ ```typescript
51
+ export interface Field {
52
+ name: string
53
+ type: string
54
+ required?: boolean
55
+ selectOptions?: string[]
56
+ references?: string
57
+ isReadOnly?: boolean
58
+ }
59
+
60
+ export interface SchemaDefinition {
61
+ resource: string
62
+ labelField: string
63
+ fields: Field[]
64
+ }
65
+
66
+ ```
67
+
68
+ ### Example Response
69
+
70
+ `GET /api/_nac/_schemas/users`
71
+
72
+ ```json
73
+ {
74
+ "resource": "users",
75
+ "labelField": "name",
76
+ "fields": [
77
+ { "name": "id", "type": "string", "required": true, "isReadOnly": true },
78
+ { "name": "name", "type": "string", "required": true, "isReadOnly": false },
79
+ { "name": "email", "type": "string", "required": true, "isReadOnly": false }
80
+ ]
81
+ }
82
+
83
+ ```
84
+ ---
85
+
86
+ ### 🛡 Security & Configuration
11
87
 
12
- ## 🛠 Architectural Logic: Zero-Codegen
13
- NAC treats your Drizzle schema as the **Single Source of Truth (SSOT)**. Unlike traditional scaffolds, it does not generate physical files; it mounts dynamic Nitro handlers at runtime.
88
+ Enabling `authentication` in the `autoCrud` config protects all **nac** routes (`/api/_nac/*`), except those explicitly defined in `publicResources`.
14
89
 
15
- * **Dynamic Routing**: Automatically maps `GET|POST|PATCH|DELETE` to your Drizzle tables.
16
- * **Real-time Sync**: Built-in SSE broadcasting for `create`, `update`, and `delete` events.
17
- * **Agentic Compatibility**: Built with an MCP-friendly structure to allow AI Agents to interact directly with the schema-driven API.
90
+ #### 🔒 Access Control & Data Safety
18
91
 
19
- ## 🔐 RBAC & Permissions
20
- Integrates with `nuxt-authorization` for database-driven Role-Based Access Control.
21
- * **Ownership Logic**: Supports `update_own` and `delete_own` via `createdBy` column reflection.
22
- * **Granular Scopes**: Fine-grained control over `list` vs `list_all` (drafts/soft-deleted).
92
+ * **`apiHiddenFields`**: Globally hides sensitive columns from all API responses. Default: `['password', 'secret', 'token', 'reset_token', 'reset_expires', 'github_id', 'google_id']`.
93
+ * **`formHiddenFields`**: Columns excluded from the frontend schema metadata to prevent user input. Defaults to `apiHiddenFields` plus system-managed fields like `id`, `uuid`, `createdAt`, `updatedAt`, `createdBy`, etc.
94
+ * **Validation Logic**: If a field is in `apiHiddenFields` or does not exist in the schema, it is silently stripped from the response even if listed in `publicResources`.
23
95
 
24
- ## 🌐 Endpoints
25
- | Method | Endpoint | Description |
26
- | :--- | :--- | :--- |
27
- | `GET` | `/api/:model` | List with filtering/paging |
28
- | `POST` | `/api/:model` | Validated creation |
29
- | `GET` | `/api/:model/:id` | Single record retrieval |
30
- | `PATCH` | `/api/:model/:id` | Partial validated update |
31
- | `DELETE` | `/api/:model/:id` | Soft/Hard deletion |
96
+ ---
97
+
98
+ #### ⚙️ Configuration Reference
99
+
100
+ | Key | Default | Description |
101
+ | --- | --- | --- |
102
+ | `realtime` | `false` | Enables/disables real-time capabilities. |
103
+ | `auth.authentication` | `true` | Requires a valid session for all NAC routes. |
104
+ | `auth.authorization` | `true` | Enables role/owner-based access checks. |
105
+ | `auth.ownerKey` | `'ownerId'` | The column name used to identify the record creator. |
106
+ | `publicResources` | `{}` | Defines tables and specific columns accessible without auth. |
107
+ | `nacEndpointPrefix` | `'/api/_nac'` | The base path for NAC routes. Access via `useRuntimeConfig().public.autoCrud`. |
108
+ | `schemaPath` | `'server/db/schema'` | Location of your Drizzle schema files. |
109
+
110
+ #### Example `nuxt.config.ts`
32
111
 
112
+ ```typescript
113
+ autoCrud: {
114
+ realtime: false,
115
+ auth: {
116
+ authentication: true,
117
+ authorization: true,
118
+ ownerKey: 'ownerId',
119
+ },
120
+ publicResources: {
121
+ users: ['id', 'name', 'email'],
122
+ },
123
+ apiHiddenFields: ['password'],
124
+ agenticToken: process.env.NAC_AGENTIC_TOKEN,
125
+ formHiddenFields: [],
126
+ nacEndpointPrefix: '/api/_nac',
127
+ schemaPath: 'server/db/schema',
128
+ }
129
+
130
+ ```
131
+
132
+ > **Note**: Modify `nacEndpointPrefix` or `schemaPath` only if the Nuxt/Nitro conventions change or you use a non-standard directory structure.
33
133
  ---
134
+ ### 🛡 Filtering & Performance Optimization
135
+
136
+ #### Automatic Status Filtering
34
137
 
35
- ## Installation
36
- It is highly recommended to use the [Template](https://auto-crud.clifland.in/docs/auto-crud) for new installations.
138
+ To align with standard application behavior, **nac** automatically filters records if a `status` column exists. By default, it will only return **active** records, reducing boilerplate for soft-state management.
37
139
 
38
- If you are adding it to an existing application, refer to the [Manual Installation](https://auto-crud.clifland.in/docs/manual-installation) guide.
140
+ #### Ownership & Permissions
39
141
 
40
- [YouTube Walkthrough](https://www.youtube.com/watch?v=_o0cddJUU50&list=PLnbvxcojhIixqM1J08Tnm7vmMdx2wsy4B)
142
+ While the implementing app handles the authentication layer, **nac** provides a standardized way to enforce record ownership and granular access.
41
143
 
42
- [NPM Package](https://www.npmjs.com/package/nuxt-auto-crud)
144
+ If your middleware populates `event.context.nac` with `resourcePermissions`, **nac** automatically injects the necessary SQL filters.
145
+
146
+ **Example: Restricting users to their own records**
147
+ If the permissions array includes `'list_own'`, **nac** appends a filter where `ownerCol === userId`.
148
+
149
+ ```typescript
150
+ // Example: Setting context in your Auth Middleware
151
+ event.context.nac = {
152
+ userId: user.id,
153
+ resourcePermissions: user.permissions[model], // e.g., ['list_own', 'list']
154
+ record: null, // Optional: Pre-fetched record to prevent double-hitting the DB
155
+ }
156
+
157
+ ```
158
+
159
+ #### Performance: The `record` Context
160
+
161
+ For `UPDATE`, `DELETE`, or `GET` (by ID) operations, **nac** must verify ownership.
162
+
163
+ * **Standard**: **nac** fetches the record to check the owner ID.
164
+ * **Optimized**: If your middleware has already fetched the record for validation, pass it to `event.context.nac.record`. **nac** will use this object instead of executing an additional database query.
165
+ ---
43
166
 
44
- [Creator: Clifland](https://www.clifland.in/)
167
+ ### 📡 Real-time Synchronization (SSE)
168
+
169
+ When `realtime` is enabled, all `create`, `update`, and `delete` operations are automatically broadcasted:
170
+
171
+ ```typescript
172
+ if (realtime) {
173
+ void broadcast({
174
+ table: model,
175
+ action: 'create',
176
+ primaryKey: newRecord.id,
177
+ data: newRecord,
178
+ })
179
+ }
180
+
181
+ ```
182
+
183
+ #### Frontend Usage
184
+
185
+ NAC provides a `useNacAutoCrudSSE` composable to listen for these changes in your frontend:
186
+
187
+ ```typescript
188
+ useNacAutoCrudSSE(({ table, action, data: sseData, primaryKey }) => {
189
+ // Optional: Filter by specific table
190
+ // if (table !== currentTable.value) return
191
+
192
+ if (action === 'update') {
193
+ // updateRow(primaryKey, sseData)
194
+ }
195
+
196
+ if (action === 'create') {
197
+ // addRow(sseData)
198
+ }
199
+
200
+ if (action === 'delete') {
201
+ // removeRow(primaryKey)
202
+ }
203
+ })
204
+
205
+ ```
206
+ ---
207
+ ## ⚠️ Limitations
208
+ **Database Support:** Currently optimized for SQLite/libSQL only.
209
+
210
+ ---
package/dist/module.d.mts CHANGED
@@ -1,58 +1,28 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
2
 
3
3
  interface ModuleOptions {
4
- /**
5
- * Path to the database schema file
6
- * @default 'server/database/schema'
7
- */
8
- schemaPath?: string;
9
- /**
10
- * Authentication configuration
11
- */
12
- auth?: boolean | AuthOptions;
13
- /**
14
- * Resource-specific configuration
15
- * Define column visibility for unauthenticated users
16
- */
17
- resources?: {
18
- [modelName: string]: string[];
4
+ realtime: boolean;
5
+ schemaPath: string;
6
+ auth: {
7
+ authentication: boolean;
8
+ authorization: boolean;
9
+ ownerKey: string;
19
10
  };
20
- /**
21
- * Fields that should be automatically hashed before storage
22
- * @default ['password']
23
- */
24
- hashedFields?: string[];
11
+ apiHiddenFields: string[]; /** Sensitive: Never leaves the server */
12
+ agenticToken: string;
13
+ publicResources: Record<string, string[]>; /** Allowed fields for public apis */
14
+ nacEndpointPrefix: string;
15
+ formHiddenFields: string[]; /** UI: Hidden from forms */
25
16
  }
26
- interface AuthOptions {
27
- /**
28
- * Authentication type
29
- * @default 'session'
30
- */
31
- type?: 'session' | 'jwt';
32
- /**
33
- * JWT Secret (required if type is 'jwt')
34
- */
35
- jwtSecret?: string;
36
- /**
37
- * Enable authentication checks (requires nuxt-auth-utils for session)
38
- * @default false
39
- */
40
- authentication: boolean;
41
- /**
42
- * Enable authorization checks (requires nuxt-authorization)
43
- * @default false
44
- */
45
- authorization?: boolean;
46
- }
47
- interface RuntimeModuleOptions extends Omit<ModuleOptions, 'auth'> {
48
- auth: AuthOptions;
49
- }
50
-
51
17
  declare module '@nuxt/schema' {
52
18
  interface RuntimeConfig {
53
- autoCrud: RuntimeModuleOptions;
19
+ autoCrud: Omit<ModuleOptions, 'nacEndpointPrefix' | 'formHiddenFields'>;
20
+ }
21
+ interface PublicRuntimeConfig {
22
+ autoCrud: Pick<ModuleOptions, 'nacEndpointPrefix' | 'formHiddenFields'>;
54
23
  }
55
24
  }
25
+
56
26
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
57
27
 
58
28
  export { _default as default };
package/dist/module.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "nuxt-auto-crud",
3
3
  "configKey": "autoCrud",
4
- "version": "1.31.0",
4
+ "version": "2.1.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
- "unbuild": "3.6.1"
7
+ "unbuild": "unknown"
8
8
  }
9
9
  }
package/dist/module.mjs CHANGED
@@ -1,4 +1,5 @@
1
- import { defineNuxtModule, createResolver, addImportsDir, hasNuxtModule, addServerImports, addServerHandler, addServerImportsDir } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, addImportsDir, addServerImportsDir, addServerHandler } from '@nuxt/kit';
2
+ import { NAC_FORM_HIDDEN_FIELDS, NAC_API_HIDDEN_FIELDS } from '../dist/runtime/server/utils/constants.js';
2
3
 
3
4
  const module$1 = defineNuxtModule({
4
5
  meta: {
@@ -6,104 +7,60 @@ const module$1 = defineNuxtModule({
6
7
  configKey: "autoCrud"
7
8
  },
8
9
  defaults: {
10
+ // Private config
11
+ realtime: false,
12
+ auth: {
13
+ authentication: false,
14
+ authorization: false,
15
+ ownerKey: "createdBy"
16
+ },
17
+ publicResources: {},
18
+ apiHiddenFields: NAC_API_HIDDEN_FIELDS,
19
+ agenticToken: "",
9
20
  schemaPath: "server/db/schema",
10
- auth: false
21
+ // Public config
22
+ formHiddenFields: NAC_FORM_HIDDEN_FIELDS,
23
+ nacEndpointPrefix: "/api/_nac"
11
24
  },
12
25
  async setup(options, nuxt) {
26
+ const prefix = options.nacEndpointPrefix || "/api/_nac";
13
27
  const resolver = createResolver(import.meta.url);
14
- const schemaPath = resolver.resolve(
15
- nuxt.options.rootDir,
16
- options.schemaPath
17
- );
18
- nuxt.options.alias["#site/schema"] = schemaPath;
19
- addImportsDir(resolver.resolve(nuxt.options.rootDir, "shared/utils"));
20
- const stubsPath = resolver.resolve("./runtime/server/stubs/auth");
21
- if (!hasNuxtModule("nuxt-auth-utils")) {
22
- addServerImports([
23
- { name: "requireUserSession", from: stubsPath },
24
- { name: "getUserSession", from: stubsPath },
25
- { name: "hashPassword", from: stubsPath }
26
- ]);
27
- }
28
- if (!hasNuxtModule("nuxt-authorization")) {
29
- addServerImports([
30
- { name: "allows", from: stubsPath },
31
- { name: "abilities", from: stubsPath },
32
- { name: "abilityLogic", from: stubsPath }
33
- ]);
34
- }
35
- nuxt.options.alias["#authorization"] ||= "nuxt-authorization/utils";
36
- const mergedAuth = options.auth === false ? { authentication: false, authorization: false, type: "session" } : {
37
- authentication: true,
38
- authorization: options.auth === true,
39
- type: "session",
40
- ...typeof options.auth === "object" ? options.auth : {}
41
- };
42
- nuxt.options.runtimeConfig.autoCrud = {
43
- auth: {
44
- authentication: mergedAuth.authentication ?? false,
45
- authorization: mergedAuth.authorization ?? false,
46
- type: mergedAuth.type ?? "session",
47
- jwtSecret: mergedAuth.jwtSecret
48
- },
49
- resources: {
50
- ...options.resources
51
- },
52
- hashedFields: options.hashedFields ?? ["password"]
53
- };
54
- const apiDir = resolver.resolve("./runtime/server/api");
55
- addServerHandler({
56
- route: "/api/_schema",
57
- method: "get",
58
- handler: resolver.resolve(apiDir, "_schema/index.get")
59
- });
60
- addServerHandler({
61
- route: "/api/_schema/:table",
62
- method: "get",
63
- handler: resolver.resolve(apiDir, "_schema/[table].get")
64
- });
65
- addServerHandler({
66
- route: "/api/_relations",
67
- method: "get",
68
- handler: resolver.resolve(apiDir, "_relations.get")
69
- });
70
- addServerHandler({
71
- route: "/api/_meta",
72
- method: "get",
73
- handler: resolver.resolve(apiDir, "_meta.get")
74
- });
75
- addServerHandler({
76
- route: "/api/sse",
77
- method: "get",
78
- handler: resolver.resolve(apiDir, "sse")
79
- });
80
- addServerHandler({
81
- route: "/api/:model",
82
- method: "get",
83
- handler: resolver.resolve(apiDir, "[model]/index.get")
84
- });
85
- addServerHandler({
86
- route: "/api/:model",
87
- method: "post",
88
- handler: resolver.resolve(apiDir, "[model]/index.post")
89
- });
90
- addServerHandler({
91
- route: "/api/:model/:id",
92
- method: "get",
93
- handler: resolver.resolve(apiDir, "[model]/[id].get")
94
- });
95
- addServerHandler({
96
- route: "/api/:model/:id",
97
- method: "patch",
98
- handler: resolver.resolve(apiDir, "[model]/[id].patch")
28
+ nuxt.options.alias["#nac/shared"] = resolver.resolve("./runtime/shared");
29
+ nuxt.options.alias["#nac/types"] = resolver.resolve("./runtime/server/types");
30
+ nuxt.options.alias["#nac/schema"] = resolver.resolve(nuxt.options.rootDir, options.schemaPath);
31
+ const { formHiddenFields, nacEndpointPrefix, ...privateOptions } = options;
32
+ nuxt.options.runtimeConfig.autoCrud = privateOptions;
33
+ nuxt.options.runtimeConfig.public.autoCrud = { formHiddenFields, nacEndpointPrefix };
34
+ addImportsDir(resolver.resolve("./runtime/composables"));
35
+ addServerImportsDir(resolver.resolve("./runtime/server/utils"));
36
+ nuxt.hook("prepare:types", ({ references }) => {
37
+ references.push({ path: resolver.resolve("./runtime/types/index.d.ts") });
99
38
  });
100
39
  addServerHandler({
101
- route: "/api/:model/:id",
102
- method: "delete",
103
- handler: resolver.resolve(apiDir, "[model]/[id].delete")
40
+ middleware: true,
41
+ handler: resolver.resolve("./runtime/server/middleware/nac-guard.ts")
104
42
  });
105
- addServerImportsDir(resolver.resolve("./runtime/server/utils"));
106
- addImportsDir(resolver.resolve("./runtime/composables"));
43
+ const apiDir = resolver.resolve("./runtime/server/api/_nac");
44
+ const routes = [
45
+ // Dynamic CRUD Endpoints
46
+ { path: "/:model", method: "get", handler: "[model]/index.get.ts" },
47
+ { path: "/:model", method: "post", handler: "[model]/index.post.ts" },
48
+ { path: "/:model/:id", method: "get", handler: "[model]/[id].get.ts" },
49
+ { path: "/:model/:id", method: "patch", handler: "[model]/[id].patch.ts" },
50
+ { path: "/:model/:id", method: "delete", handler: "[model]/[id].delete.ts" },
51
+ // System Endpoints
52
+ { path: "/_schemas", method: "get", handler: "_schemas/index.get.ts" },
53
+ { path: "/_schemas/:model", method: "get", handler: "_schemas/[model].get.ts" },
54
+ { path: "/_meta", method: "get", handler: "_meta.get.ts" },
55
+ { path: "/_sse", method: "get", handler: "_sse.get.ts" }
56
+ ];
57
+ for (const route of routes) {
58
+ addServerHandler({
59
+ route: `${prefix}${route.path}`,
60
+ method: route.method,
61
+ handler: resolver.resolve(apiDir, route.handler)
62
+ });
63
+ }
107
64
  }
108
65
  });
109
66
 
@@ -4,4 +4,4 @@ export interface AutoCrudEvent {
4
4
  data: Record<string, unknown>;
5
5
  primaryKey: string | number;
6
6
  }
7
- export declare function useAutoCrudSSE(onEvent: (e: AutoCrudEvent) => void): void;
7
+ export declare function useNacAutoCrudSSE(onEvent: (e: AutoCrudEvent) => void): void;
@@ -0,0 +1,21 @@
1
+ export function useNacAutoCrudSSE(onEvent) {
2
+ let source = null;
3
+ onMounted(() => {
4
+ if (typeof window === "undefined" || !("EventSource" in window)) return;
5
+ source = new EventSource(`/api/_nac/_sse`);
6
+ source.onerror = (err) => {
7
+ console.error("[NAC] SSE Connection Error:", err);
8
+ };
9
+ source.addEventListener("crud", (e) => {
10
+ try {
11
+ const payload = JSON.parse(e.data);
12
+ onEvent(payload);
13
+ } catch (err) {
14
+ console.error("[NAC] SSE Parse Error:", err);
15
+ }
16
+ });
17
+ });
18
+ onBeforeUnmount(() => {
19
+ source?.close();
20
+ });
21
+ }
@@ -0,0 +1,22 @@
1
+ import { eventHandler, getRouterParams } from "h3";
2
+ import { useRuntimeConfig } from "#imports";
3
+ import { modelTableMap } from "../../../utils/modelMapper.js";
4
+ import { nacDeleteRow } from "../../../utils/queries.js";
5
+ import { broadcast } from "../../../utils/sse-bus.js";
6
+ import { ResourceNotFoundError } from "../../../exceptions.js";
7
+ export default eventHandler(async (event) => {
8
+ const { model, id } = getRouterParams(event);
9
+ const table = modelTableMap[model];
10
+ if (!table) throw new ResourceNotFoundError(model);
11
+ const deletedRecord = await nacDeleteRow(table, id);
12
+ const { realtime } = useRuntimeConfig().autoCrud;
13
+ if (realtime) {
14
+ void broadcast({
15
+ table: model,
16
+ action: "delete",
17
+ primaryKey: deletedRecord.id,
18
+ data: deletedRecord
19
+ });
20
+ }
21
+ return deletedRecord;
22
+ });
@@ -1,2 +1,2 @@
1
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Record<string, unknown>>>;
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
2
  export default _default;
@@ -0,0 +1,10 @@
1
+ import { eventHandler, getRouterParams } from "h3";
2
+ import { modelTableMap } from "../../../utils/modelMapper.js";
3
+ import { nacGetRow } from "../../../utils/queries.js";
4
+ import { ResourceNotFoundError } from "../../../exceptions.js";
5
+ export default eventHandler(async (event) => {
6
+ const { model, id } = getRouterParams(event);
7
+ const table = modelTableMap[model];
8
+ if (!table) throw new ResourceNotFoundError(model);
9
+ return await nacGetRow(table, id, event.context.nac || {});
10
+ });
@@ -1,2 +1,2 @@
1
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Record<string, unknown>>>;
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
2
  export default _default;
@@ -0,0 +1,25 @@
1
+ import { eventHandler, getRouterParams, readBody } from "h3";
2
+ import { useRuntimeConfig } from "#imports";
3
+ import { modelTableMap } from "../../../utils/modelMapper.js";
4
+ import { resolveValidatedSchema } from "../../../utils/validator.js";
5
+ import { nacUpdateRow } from "../../../utils/queries.js";
6
+ import { broadcast } from "../../../utils/sse-bus.js";
7
+ import { ResourceNotFoundError } from "../../../exceptions.js";
8
+ export default eventHandler(async (event) => {
9
+ const { model, id } = getRouterParams(event);
10
+ const body = await readBody(event);
11
+ const table = modelTableMap[model];
12
+ if (!table) throw new ResourceNotFoundError(model);
13
+ const validatedData = await resolveValidatedSchema(table, "patch").parseAsync(body);
14
+ const updatedRecord = await nacUpdateRow(table, id, validatedData, event.context.nac || {});
15
+ const { realtime } = useRuntimeConfig().autoCrud;
16
+ if (realtime) {
17
+ void broadcast({
18
+ table: model,
19
+ action: "update",
20
+ primaryKey: updatedRecord.id,
21
+ data: updatedRecord
22
+ });
23
+ }
24
+ return updatedRecord;
25
+ });
@@ -1,2 +1,2 @@
1
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Record<string, any>>>;
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
2
  export default _default;
@@ -0,0 +1,11 @@
1
+ import { eventHandler, getRouterParams } from "h3";
2
+ import { modelTableMap } from "../../../utils/modelMapper.js";
3
+ import { nacGetRows } from "../../../utils/queries.js";
4
+ import { ResourceNotFoundError } from "../../../exceptions.js";
5
+ export default eventHandler(async (event) => {
6
+ const { model } = getRouterParams(event);
7
+ const table = modelTableMap[model];
8
+ if (!table) throw new ResourceNotFoundError(model);
9
+ const results = await nacGetRows(table, event.context.nac || {});
10
+ return results;
11
+ });
@@ -1,2 +1,2 @@
1
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Record<string, unknown>>>;
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
2
2
  export default _default;
@@ -0,0 +1,25 @@
1
+ import { eventHandler, getRouterParams, readBody } from "h3";
2
+ import { useRuntimeConfig } from "#imports";
3
+ import { modelTableMap } from "../../../utils/modelMapper.js";
4
+ import { resolveValidatedSchema } from "../../../utils/validator.js";
5
+ import { nacCreateRow } from "../../../utils/queries.js";
6
+ import { broadcast } from "../../../utils/sse-bus.js";
7
+ import { ResourceNotFoundError } from "../../../exceptions.js";
8
+ export default eventHandler(async (event) => {
9
+ const { model } = getRouterParams(event);
10
+ const body = await readBody(event);
11
+ const table = modelTableMap[model];
12
+ if (!table) throw new ResourceNotFoundError(model);
13
+ const validatedData = await resolveValidatedSchema(table, "insert").parseAsync(body);
14
+ const newRecord = await nacCreateRow(table, validatedData, event.context.nac || {});
15
+ const { realtime } = useRuntimeConfig().autoCrud;
16
+ if (realtime) {
17
+ void broadcast({
18
+ table: model,
19
+ action: "create",
20
+ primaryKey: newRecord.id,
21
+ data: newRecord
22
+ });
23
+ }
24
+ return newRecord;
25
+ });
@@ -8,11 +8,11 @@ declare const _default: import("h3").EventHandler<import("h3").EventHandlerReque
8
8
  methods: string[];
9
9
  fields: {
10
10
  name: string;
11
- type: any;
12
- required: boolean;
11
+ type: string;
12
+ required: boolean | undefined;
13
13
  isEnum: boolean;
14
- options: any;
15
- references: any;
14
+ options: string[] | null;
15
+ references: string | null;
16
16
  isRelation: boolean;
17
17
  isReadOnly: boolean;
18
18
  }[];