@web-ts-toolkit/access-router 0.31.5 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,54 +25,127 @@ Peer dependencies:
25
25
  ## Quick Start
26
26
 
27
27
  ```ts
28
- import acl from '@web-ts-toolkit/access-router';
28
+ import express from 'express';
29
+ import mongoose from 'mongoose';
30
+ import acl, { permissionsPlugin } from '@web-ts-toolkit/access-router';
29
31
 
32
+ // 1. Configure how request permissions are resolved globally.
30
33
  acl.setGlobalOptions({
34
+ requestPermissionField: '_permissions',
31
35
  globalPermissions(req) {
32
36
  return req.headers.user === 'admin' ? ['isAdmin'] : [];
33
37
  },
34
38
  });
35
39
 
40
+ // 2. Register a Mongoose model and schema. The schema opts in to the
41
+ // permissions plugin so generated routers can enforce field access.
42
+ const userSchema = new mongoose.Schema({ name: String, role: String, public: Boolean });
43
+ userSchema.plugin(permissionsPlugin, { modelName: 'User' });
44
+ const UserModel = mongoose.model('User', userSchema);
45
+
46
+ // 3. Create routers. Pass either the model name or the mongoose.Model
47
+ // instance. Passing the instance preserves an explicit connection
48
+ // (useful for multi-connection setups) and registers it with the
49
+ // active runtime.
50
+ const userRouter = acl.createRouter('User', {
51
+ basePath: '/users',
52
+ operationAccess: { list: true, create: true, read: true, update: true, delete: true },
53
+ permissionSchema: { name: true, role: 'isAdmin', public: true },
54
+ });
55
+
36
56
  const fruitRouter = acl.createDataRouter('fruit', {
37
57
  basePath: '/fruit',
38
58
  idField: 'id',
39
59
  operationAccess: { list: true, read: true },
40
60
  data: [{ id: 'apple', name: 'Apple', public: true }],
41
- permissionSchema: {
42
- id: true,
43
- name: 'isAdmin',
44
- public: true,
45
- },
46
- });
47
-
48
- const userRouter = acl.createRouter('User', {
49
- basePath: '/users',
61
+ permissionSchema: { id: true, name: 'isAdmin', public: true },
50
62
  });
51
63
 
52
64
  const docsRouter = acl.createOpenApiRouter({
53
65
  title: 'Example API',
54
66
  version: '1.0.0',
55
67
  });
68
+
69
+ // 4. Mount routers under an Express app.
70
+ const app = express();
71
+ app.use(express.json());
72
+ app.use(userRouter.routes);
73
+ app.use(fruitRouter.routes);
74
+ app.use(docsRouter);
75
+
76
+ // 5. Connect to MongoDB before accepting traffic. A failed connection throws
77
+ // and exits before the server calls `app.listen`, so the service never
78
+ // publishes routes it cannot serve.
79
+ const port = Number(process.env.PORT ?? 3000);
80
+ const mongoUrl = process.env.MONGODB_URL ?? 'mongodb://localhost:27017/example';
81
+ try {
82
+ await mongoose.connect(mongoUrl);
83
+ } catch (err) {
84
+ console.error(`Failed to connect to MongoDB at ${mongoUrl}:`, err);
85
+ process.exit(1);
86
+ }
87
+
88
+ app.listen(port, () => {
89
+ console.log(`API listening on http://localhost:${port}`);
90
+ });
56
91
  ```
57
92
 
58
93
  ## Main Exports
59
94
 
60
95
  Root entrypoint (`@web-ts-toolkit/access-router`):
61
96
 
62
- - default export `acl`
63
- - `createAccessRuntime()`
64
- - `createOpenApiRouter(...)`
65
- - `combineRoutes(...)`
66
- - `guard(...)`
67
- - `RootRouter`, `ModelRouter`, `DataRouter`
68
- - validation adapters such as `fromZod(...)` and `defineRequestSchema(...)`
97
+ - default export `acl` — the default-runtime API (functions bound to the shared `AccessRuntime`)
98
+ - `createAccessRuntime()` — create an isolated runtime with its own options and model registry
99
+ - `AccessRuntime`, `RootRouter`, `ModelRouter`, `DataRouter` — router/runtime classes
100
+ - `registerModelInstance(name, model)`, `hasModelInstance(name)`, `getModelInstance(name)` — explicit runtime-owned model registry helpers
101
+ - `guard(...)` and `GuardModelCondition`, `GuardModelConditionID` types
102
+ - `combineRoutes(...)` and `createOpenApiRouter(runtime, options)` (note: prefer `acl.createOpenApiRouter(options)` for the default runtime)
103
+ - validation adapters: `defineRequestSchema(...)`, `fromZod(...)`, `fromYup(...)`, `fromJoi(...)`, `fromAjv(...)`, `fromStandardSchema(...)`, `fromValibot(...)`, `fromArkType(...)`, `fromIoTs(...)`, `fromSuperstruct(...)`, `fromVine(...)`
104
+ - option helpers: `setGlobalOptions`, `setGlobalOption`, `getGlobalOptions`, `getGlobalOption`, `setModelOptions`, `setModelOption`, `getModelOptions`, `getModelOption`, `getModelNames`, `getModelJsonSchema`, `setDefaultModelOptions`, `setDefaultModelOption`, `getDefaultModelOptions`, `getDefaultModelOption`
69
105
 
70
106
  Subpath entrypoints:
71
107
 
72
- - `@web-ts-toolkit/access-router/advanced` — runtime context and advanced internals
73
- - `@web-ts-toolkit/access-router/processors` — field processors
108
+ - `@web-ts-toolkit/access-router/advanced` — low-level runtime context, symbols (`MIDDLEWARE`, `PERMISSIONS`, ...), enums (`Codes`, `StatusCodes`), internals (`parseBody`, `parseQuery`, request schemas). Does NOT export `acl`, `defaultRuntime`, or router-creation helpers.
109
+ - `@web-ts-toolkit/access-router/processors` — `copyAndDepopulate` and its `ProcessCopy` / `CopyAndDepopulateOptions` types for transforming populated documents.
110
+
111
+ ## Default runtime vs. isolated runtime
112
+
113
+ The default export `acl` is bound to a single shared `AccessRuntime`, which is fine for most services.
114
+ For tests, multi-tenant services, or libraries that must avoid global state, create an isolated runtime:
115
+
116
+ ```ts
117
+ import { createAccessRuntime } from '@web-ts-toolkit/access-router';
118
+
119
+ const runtime = createAccessRuntime();
120
+ runtime.setGlobalOptions({ globalPermissions: () => [] });
121
+
122
+ // `runtime.createRouter(...)`, `runtime.createDataRouter(...)`,
123
+ // `runtime.createOpenApiRouter(...)`, `runtime.registerModelInstance(...)`, ...
124
+ ```
125
+
126
+ Two isolated runtimes with the same model name resolve against their own model registry and options without interference.
127
+
128
+ ## createRouter overloads
129
+
130
+ `createRouter(modelName, options)` — accept the Mongoose model name registered with `mongoose.model(name, schema)`.
131
+
132
+ `createRouter(model, options)` — accept a `mongoose.Model` instance directly. The instance is registered with the active runtime's registry, so a model attached to a non-default `mongoose.createConnection()` works without polluting the global registry.
133
+
134
+ ```ts
135
+ import mongoose from 'mongoose';
136
+ import acl, { permissionsPlugin } from '@web-ts-toolkit/access-router';
74
137
 
