prisma-generator-express 1.64.2 → 1.64.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generators/generateRouteConfigType.js +2 -1
- package/dist/generators/generateRouteConfigType.js.map +1 -1
- package/dist/generators/generateRouterHono.js +132 -70
- package/dist/generators/generateRouterHono.js.map +1 -1
- package/dist/guardOptions.d.ts +30 -0
- package/dist/guardOptions.js +123 -0
- package/dist/guardOptions.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/copy/routeConfig.hono.ts +85 -0
- package/src/copy/routeConfig.ts +148 -20
- package/src/generators/generateRouteConfigType.ts +6 -1
- package/src/generators/generateRouterHono.ts +162 -78
- package/src/guardOptions.ts +226 -0
- package/src/index.ts +19 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PUBLIC, COMPILED METADATA for the Hono guard controls.
|
|
3
|
+
*
|
|
4
|
+
* **Why this file exists separately from `src/copy/routeConfig.ts`.**
|
|
5
|
+
* `src/copy/**` is excluded from the TypeScript build (`tsconfig.json`) because
|
|
6
|
+
* those files are copied verbatim into a generated project rather than compiled
|
|
7
|
+
* into this package. So anything defined only there is absent from `dist` and
|
|
8
|
+
* unreachable through the package entry point — which made the first version of
|
|
9
|
+
* this metadata unusable by the very consumer it was written for.
|
|
10
|
+
*
|
|
11
|
+
* This module is compiled, exported from `src/index.ts`, and therefore importable
|
|
12
|
+
* as `require('prisma-generator-express').GUARD_OPTION_METADATA` from an
|
|
13
|
+
* installed package. `test/consumer/packedMetadata.test.ts` proves that by packing
|
|
14
|
+
* the tarball and importing it the way a consumer would.
|
|
15
|
+
*
|
|
16
|
+
* **Scope, stated narrowly.** This is authoritative for the seven M8.7 GUARD
|
|
17
|
+
* controls and nothing else. The generator has many other public settings that
|
|
18
|
+
* change the emitted API — `dropGuard`, the target, the write strategy, the
|
|
19
|
+
* pagination mode, prefixes, OpenAPI options, the query-builder toggle — and none
|
|
20
|
+
* of them are described here yet. A configuration UI cannot build a complete
|
|
21
|
+
* screen from this file alone, and should not pretend otherwise.
|
|
22
|
+
*
|
|
23
|
+
* **Duplication is deliberate and guarded.** The runtime defaults live in
|
|
24
|
+
* `src/copy/routeConfig.ts` because the generated project needs them with no
|
|
25
|
+
* dependency on this package. They cannot import each other across the compile
|
|
26
|
+
* boundary, so the values appear twice and
|
|
27
|
+
* `test/unit/copy/guardOptionIndependence.test.ts` asserts the two agree, against
|
|
28
|
+
* the resolver the router actually runs. Drift fails a test rather than shipping a
|
|
29
|
+
* UI that advertises a default the code does not apply.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
export type GuardResolutionOrder = 'after-hooks' | 'before-hooks'
|
|
33
|
+
|
|
34
|
+
export type GuardPolicy = {
|
|
35
|
+
requireGuardShape: boolean
|
|
36
|
+
validateGuardShapes: boolean
|
|
37
|
+
requireDefaultVariantOptIn: boolean
|
|
38
|
+
enableUpdateEach: boolean
|
|
39
|
+
guardResolutionOrder: GuardResolutionOrder
|
|
40
|
+
allowE2EGuardBypass: boolean
|
|
41
|
+
validateResolvedShapes: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The public package's pre-1.64.2 behaviour, exactly.
|
|
46
|
+
*
|
|
47
|
+
* An unset option resolves to the value here. Changing one changes what every
|
|
48
|
+
* consumer who configured nothing gets on upgrade, which is the failure 1.64.2
|
|
49
|
+
* shipped and this table exists to prevent.
|
|
50
|
+
*/
|
|
51
|
+
export const UPSTREAM_GUARD_DEFAULTS: Readonly<GuardPolicy> = Object.freeze({
|
|
52
|
+
requireGuardShape: false,
|
|
53
|
+
validateGuardShapes: false,
|
|
54
|
+
requireDefaultVariantOptIn: false,
|
|
55
|
+
enableUpdateEach: true,
|
|
56
|
+
guardResolutionOrder: 'after-hooks',
|
|
57
|
+
allowE2EGuardBypass: true,
|
|
58
|
+
validateResolvedShapes: false,
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Every control at its most restrictive — a SHORTCUT, not a mode.
|
|
63
|
+
*
|
|
64
|
+
* Spread it, change what you disagree with, store the result. There is
|
|
65
|
+
* deliberately no `preset: 'hardened'` option anywhere in this package: a stored
|
|
66
|
+
* preset NAME would let a future version silently change what an existing
|
|
67
|
+
* configuration does, which is the same class of mistake as 1.64.2.
|
|
68
|
+
*/
|
|
69
|
+
export const HARDENED_GUARD_PROFILE: Readonly<GuardPolicy> = Object.freeze({
|
|
70
|
+
requireGuardShape: true,
|
|
71
|
+
validateGuardShapes: true,
|
|
72
|
+
requireDefaultVariantOptIn: true,
|
|
73
|
+
enableUpdateEach: false,
|
|
74
|
+
guardResolutionOrder: 'before-hooks',
|
|
75
|
+
allowE2EGuardBypass: false,
|
|
76
|
+
validateResolvedShapes: true,
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
export type GuardOptionMetadata = {
|
|
80
|
+
name: keyof GuardPolicy
|
|
81
|
+
type: 'boolean' | 'enum'
|
|
82
|
+
values?: readonly string[]
|
|
83
|
+
default: boolean | string
|
|
84
|
+
hardened: boolean | string
|
|
85
|
+
label: string
|
|
86
|
+
description: string
|
|
87
|
+
/** Present where the permissive value can emit an unguarded or caller-selected route. */
|
|
88
|
+
warning: string
|
|
89
|
+
/** The target whose generated router reads this control. */
|
|
90
|
+
target: 'hono'
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Machine-readable description of the seven guard controls.
|
|
95
|
+
*
|
|
96
|
+
* `target: 'hono'` on every entry is not decoration. These options are declared
|
|
97
|
+
* on the Hono route configuration only, because only the Hono generator reads
|
|
98
|
+
* them; declaring them on the shared base made Express and Fastify accept them in
|
|
99
|
+
* TypeScript and ignore them at runtime, which is a false public API.
|
|
100
|
+
*/
|
|
101
|
+
export const GUARD_OPTION_METADATA: readonly GuardOptionMetadata[] = Object.freeze([
|
|
102
|
+
{
|
|
103
|
+
name: 'requireGuardShape',
|
|
104
|
+
type: 'boolean',
|
|
105
|
+
default: UPSTREAM_GUARD_DEFAULTS.requireGuardShape,
|
|
106
|
+
hardened: HARDENED_GUARD_PROFILE.requireGuardShape,
|
|
107
|
+
label: 'Require a guard on every enabled operation',
|
|
108
|
+
description:
|
|
109
|
+
'Refuses at router construction when an operation defines neither "shape" nor "variants".',
|
|
110
|
+
warning:
|
|
111
|
+
"Off, an operation nobody configured passes the caller's own where/select/include straight to Prisma.",
|
|
112
|
+
target: 'hono',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'validateGuardShapes',
|
|
116
|
+
type: 'boolean',
|
|
117
|
+
default: UPSTREAM_GUARD_DEFAULTS.validateGuardShapes,
|
|
118
|
+
hardened: HARDENED_GUARD_PROFILE.validateGuardShapes,
|
|
119
|
+
label: 'Validate guard shapes',
|
|
120
|
+
description:
|
|
121
|
+
'Refuses an empty shape, and one mixing guard keys with non-guard keys, at the top level and inside variants.',
|
|
122
|
+
warning:
|
|
123
|
+
'Off, "shape: {}" reads as guarded and constrains nothing. On, strict validation may reject existing route configs.',
|
|
124
|
+
target: 'hono',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'requireDefaultVariantOptIn',
|
|
128
|
+
type: 'boolean',
|
|
129
|
+
default: UPSTREAM_GUARD_DEFAULTS.requireDefaultVariantOptIn,
|
|
130
|
+
hardened: HARDENED_GUARD_PROFILE.requireDefaultVariantOptIn,
|
|
131
|
+
label: 'Require confirmation for a "default" variant',
|
|
132
|
+
description:
|
|
133
|
+
'A variant named "default" must set allowDefaultVariant: true to be accepted.',
|
|
134
|
+
warning: 'A "default" variant answers every unrecognised, blank and missing caller.',
|
|
135
|
+
target: 'hono',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'enableUpdateEach',
|
|
139
|
+
type: 'boolean',
|
|
140
|
+
default: UPSTREAM_GUARD_DEFAULTS.enableUpdateEach,
|
|
141
|
+
hardened: HARDENED_GUARD_PROFILE.enableUpdateEach,
|
|
142
|
+
label: 'Register the updateEach batch route',
|
|
143
|
+
description: 'Mounts POST <base>/each. Set false to refuse it at router construction.',
|
|
144
|
+
warning:
|
|
145
|
+
'updateEach bypasses guard shapes entirely; only a development-only warning stands between it and an unguarded mass update.',
|
|
146
|
+
target: 'hono',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: 'guardResolutionOrder',
|
|
150
|
+
type: 'enum',
|
|
151
|
+
values: Object.freeze(['after-hooks', 'before-hooks']),
|
|
152
|
+
default: UPSTREAM_GUARD_DEFAULTS.guardResolutionOrder,
|
|
153
|
+
hardened: HARDENED_GUARD_PROFILE.guardResolutionOrder,
|
|
154
|
+
label: 'When guard failures are raised',
|
|
155
|
+
description:
|
|
156
|
+
'after-hooks is the upstream behaviour; before-hooks settles variant and shape resolution before any operation hook runs.',
|
|
157
|
+
warning:
|
|
158
|
+
'With after-hooks, a hook that returns a Response can answer a request whose guard was never established.',
|
|
159
|
+
target: 'hono',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'allowE2EGuardBypass',
|
|
163
|
+
type: 'boolean',
|
|
164
|
+
default: UPSTREAM_GUARD_DEFAULTS.allowE2EGuardBypass,
|
|
165
|
+
hardened: HARDENED_GUARD_PROFILE.allowE2EGuardBypass,
|
|
166
|
+
label: 'Honour the E2E environment guard bypass',
|
|
167
|
+
description: 'Treats E2E=true in the environment as a request to drop the guard.',
|
|
168
|
+
warning:
|
|
169
|
+
'A runtime variable can weaken a deployed artifact after generation. On an edge runtime this is an ordinary config var.',
|
|
170
|
+
target: 'hono',
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: 'validateResolvedShapes',
|
|
174
|
+
type: 'boolean',
|
|
175
|
+
default: UPSTREAM_GUARD_DEFAULTS.validateResolvedShapes,
|
|
176
|
+
hardened: HARDENED_GUARD_PROFILE.validateResolvedShapes,
|
|
177
|
+
label: 'Validate what a shape function returns',
|
|
178
|
+
description:
|
|
179
|
+
'Checks a dynamically resolved shape before use and fails the request with 500 if it is unusable. Also resolves it exactly once.',
|
|
180
|
+
warning:
|
|
181
|
+
'Off, a shape function returning {} or undefined runs the operation unguarded, once per request.',
|
|
182
|
+
target: 'hono',
|
|
183
|
+
},
|
|
184
|
+
])
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* What this metadata does NOT cover.
|
|
188
|
+
*
|
|
189
|
+
* Exported so a consumer can display the gap rather than infer completeness from
|
|
190
|
+
* silence, and so a test can assert the claim is narrow. Every name here is a
|
|
191
|
+
* public setting that changes the emitted API and has no entry above.
|
|
192
|
+
*/
|
|
193
|
+
export const GUARD_METADATA_SCOPE = Object.freeze({
|
|
194
|
+
covers: 'the seven M8.7 guard controls on the Hono target',
|
|
195
|
+
complete: false,
|
|
196
|
+
notDescribedYet: Object.freeze([
|
|
197
|
+
'dropGuard',
|
|
198
|
+
'target',
|
|
199
|
+
'writeStrategy',
|
|
200
|
+
'findManyPaginated / pagination mode',
|
|
201
|
+
'pagination.countSource',
|
|
202
|
+
'addModelPrefix / customUrlPrefix / specBasePath',
|
|
203
|
+
'disableOpenApi and the OpenAPI metadata options',
|
|
204
|
+
'disablePostReads',
|
|
205
|
+
'queryBuilder',
|
|
206
|
+
'guard.resolveVariant / guard.variantHeader',
|
|
207
|
+
]),
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
/** Fill in the defaults. Absent means upstream, never an opinion. */
|
|
211
|
+
export function resolveGuardPolicy(config: Partial<GuardPolicy> | undefined): GuardPolicy {
|
|
212
|
+
return {
|
|
213
|
+
requireGuardShape: config?.requireGuardShape ?? UPSTREAM_GUARD_DEFAULTS.requireGuardShape,
|
|
214
|
+
validateGuardShapes:
|
|
215
|
+
config?.validateGuardShapes ?? UPSTREAM_GUARD_DEFAULTS.validateGuardShapes,
|
|
216
|
+
requireDefaultVariantOptIn:
|
|
217
|
+
config?.requireDefaultVariantOptIn ?? UPSTREAM_GUARD_DEFAULTS.requireDefaultVariantOptIn,
|
|
218
|
+
enableUpdateEach: config?.enableUpdateEach ?? UPSTREAM_GUARD_DEFAULTS.enableUpdateEach,
|
|
219
|
+
guardResolutionOrder:
|
|
220
|
+
config?.guardResolutionOrder ?? UPSTREAM_GUARD_DEFAULTS.guardResolutionOrder,
|
|
221
|
+
allowE2EGuardBypass:
|
|
222
|
+
config?.allowE2EGuardBypass ?? UPSTREAM_GUARD_DEFAULTS.allowE2EGuardBypass,
|
|
223
|
+
validateResolvedShapes:
|
|
224
|
+
config?.validateResolvedShapes ?? UPSTREAM_GUARD_DEFAULTS.validateResolvedShapes,
|
|
225
|
+
}
|
|
226
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public, compiled configuration metadata (M8.7).
|
|
3
|
+
*
|
|
4
|
+
* Re-exported here because `src/copy/**` is excluded from the build, so anything
|
|
5
|
+
* defined only there never reaches `dist` and cannot be imported from an
|
|
6
|
+
* installed package. See src/guardOptions.ts.
|
|
7
|
+
*
|
|
8
|
+
* Authoritative for the seven Hono guard controls only — `GUARD_METADATA_SCOPE`
|
|
9
|
+
* lists what is not described yet.
|
|
10
|
+
*/
|
|
11
|
+
export {
|
|
12
|
+
GUARD_OPTION_METADATA,
|
|
13
|
+
GUARD_METADATA_SCOPE,
|
|
14
|
+
UPSTREAM_GUARD_DEFAULTS,
|
|
15
|
+
HARDENED_GUARD_PROFILE,
|
|
16
|
+
resolveGuardPolicy,
|
|
17
|
+
} from './guardOptions'
|
|
18
|
+
export type { GuardPolicy, GuardOptionMetadata, GuardResolutionOrder } from './guardOptions'
|
|
19
|
+
|
|
1
20
|
import {
|
|
2
21
|
generatorHandler,
|
|
3
22
|
GeneratorOptions,
|