@cerbos/orm-prisma 3.3.0 → 4.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.
- package/README.md +243 -10
- package/lib/index.d.ts +102 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1754 -155
- package/lib/index.js.map +1 -1
- package/package.json +24 -15
- package/src/adversarial.test.ts +1281 -0
- package/src/corpus.ts +414 -0
- package/src/generated/prisma-adversarial/browser.ts +54 -0
- package/src/generated/prisma-adversarial/client.ts +76 -0
- package/src/generated/{prisma → prisma-adversarial}/commonInputTypes.ts +131 -27
- package/src/generated/prisma-adversarial/enums.ts +15 -0
- package/src/generated/prisma-adversarial/internal/class.ts +264 -0
- package/src/generated/prisma-adversarial/internal/prismaNamespace.ts +1332 -0
- package/src/generated/{prisma → prisma-adversarial}/internal/prismaNamespaceBrowser.ts +45 -47
- package/src/generated/prisma-adversarial/models/AdversarialCategory.ts +1401 -0
- package/src/generated/prisma-adversarial/models/AdversarialInner.ts +1369 -0
- package/src/generated/prisma-adversarial/models/AdversarialLabel.ts +1272 -0
- package/src/generated/prisma-adversarial/models/AdversarialParent.ts +1474 -0
- package/src/generated/prisma-adversarial/models/AdversarialResource.ts +1789 -0
- package/src/generated/prisma-adversarial/models/AdversarialSubCategory.ts +1401 -0
- package/src/generated/prisma-adversarial/models/AdversarialTag.ts +1307 -0
- package/src/generated/prisma-adversarial/models.ts +18 -0
- package/src/generated/{prisma → prisma-adversarial-pg}/browser.ts +14 -19
- package/src/generated/{prisma → prisma-adversarial-pg}/client.ts +16 -21
- package/src/generated/prisma-adversarial-pg/commonInputTypes.ts +356 -0
- package/src/generated/prisma-adversarial-pg/internal/class.ts +264 -0
- package/src/generated/prisma-adversarial-pg/internal/prismaNamespace.ts +1371 -0
- package/src/generated/prisma-adversarial-pg/internal/prismaNamespaceBrowser.ts +177 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialCategory.ts +1404 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialInner.ts +1371 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialLabel.ts +1275 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialParent.ts +1476 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialResource.ts +1791 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialSubCategory.ts +1404 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialTag.ts +1310 -0
- package/src/generated/prisma-adversarial-pg/models.ts +18 -0
- package/src/generated/prisma-adversarial-pg-v6/index.d.ts +11830 -0
- package/src/generated/prisma-adversarial-v6/client.d.ts +1 -0
- package/src/generated/prisma-adversarial-v6/default.d.ts +1 -0
- package/src/generated/prisma-adversarial-v6/edge.d.ts +1 -0
- package/src/generated/prisma-adversarial-v6/index.d.ts +11769 -0
- package/src/generated/prisma-adversarial-v6/runtime/index-browser.d.ts +370 -0
- package/src/generated/prisma-adversarial-v6/runtime/library.d.ts +3982 -0
- package/src/generated/prisma-adversarial-v6/wasm.d.ts +1 -0
- package/src/index.ts +2515 -219
- package/src/test-setup.adversarial-url.ts +18 -0
- package/src/test-setup.adversarial.pg.v6.ts +13 -0
- package/src/test-setup.adversarial.pg.v7.ts +9 -0
- package/src/test-setup.adversarial.v6.ts +5 -0
- package/src/test-setup.adversarial.v7.ts +12 -0
- package/src/translator.test.ts +1586 -0
- package/src/generated/prisma/internal/class.ts +0 -274
- package/src/generated/prisma/internal/prismaNamespace.ts +0 -1333
- package/src/generated/prisma/models/Category.ts +0 -1412
- package/src/generated/prisma/models/Label.ts +0 -1276
- package/src/generated/prisma/models/NestedResource.ts +0 -1562
- package/src/generated/prisma/models/NextLevelNestedResource.ts +0 -1337
- package/src/generated/prisma/models/Resource.ts +0 -2136
- package/src/generated/prisma/models/SubCategory.ts +0 -1412
- package/src/generated/prisma/models/Tag.ts +0 -1276
- package/src/generated/prisma/models/User.ts +0 -1503
- package/src/generated/prisma/models.ts +0 -19
- package/src/generated/prisma-v6/index.d.ts +0 -13639
- package/src/index.test.ts +0 -6322
- package/src/test-setup.v6.ts +0 -5
- package/src/test-setup.v7.ts +0 -7
- /package/src/generated/{prisma → prisma-adversarial-pg}/enums.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-pg-v6}/client.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-pg-v6}/default.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-pg-v6}/edge.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-pg-v6}/runtime/index-browser.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-pg-v6}/runtime/library.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-pg-v6}/wasm.d.ts +0 -0
package/src/corpus.ts
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
PlanExpression,
|
|
6
|
+
PlanExpressionValue,
|
|
7
|
+
PlanExpressionVariable,
|
|
8
|
+
} from "@cerbos/core";
|
|
9
|
+
import type {
|
|
10
|
+
PlanExpressionOperand,
|
|
11
|
+
PlanResourcesResponse,
|
|
12
|
+
Value,
|
|
13
|
+
} from "@cerbos/core";
|
|
14
|
+
|
|
15
|
+
import { PlanKind, MapperConfig } from ".";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The parts of the shared `../conformance/` corpus that both of this adapter's suites read.
|
|
19
|
+
*
|
|
20
|
+
* `adversarial.test.ts` plans against a real PDP and executes the translated query;
|
|
21
|
+
* `translator.test.ts` reads the same actions off the golden wire fixtures and asserts nothing
|
|
22
|
+
* but the emitted filter. They must agree on two things or they prove less than they appear to:
|
|
23
|
+
*
|
|
24
|
+
* - **the mapper.** The unit test pins the filter this adapter emits for a mapping; the harness
|
|
25
|
+
* proves that same filter returns the rows the PDP allows. Two copies that drifted would leave
|
|
26
|
+
* the pinned filters describing a mapping no harness ever executes.
|
|
27
|
+
* - **the classification.** Which actions this adapter must refuse, and with which message, is a
|
|
28
|
+
* corpus decision (`actions.json`), not a per-suite one.
|
|
29
|
+
*
|
|
30
|
+
* Test-only: excluded from `tsc --build` by `tsconfig.json`, so nothing here reaches `lib/`.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export const CONFORMANCE_DIR = path.join(
|
|
34
|
+
__dirname,
|
|
35
|
+
"..",
|
|
36
|
+
"..",
|
|
37
|
+
"conformance"
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const WIRE_FIXTURES_DIR = path.join(CONFORMANCE_DIR, "wire-fixtures");
|
|
41
|
+
|
|
42
|
+
/** The Prisma model the corpus's resource kind maps onto. */
|
|
43
|
+
export const MODEL = "AdversarialResource";
|
|
44
|
+
|
|
45
|
+
// -- actions.json --------------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
export interface UnsupportedShape {
|
|
48
|
+
action: string;
|
|
49
|
+
shape: string;
|
|
50
|
+
/** One entry per adapter that must reject the shape; the corpus asserts the key set. */
|
|
51
|
+
messages: Record<string, string>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface AdapterUnsupportedEntry {
|
|
55
|
+
action: string;
|
|
56
|
+
reason: string;
|
|
57
|
+
/** Absent on `adapterSupportedExpected` / `nullRepresentationOmitted`, required on a throw. */
|
|
58
|
+
message?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* A `nullRepresentationOmitted` entry. Every adapter must reject these — the two NULL conventions
|
|
63
|
+
* are indistinguishable on the wire — so `messages` names the whole roster with no promotions to
|
|
64
|
+
* subtract.
|
|
65
|
+
*/
|
|
66
|
+
export interface NullRepresentationOmittedEntry {
|
|
67
|
+
action: string;
|
|
68
|
+
reason: string;
|
|
69
|
+
messages: Record<string, string>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface KnownDivergence {
|
|
73
|
+
action: string;
|
|
74
|
+
adapters: string[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ActionsFile {
|
|
78
|
+
conformance: string[];
|
|
79
|
+
adapterUnsupported?: Record<string, AdapterUnsupportedEntry[]>;
|
|
80
|
+
adapterSupportedExpected?: Record<string, AdapterUnsupportedEntry[]>;
|
|
81
|
+
expectedUnsupported: UnsupportedShape[];
|
|
82
|
+
nullRepresentationOmitted: NullRepresentationOmittedEntry[];
|
|
83
|
+
knownDivergences?: KnownDivergence[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* A shape this adapter must refuse, with the substring its error has to contain.
|
|
88
|
+
*
|
|
89
|
+
* The message is what turns "it threw" into "it threw for the declared reason": without it a
|
|
90
|
+
* mapper typo or an unrelated validation satisfies the assertion just as well as the limitation
|
|
91
|
+
* the corpus documents (cerbos/query-plan-adapters#326).
|
|
92
|
+
*/
|
|
93
|
+
export type ThrowingAction = readonly [
|
|
94
|
+
action: string,
|
|
95
|
+
reason: string,
|
|
96
|
+
message: string,
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
export interface ActionClassification {
|
|
100
|
+
oracleActions: string[];
|
|
101
|
+
throwingActions: ThrowingAction[];
|
|
102
|
+
supportedExpected: Set<string>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The pinned message, or a failure — a throwing action without one asserts nothing. */
|
|
106
|
+
export function requireMessage(
|
|
107
|
+
label: string,
|
|
108
|
+
message: string | undefined
|
|
109
|
+
): string {
|
|
110
|
+
if (message === undefined || message === "") {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`actions.json pins no throw message for ${label}: the throw suite would accept a failure for any reason`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return message;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function classifyActionsForAdapter(
|
|
119
|
+
manifest: ActionsFile,
|
|
120
|
+
adapter: string
|
|
121
|
+
): ActionClassification {
|
|
122
|
+
const unsupported = manifest.adapterUnsupported?.[adapter] ?? [];
|
|
123
|
+
const unsupportedActions = new Set(unsupported.map((entry) => entry.action));
|
|
124
|
+
const supportedExpected = new Set(
|
|
125
|
+
(manifest.adapterSupportedExpected?.[adapter] ?? []).map(
|
|
126
|
+
(entry) => entry.action
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
const oracleActions = [
|
|
130
|
+
...manifest.conformance.filter(
|
|
131
|
+
(action) => !unsupportedActions.has(action)
|
|
132
|
+
),
|
|
133
|
+
...supportedExpected,
|
|
134
|
+
];
|
|
135
|
+
const throwingActions: ThrowingAction[] = [
|
|
136
|
+
...unsupported.map(
|
|
137
|
+
(entry): ThrowingAction => [
|
|
138
|
+
entry.action,
|
|
139
|
+
entry.reason,
|
|
140
|
+
requireMessage(`adapterUnsupported.${adapter}.${entry.action}`, entry.message),
|
|
141
|
+
]
|
|
142
|
+
),
|
|
143
|
+
...manifest.expectedUnsupported
|
|
144
|
+
.filter((entry) => !supportedExpected.has(entry.action))
|
|
145
|
+
.map((entry): ThrowingAction => [
|
|
146
|
+
entry.action,
|
|
147
|
+
entry.shape,
|
|
148
|
+
requireMessage(
|
|
149
|
+
`expectedUnsupported.${entry.action}.messages.${adapter}`,
|
|
150
|
+
entry.messages?.[adapter]
|
|
151
|
+
),
|
|
152
|
+
]),
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
oracleActions: [...new Set(oracleActions)].sort(),
|
|
157
|
+
throwingActions: throwingActions.sort(([left], [right]) =>
|
|
158
|
+
left.localeCompare(right)
|
|
159
|
+
),
|
|
160
|
+
supportedExpected,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// -- the golden wire fixtures --------------------------------------------------------------------
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The instant `regenerate-wire-fixtures.sh` substitutes for the one operand it cannot pin.
|
|
168
|
+
*
|
|
169
|
+
* `ts-window` and `ts-vf` compare against `now() - duration("24h")`, which the planner folds to a
|
|
170
|
+
* literal timestamp: a different value on every capture, so the script rewrites it to
|
|
171
|
+
* `__NOW_MINUS_24H__` to keep the drift check deterministic. Reading the fixture back therefore
|
|
172
|
+
* means choosing a value, and the choice is load-bearing rather than arbitrary — Cerbos emits the
|
|
173
|
+
* PDP's clock at nanosecond precision, which is exactly why both actions are `adapterUnsupported`
|
|
174
|
+
* for this adapter (`Timestamp value exceeds millisecond precision`). A tidy millisecond instant
|
|
175
|
+
* here would translate cleanly and quietly contradict `actions.json`, so the fraction carries the
|
|
176
|
+
* nine digits a real plan carries. `translator.test.ts` pins both sides of that boundary, which is
|
|
177
|
+
* what the `plannedAt` override on `planFromWireFixture` is for.
|
|
178
|
+
*/
|
|
179
|
+
export const PLANNED_AT = "2026-08-11T09:13:39.123456789Z";
|
|
180
|
+
|
|
181
|
+
interface WireOperand {
|
|
182
|
+
expression?: { operator: string; operands: WireOperand[] };
|
|
183
|
+
variable?: string;
|
|
184
|
+
value?: unknown;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
interface WireFixture {
|
|
188
|
+
action: string;
|
|
189
|
+
resourceKind: string;
|
|
190
|
+
filter: { kind: string; condition?: WireOperand };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function operandFromWire(
|
|
194
|
+
node: WireOperand,
|
|
195
|
+
plannedAt: string
|
|
196
|
+
): PlanExpressionOperand {
|
|
197
|
+
if (node.expression) {
|
|
198
|
+
return new PlanExpression(
|
|
199
|
+
node.expression.operator,
|
|
200
|
+
node.expression.operands.map((child) => operandFromWire(child, plannedAt))
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
if (node.variable !== undefined) {
|
|
204
|
+
return new PlanExpressionVariable(node.variable);
|
|
205
|
+
}
|
|
206
|
+
if (!("value" in node)) {
|
|
207
|
+
throw new Error(
|
|
208
|
+
`Wire fixture operand is neither an expression, a variable nor a value: ${JSON.stringify(node)}`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
// The one cast in this file. A fixture is JSON the PDP produced, so its leaves are already
|
|
212
|
+
// exactly the JSON shapes `Value` admits — but `JSON.parse` cannot say so, and re-validating a
|
|
213
|
+
// file the corpus workflow regenerates and diffs would assert nothing new.
|
|
214
|
+
return new PlanExpressionValue(
|
|
215
|
+
(node.value === "__NOW_MINUS_24H__" ? plannedAt : node.value) as Value
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Every action the corpus has a golden wire fixture for, sorted. */
|
|
220
|
+
export function wireFixtureActions(): string[] {
|
|
221
|
+
return fs
|
|
222
|
+
.readdirSync(WIRE_FIXTURES_DIR)
|
|
223
|
+
.filter((name) => name.endsWith(".json"))
|
|
224
|
+
.map((name) => name.slice(0, -".json".length))
|
|
225
|
+
.sort();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The plan the pinned PDP produced for `action`, decoded into the shape the SDK hands callers.
|
|
230
|
+
*
|
|
231
|
+
* The fixture is the PDP's HTTP response, so the decoding here is the one `@cerbos/http` performs
|
|
232
|
+
* — `{expression|variable|value}` nodes into `PlanExpression` / `PlanExpressionVariable` /
|
|
233
|
+
* `PlanExpressionValue`. It is deliberately not a hand-built plan: a plan somebody typed is a
|
|
234
|
+
* belief about what the planner emits, and this repository keeps fixtures precisely because that
|
|
235
|
+
* belief has been wrong before. See docs/adr/0006.
|
|
236
|
+
*/
|
|
237
|
+
export function planFromWireFixture(
|
|
238
|
+
action: string,
|
|
239
|
+
plannedAt: string = PLANNED_AT
|
|
240
|
+
): PlanResourcesResponse {
|
|
241
|
+
const fixture: WireFixture = JSON.parse(
|
|
242
|
+
fs.readFileSync(path.join(WIRE_FIXTURES_DIR, `${action}.json`), "utf8")
|
|
243
|
+
);
|
|
244
|
+
const base = {
|
|
245
|
+
cerbosCallId: "",
|
|
246
|
+
requestId: "",
|
|
247
|
+
validationErrors: [],
|
|
248
|
+
metadata: undefined,
|
|
249
|
+
};
|
|
250
|
+
switch (fixture.filter.kind) {
|
|
251
|
+
case PlanKind.CONDITIONAL:
|
|
252
|
+
if (!fixture.filter.condition) {
|
|
253
|
+
throw new Error(`Wire fixture ${action} is conditional with no condition`);
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
...base,
|
|
257
|
+
kind: PlanKind.CONDITIONAL,
|
|
258
|
+
condition: operandFromWire(fixture.filter.condition, plannedAt),
|
|
259
|
+
};
|
|
260
|
+
case PlanKind.ALWAYS_ALLOWED:
|
|
261
|
+
case PlanKind.ALWAYS_DENIED:
|
|
262
|
+
return { ...base, kind: fixture.filter.kind };
|
|
263
|
+
default:
|
|
264
|
+
throw new Error(
|
|
265
|
+
`Wire fixture ${action} has an unrecognised filter kind ${fixture.filter.kind}`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// -- the mapper ----------------------------------------------------------------------------------
|
|
271
|
+
|
|
272
|
+
export const MAPPER: Record<string, MapperConfig> = {
|
|
273
|
+
// The primary key, reached as `request.resource.id` rather than through `attr` (the `id-*`
|
|
274
|
+
// actions). It is a mapping like any other here, which is the point: an adapter that resolves
|
|
275
|
+
// references by stripping a `request.resource.attr.` prefix never sees this name.
|
|
276
|
+
"request.resource.id": { field: "id" },
|
|
277
|
+
"request.resource.attr.aBool": { field: "aBool" },
|
|
278
|
+
"request.resource.attr.aString": { field: "aString" },
|
|
279
|
+
"request.resource.attr.aNumber": { field: "aNumber" },
|
|
280
|
+
"request.resource.attr.aDouble": { field: "aDouble" },
|
|
281
|
+
"request.resource.attr.aOptionalString": { field: "aOptionalString" },
|
|
282
|
+
"request.resource.attr.createdBy": { field: "createdBy" },
|
|
283
|
+
"request.resource.attr.scope": { field: "scope" },
|
|
284
|
+
"request.resource.attr.createdAt": {
|
|
285
|
+
field: "createdAt",
|
|
286
|
+
valueType: "dateTime",
|
|
287
|
+
},
|
|
288
|
+
// `owner` and `coOwner` alias columns that `aOptionalString` and `scope` also map, under the
|
|
289
|
+
// OTHER null convention: the oracle sends a real null attribute for them rather than omitting
|
|
290
|
+
// it. Declaring that here is what makes the equality family definite for these two attributes
|
|
291
|
+
// and leaves it untouched for every other mapping (cerbos/query-plan-adapters#308).
|
|
292
|
+
"request.resource.attr.owner": {
|
|
293
|
+
field: "aOptionalString",
|
|
294
|
+
nullable: true,
|
|
295
|
+
nullAttributeRepresentation: "explicit",
|
|
296
|
+
},
|
|
297
|
+
"request.resource.attr.coOwner": {
|
|
298
|
+
field: "scope",
|
|
299
|
+
nullable: true,
|
|
300
|
+
nullAttributeRepresentation: "explicit",
|
|
301
|
+
},
|
|
302
|
+
// obj.inner is not a real nested column — mirrors aString, same trick the spring-data
|
|
303
|
+
// reference harness uses for the p-struct probe. `parent.inner` below is the opposite: a real
|
|
304
|
+
// two-level join. The two are kept side by side on purpose.
|
|
305
|
+
"request.resource.attr.obj.inner": { field: "aString" },
|
|
306
|
+
// The corpus's one REAL to-one chain (the `rel-*` actions). `type: "one"` is what makes the
|
|
307
|
+
// adapter emit `is:` rather than `some:`, and `is:` on an optional relation is what requires
|
|
308
|
+
// the hop to exist — the absent-parent guard the negated shapes discriminate. `inner` nests
|
|
309
|
+
// the same declaration one level further out; two levels is where alias scoping breaks.
|
|
310
|
+
"request.resource.attr.parent": {
|
|
311
|
+
relation: {
|
|
312
|
+
name: "parent",
|
|
313
|
+
type: "one",
|
|
314
|
+
model: "AdversarialParent",
|
|
315
|
+
fields: {
|
|
316
|
+
aBool: { field: "aBool" },
|
|
317
|
+
aString: { field: "aString" },
|
|
318
|
+
aNumber: { field: "aNumber" },
|
|
319
|
+
aOptionalString: { field: "aOptionalString", nullable: true },
|
|
320
|
+
inner: {
|
|
321
|
+
relation: {
|
|
322
|
+
name: "inner",
|
|
323
|
+
type: "one",
|
|
324
|
+
model: "AdversarialInner",
|
|
325
|
+
fields: {
|
|
326
|
+
aBool: { field: "aBool" },
|
|
327
|
+
aString: { field: "aString" },
|
|
328
|
+
aNumber: { field: "aNumber" },
|
|
329
|
+
aOptionalString: { field: "aOptionalString", nullable: true },
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
"request.resource.attr.tags": {
|
|
337
|
+
relation: {
|
|
338
|
+
name: "tags",
|
|
339
|
+
type: "many",
|
|
340
|
+
// Model name enables field-to-field comparisons between tag columns; the nullable
|
|
341
|
+
// flag on `name` enables the adapter's three-valued-logic guards for collection
|
|
342
|
+
// macros over elements whose name column is NULL (a missing attribute — a CEL
|
|
343
|
+
// error, hence deny — on the check side).
|
|
344
|
+
model: "AdversarialTag",
|
|
345
|
+
fields: {
|
|
346
|
+
id: { field: "tagId" },
|
|
347
|
+
name: { field: "name", nullable: true },
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
"request.resource.attr.tagNames": {
|
|
352
|
+
relation: {
|
|
353
|
+
name: "tags",
|
|
354
|
+
type: "many",
|
|
355
|
+
field: "name",
|
|
356
|
+
fields: { name: { field: "name", nullable: true } },
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
"request.resource.attr.categories": {
|
|
360
|
+
relation: {
|
|
361
|
+
name: "categories",
|
|
362
|
+
type: "many",
|
|
363
|
+
fields: {
|
|
364
|
+
name: { field: "name" },
|
|
365
|
+
subCategories: {
|
|
366
|
+
relation: {
|
|
367
|
+
name: "subCategories",
|
|
368
|
+
type: "many",
|
|
369
|
+
fields: {
|
|
370
|
+
name: { field: "name" },
|
|
371
|
+
labels: {
|
|
372
|
+
relation: {
|
|
373
|
+
name: "labels",
|
|
374
|
+
type: "many",
|
|
375
|
+
fields: {
|
|
376
|
+
name: { field: "name", nullable: true },
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
// Multi-hop chain probe (W1): mainCategory mirrors the SAME categories/subCategories relation
|
|
387
|
+
// as a single-object dotted chain on the check side (every seed holds at most one category),
|
|
388
|
+
// pinning that the adapter joins through every intermediate hop, never off the root.
|
|
389
|
+
"request.resource.attr.mainCategory": {
|
|
390
|
+
relation: {
|
|
391
|
+
name: "categories",
|
|
392
|
+
type: "many",
|
|
393
|
+
fields: {
|
|
394
|
+
name: { field: "name" },
|
|
395
|
+
subCategories: {
|
|
396
|
+
relation: {
|
|
397
|
+
name: "subCategories",
|
|
398
|
+
type: "many",
|
|
399
|
+
fields: { name: { field: "name" } },
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
// subNames: the same 2-hop chain but with a bare `field`, so plain `in` membership
|
|
403
|
+
// compares the flattened tail's name column directly.
|
|
404
|
+
subNames: {
|
|
405
|
+
relation: {
|
|
406
|
+
name: "subCategories",
|
|
407
|
+
type: "many",
|
|
408
|
+
field: "name",
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
|
|
2
|
+
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
// biome-ignore-all lint: generated file
|
|
5
|
+
// @ts-nocheck
|
|
6
|
+
/*
|
|
7
|
+
* This file should be your main import to use Prisma-related types and utilities in a browser.
|
|
8
|
+
* Use it to get access to models, enums, and input types.
|
|
9
|
+
*
|
|
10
|
+
* This file does not contain a `PrismaClient` class, nor several other helpers that are intended as server-side only.
|
|
11
|
+
* See `client.ts` for the standard, server-side entry point.
|
|
12
|
+
*
|
|
13
|
+
* 🟢 You can import this file directly.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as Prisma from './internal/prismaNamespaceBrowser.js'
|
|
17
|
+
export { Prisma }
|
|
18
|
+
export * as $Enums from './enums.js'
|
|
19
|
+
export * from './enums.js';
|
|
20
|
+
/**
|
|
21
|
+
* Model AdversarialResource
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
24
|
+
export type AdversarialResource = Prisma.AdversarialResourceModel
|
|
25
|
+
/**
|
|
26
|
+
* Model AdversarialTag
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
export type AdversarialTag = Prisma.AdversarialTagModel
|
|
30
|
+
/**
|
|
31
|
+
* Model AdversarialCategory
|
|
32
|
+
*
|
|
33
|
+
*/
|
|
34
|
+
export type AdversarialCategory = Prisma.AdversarialCategoryModel
|
|
35
|
+
/**
|
|
36
|
+
* Model AdversarialSubCategory
|
|
37
|
+
*
|
|
38
|
+
*/
|
|
39
|
+
export type AdversarialSubCategory = Prisma.AdversarialSubCategoryModel
|
|
40
|
+
/**
|
|
41
|
+
* Model AdversarialLabel
|
|
42
|
+
*
|
|
43
|
+
*/
|
|
44
|
+
export type AdversarialLabel = Prisma.AdversarialLabelModel
|
|
45
|
+
/**
|
|
46
|
+
* Model AdversarialParent
|
|
47
|
+
*
|
|
48
|
+
*/
|
|
49
|
+
export type AdversarialParent = Prisma.AdversarialParentModel
|
|
50
|
+
/**
|
|
51
|
+
* Model AdversarialInner
|
|
52
|
+
*
|
|
53
|
+
*/
|
|
54
|
+
export type AdversarialInner = Prisma.AdversarialInnerModel
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
|
|
2
|
+
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
// biome-ignore-all lint: generated file
|
|
5
|
+
// @ts-nocheck
|
|
6
|
+
/*
|
|
7
|
+
* This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.
|
|
8
|
+
* If you're looking for something you can import in the client-side of your application, please refer to the `browser.ts` file instead.
|
|
9
|
+
*
|
|
10
|
+
* 🟢 You can import this file directly.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as process from 'node:process'
|
|
14
|
+
import * as path from 'node:path'
|
|
15
|
+
|
|
16
|
+
import * as runtime from "@prisma/client/runtime/client"
|
|
17
|
+
import * as $Enums from "./enums.js"
|
|
18
|
+
import * as $Class from "./internal/class.js"
|
|
19
|
+
import * as Prisma from "./internal/prismaNamespace.js"
|
|
20
|
+
|
|
21
|
+
export * as $Enums from './enums.js'
|
|
22
|
+
export * from "./enums.js"
|
|
23
|
+
/**
|
|
24
|
+
* ## Prisma Client
|
|
25
|
+
*
|
|
26
|
+
* Type-safe database client for TypeScript
|
|
27
|
+
* @example
|
|
28
|
+
* ```
|
|
29
|
+
* const prisma = new PrismaClient({
|
|
30
|
+
* adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })
|
|
31
|
+
* })
|
|
32
|
+
* // Fetch zero or more AdversarialResources
|
|
33
|
+
* const adversarialResources = await prisma.adversarialResource.findMany()
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Read more in our [docs](https://pris.ly/d/client).
|
|
37
|
+
*/
|
|
38
|
+
export const PrismaClient = $Class.getPrismaClientClass()
|
|
39
|
+
export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
|
|
40
|
+
export { Prisma }
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Model AdversarialResource
|
|
44
|
+
*
|
|
45
|
+
*/
|
|
46
|
+
export type AdversarialResource = Prisma.AdversarialResourceModel
|
|
47
|
+
/**
|
|
48
|
+
* Model AdversarialTag
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
export type AdversarialTag = Prisma.AdversarialTagModel
|
|
52
|
+
/**
|
|
53
|
+
* Model AdversarialCategory
|
|
54
|
+
*
|
|
55
|
+
*/
|
|
56
|
+
export type AdversarialCategory = Prisma.AdversarialCategoryModel
|
|
57
|
+
/**
|
|
58
|
+
* Model AdversarialSubCategory
|
|
59
|
+
*
|
|
60
|
+
*/
|
|
61
|
+
export type AdversarialSubCategory = Prisma.AdversarialSubCategoryModel
|
|
62
|
+
/**
|
|
63
|
+
* Model AdversarialLabel
|
|
64
|
+
*
|
|
65
|
+
*/
|
|
66
|
+
export type AdversarialLabel = Prisma.AdversarialLabelModel
|
|
67
|
+
/**
|
|
68
|
+
* Model AdversarialParent
|
|
69
|
+
*
|
|
70
|
+
*/
|
|
71
|
+
export type AdversarialParent = Prisma.AdversarialParentModel
|
|
72
|
+
/**
|
|
73
|
+
* Model AdversarialInner
|
|
74
|
+
*
|
|
75
|
+
*/
|
|
76
|
+
export type AdversarialInner = Prisma.AdversarialInnerModel
|