75
- ### Import styles
138
+ // `uri` is the MongoDB connection string for the non-default connection.
139
+ const uri = process.env.MONGODB_URL_TENANT ?? 'mongodb://localhost:27017/tenant';
140
+ const conn = await mongoose.createConnection(uri).asPromise();
141
+ const schema = new mongoose.Schema({ name: String });
142
+ schema.plugin(permissionsPlugin, { modelName: 'TenantUser' });
143
+ const TenantUser = conn.model('TenantUser', schema);
144
+
145
+ const tenantRouter = acl.createRouter(TenantUser, { basePath: '/tenant-users' });
146
+ ```
147
+
148
+ ## Import styles
76
149
 
77
150
  The package ships both a default export and named exports:
78
151
 
@@ -81,32 +154,23 @@ The package ships both a default export and named exports:
81
154
  import acl from '@web-ts-toolkit/access-router';
82
155
 
83
156
  // named exports (useful when you only need specific helpers)
84
- import { createOpenApiRouter, fromZod } from '@web-ts-toolkit/access-router';
157
+ import { createAccessRuntime, fromZod } from '@web-ts-toolkit/access-router';
85
158
  ```
86
159
 
87
160
  ### Subpath import example
88
161
 
89
162
  ```ts
90
- import { setGlobalOptions } from '@web-ts-toolkit/access-router';
91
163
  import { copyAndDepopulate } from '@web-ts-toolkit/access-router/processors';
92
164
 
93
- setGlobalOptions({
94
- globalPermissions(req) {
95
- return [];
96
- },
97
- });
98
-
99
- const depopulated = copyAndDepopulate({ items: [{ _id: 'a1', name: 'Apple' }] }, [{ src: 'items', dest: 'items' }]);
165
+ const { items } = copyAndDepopulate(
166
+ { items: [{ _id: 'a1', name: 'Apple' }] },
167
+ [{ src: 'items', dest: 'itemsSnapshot' }],
168
+ { mutable: false },
169
+ );
170
+ // `items` is now `['a1']`; `itemsSnapshot` holds the original objects.
100
171
  ```
101
172
 
102
173
  ## Documentation
103
174
 
104
- Full package documentation lives in `website/docs/packages/access-router/`.
105
-
106
175
  - live docs: https://web-ts-toolkit.pages.dev/docs/packages/access-router
107
- - overview: `website/docs/packages/access-router/index.mdx`
108
- - routing: `website/docs/packages/access-router/routing.mdx`
109
- - configuration: `website/docs/packages/access-router/configuration.mdx`
110
- - hooks: `website/docs/packages/access-router/hooks.mdx`
111
- - validation: `website/docs/packages/access-router/validation.mdx`
112
- - OpenAPI: `website/docs/packages/access-router/openapi.mdx`
176
+ - source docs live in the website/docs package of this repository; paths may move as the docs site evolves.