@suss/packs 0.33.1 → 0.34.1
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 +10 -11
- package/dist/activerecord.d.ts +7 -0
- package/dist/activerecord.js +53 -5
- package/dist/aiohttp.js +20 -9
- package/dist/apollo-client.js +26 -39
- package/dist/apollo.js +12 -20
- package/dist/aws-dynamodb.js +4 -4
- package/dist/aws-eventbridge.js +3 -3
- package/dist/aws-lambda.js +31 -54
- package/dist/aws-s3.js +1 -1
- package/dist/aws-secrets-manager.js +1 -1
- package/dist/aws-sns.js +2 -2
- package/dist/aws-sqs.js +20 -11
- package/dist/aws-ssm.js +1 -1
- package/dist/axios.js +36 -37
- package/dist/bigquery-python.js +3 -3
- package/dist/bigquery.js +1 -1
- package/dist/{chunk-NYWZHQBJ.js → chunk-GK4BILHB.js} +8 -9
- package/dist/cloudflare-workers.js +26 -35
- package/dist/drizzle.js +15 -20
- package/dist/express.js +3 -6
- package/dist/fabrication.d.ts +1 -0
- package/dist/fabrication.js +36 -0
- package/dist/factory-bot.d.ts +1 -0
- package/dist/factory-bot.js +36 -0
- package/dist/faraday.js +8 -6
- package/dist/fastapi.js +123 -5
- package/dist/fastify.js +10 -14
- package/dist/fetch.js +12 -6
- package/dist/flask-restx.js +19 -12
- package/dist/gcs.js +1 -1
- package/dist/graphql-ruby.js +20 -16
- package/dist/hono.js +17 -27
- package/dist/httpx.js +8 -6
- package/dist/minitest.d.ts +1 -0
- package/dist/minitest.js +59 -0
- package/dist/mongoose.js +7 -7
- package/dist/nestjs-graphql.js +14 -25
- package/dist/nestjs-microservices.js +9 -10
- package/dist/nestjs-rest.js +193 -23
- package/dist/net-http.js +9 -7
- package/dist/nextjs.js +16 -17
- package/dist/node.js +139 -23
- package/dist/package-exports.js +1 -1
- package/dist/pg.js +3 -2
- package/dist/prisma.js +375 -56
- package/dist/pytest.d.ts +1 -0
- package/dist/pytest.js +81 -0
- package/dist/rails.js +1792 -132
- package/dist/react-query.js +1 -1
- package/dist/react-router.js +21 -48
- package/dist/react.js +14 -5
- package/dist/redis.js +1 -1
- package/dist/requests.js +27 -19
- package/dist/rspec.d.ts +1 -0
- package/dist/rspec.js +135 -0
- package/dist/sqlalchemy.js +1 -1
- package/dist/sqlmodel.js +7 -8
- package/dist/ts-rest.js +6 -8
- package/dist/typeorm.d.ts +1 -0
- package/dist/typeorm.js +291 -0
- package/dist/vitest.d.ts +1 -0
- package/dist/vitest.js +64 -0
- package/package.json +47 -11
package/dist/prisma.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// ../framework/prisma/dist/index.js
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import fs2 from "fs";
|
|
3
|
+
import path2 from "path";
|
|
4
4
|
import {
|
|
5
5
|
Node as N
|
|
6
6
|
} from "ts-morph";
|
|
7
7
|
import { z } from "zod";
|
|
8
|
-
import {
|
|
8
|
+
import { receiverTypesOf, writtenNodeOf } from "@suss/adapter-typescript";
|
|
9
|
+
import { storageBinding as storageBinding2 } from "@suss/behavioral-ir";
|
|
9
10
|
import { scopeOption, storageSystemOption } from "@suss/extractor";
|
|
10
11
|
import {
|
|
11
12
|
compile,
|
|
@@ -13,6 +14,252 @@ import {
|
|
|
13
14
|
declaredBy,
|
|
14
15
|
sqlStatements
|
|
15
16
|
} from "@suss/recognize";
|
|
17
|
+
import fs from "fs";
|
|
18
|
+
import path from "path";
|
|
19
|
+
import { getSchema as getSchema2 } from "@mrleebo/prisma-ast";
|
|
20
|
+
import { ts } from "ts-morph";
|
|
21
|
+
|
|
22
|
+
// ../contract/prisma/dist/index.js
|
|
23
|
+
import { getSchema } from "@mrleebo/prisma-ast";
|
|
24
|
+
import { storageBinding } from "@suss/behavioral-ir";
|
|
25
|
+
var PROVIDER_TO_SYSTEM = {
|
|
26
|
+
postgresql: "postgresql",
|
|
27
|
+
postgres: "postgresql",
|
|
28
|
+
mysql: "mysql",
|
|
29
|
+
sqlite: "sqlite"
|
|
30
|
+
};
|
|
31
|
+
function prismaStorageSystem(source) {
|
|
32
|
+
try {
|
|
33
|
+
return storageSystemIn(getSchema(source).list);
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function storageSystemIn(list) {
|
|
39
|
+
for (const node of list) {
|
|
40
|
+
if (node.type !== "datasource") {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const provider = readProviderString(node);
|
|
44
|
+
if (provider !== null && provider in PROVIDER_TO_SYSTEM) {
|
|
45
|
+
return PROVIDER_TO_SYSTEM[provider];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
function readProviderString(ds) {
|
|
51
|
+
for (const a of ds.assignments) {
|
|
52
|
+
if (a.key !== "provider") {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (typeof a.value === "string") {
|
|
56
|
+
return a.value.replace(/^"|"$/g, "");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ../framework/prisma/dist/index.js
|
|
63
|
+
var CONFIG_FILES = [
|
|
64
|
+
"prisma.config.ts",
|
|
65
|
+
"prisma.config.mts",
|
|
66
|
+
"prisma.config.cts",
|
|
67
|
+
"prisma.config.js",
|
|
68
|
+
"prisma.config.mjs",
|
|
69
|
+
"prisma.config.cjs",
|
|
70
|
+
path.join(".config", "prisma.ts")
|
|
71
|
+
];
|
|
72
|
+
var DEFAULT_SCHEMAS = [path.join("prisma", "schema.prisma"), "schema.prisma"];
|
|
73
|
+
var readProjects = /* @__PURE__ */ new Map();
|
|
74
|
+
function generatedClientDirs(fromDir) {
|
|
75
|
+
return readProjectAt(fromDir)?.dirs ?? [];
|
|
76
|
+
}
|
|
77
|
+
function schemaStorageSystem(fromDir) {
|
|
78
|
+
return readProjectAt(fromDir)?.storageSystem ?? null;
|
|
79
|
+
}
|
|
80
|
+
function readProjectAt(fromDir) {
|
|
81
|
+
const root = projectRootOf(fromDir);
|
|
82
|
+
if (root === null) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const known = readProjects.get(root);
|
|
86
|
+
if (known !== void 0 && unchanged(known.readFrom)) {
|
|
87
|
+
return known;
|
|
88
|
+
}
|
|
89
|
+
const schemaFiles = schemaFilesOf(root);
|
|
90
|
+
const readFrom = /* @__PURE__ */ new Map();
|
|
91
|
+
for (const file of [...projectFilesOf(root), ...schemaFiles]) {
|
|
92
|
+
readFrom.set(file, changedAt(file));
|
|
93
|
+
}
|
|
94
|
+
const sources = schemaFiles.map((file) => ({ file, text: textOf(file) }));
|
|
95
|
+
const project = {
|
|
96
|
+
dirs: sources.flatMap(outputDirsIn),
|
|
97
|
+
storageSystem: sources.map(({ text }) => prismaStorageSystem(text)).find((system) => system !== null) ?? null,
|
|
98
|
+
readFrom
|
|
99
|
+
};
|
|
100
|
+
readProjects.set(root, project);
|
|
101
|
+
return project;
|
|
102
|
+
}
|
|
103
|
+
function textOf(file) {
|
|
104
|
+
try {
|
|
105
|
+
return fs.readFileSync(file, "utf8");
|
|
106
|
+
} catch {
|
|
107
|
+
return "";
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function isInGeneratedClient(filePath) {
|
|
111
|
+
return generatedClientDirs(path.dirname(filePath)).some(
|
|
112
|
+
(dir) => filePath.startsWith(`${dir}${path.sep}`)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
function projectFilesOf(root) {
|
|
116
|
+
return [
|
|
117
|
+
path.join(root, "package.json"),
|
|
118
|
+
...CONFIG_FILES.map((name) => path.join(root, name)),
|
|
119
|
+
...DEFAULT_SCHEMAS.map((name) => path.join(root, name))
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
function changedAt(file) {
|
|
123
|
+
try {
|
|
124
|
+
return fs.statSync(file).mtimeMs;
|
|
125
|
+
} catch {
|
|
126
|
+
return -1;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function unchanged(readFrom) {
|
|
130
|
+
for (const [file, at] of readFrom) {
|
|
131
|
+
if (changedAt(file) !== at) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
var rootsByDir = /* @__PURE__ */ new Map();
|
|
138
|
+
function projectRootOf(fromDir) {
|
|
139
|
+
const known = rootsByDir.get(fromDir);
|
|
140
|
+
if (known !== void 0) {
|
|
141
|
+
return known;
|
|
142
|
+
}
|
|
143
|
+
const root = findProjectRoot(fromDir);
|
|
144
|
+
rootsByDir.set(fromDir, root);
|
|
145
|
+
return root;
|
|
146
|
+
}
|
|
147
|
+
function findProjectRoot(fromDir) {
|
|
148
|
+
let dir = path.resolve(fromDir);
|
|
149
|
+
while (true) {
|
|
150
|
+
if (fs.existsSync(path.join(dir, "package.json")) || configFileIn(dir) !== null) {
|
|
151
|
+
return dir;
|
|
152
|
+
}
|
|
153
|
+
const parent = path.dirname(dir);
|
|
154
|
+
if (parent === dir) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
dir = parent;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function configFileIn(dir) {
|
|
161
|
+
for (const name of CONFIG_FILES) {
|
|
162
|
+
const candidate = path.join(dir, name);
|
|
163
|
+
if (fs.existsSync(candidate)) {
|
|
164
|
+
return candidate;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
function schemaFilesOf(root) {
|
|
170
|
+
const stated = [configuredSchema(root), packageJsonSchema(root)];
|
|
171
|
+
for (const schema of [
|
|
172
|
+
...stated.filter((one) => one !== null),
|
|
173
|
+
...DEFAULT_SCHEMAS.map((name) => path.join(root, name))
|
|
174
|
+
]) {
|
|
175
|
+
const files = prismaFilesAt(schema);
|
|
176
|
+
if (files.length > 0) {
|
|
177
|
+
return files;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
function prismaFilesAt(schemaPath) {
|
|
183
|
+
let stat;
|
|
184
|
+
try {
|
|
185
|
+
stat = fs.statSync(schemaPath);
|
|
186
|
+
} catch {
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
if (stat.isFile()) {
|
|
190
|
+
return [schemaPath];
|
|
191
|
+
}
|
|
192
|
+
if (!stat.isDirectory()) {
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
return fs.readdirSync(schemaPath, { recursive: true, encoding: "utf8" }).filter((name) => name.endsWith(".prisma")).map((name) => path.join(schemaPath, name)).sort();
|
|
196
|
+
}
|
|
197
|
+
function configuredSchema(root) {
|
|
198
|
+
const config = configFileIn(root);
|
|
199
|
+
if (config === null) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
const source = ts.createSourceFile(
|
|
203
|
+
config,
|
|
204
|
+
fs.readFileSync(config, "utf8"),
|
|
205
|
+
ts.ScriptTarget.Latest
|
|
206
|
+
);
|
|
207
|
+
let found = null;
|
|
208
|
+
const visit = (node) => {
|
|
209
|
+
if (found !== null) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (ts.isPropertyAssignment(node) && node.name.getText(source) === "schema" && ts.isStringLiteralLike(node.initializer)) {
|
|
213
|
+
found = node.initializer.text;
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
ts.forEachChild(node, visit);
|
|
217
|
+
};
|
|
218
|
+
visit(source);
|
|
219
|
+
return found === null ? null : path.resolve(root, found);
|
|
220
|
+
}
|
|
221
|
+
function packageJsonSchema(root) {
|
|
222
|
+
const file = path.join(root, "package.json");
|
|
223
|
+
if (!fs.existsSync(file)) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
const manifest = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
228
|
+
const schema = manifest.prisma?.schema;
|
|
229
|
+
return typeof schema === "string" ? path.resolve(root, schema) : null;
|
|
230
|
+
} catch {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function outputDirsIn(schema) {
|
|
235
|
+
const schemaFile = schema.file;
|
|
236
|
+
let list;
|
|
237
|
+
try {
|
|
238
|
+
list = getSchema2(schema.text).list;
|
|
239
|
+
} catch {
|
|
240
|
+
return [];
|
|
241
|
+
}
|
|
242
|
+
const dirs = [];
|
|
243
|
+
for (const block of list) {
|
|
244
|
+
const output = generatorOutput(block);
|
|
245
|
+
if (output !== null) {
|
|
246
|
+
dirs.push(path.resolve(path.dirname(schemaFile), output));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return dirs;
|
|
250
|
+
}
|
|
251
|
+
function generatorOutput(block) {
|
|
252
|
+
const generator = block;
|
|
253
|
+
if (generator.type !== "generator") {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
for (const assignment of generator.assignments ?? []) {
|
|
257
|
+
if (assignment.type === "assignment" && assignment.key === "output" && typeof assignment.value === "string") {
|
|
258
|
+
return assignment.value.replace(/^"|"$/g, "");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
16
263
|
var PRISMA_READ_METHODS = /* @__PURE__ */ new Set([
|
|
17
264
|
"findUnique",
|
|
18
265
|
"findFirst",
|
|
@@ -33,23 +280,30 @@ var PRISMA_WRITE_METHODS = /* @__PURE__ */ new Set([
|
|
|
33
280
|
]);
|
|
34
281
|
var optionsSchema = z.object({
|
|
35
282
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* the dominant Prisma deployment.
|
|
283
|
+
* The storage system the calls target. When unset, the project
|
|
284
|
+
* schema's datasource decides, then `"postgresql"`. The README says
|
|
285
|
+
* why it has to match the schema reader's summaries.
|
|
40
286
|
*/
|
|
41
287
|
storageSystem: storageSystemOption.optional(),
|
|
42
288
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
289
|
+
* Has to match the schema reader's scope. It is `"default"` when
|
|
290
|
+
* unset, the same default `prismaSchemaToSummaries` uses.
|
|
45
291
|
*/
|
|
46
292
|
scope: scopeOption.optional()
|
|
47
293
|
}).strict();
|
|
48
294
|
function makeRecognizer(opts) {
|
|
49
|
-
const storageSystem = opts.storageSystem ?? "postgresql";
|
|
50
295
|
const scope = opts.scope ?? "default";
|
|
51
|
-
return (call, ctx) => recognizePrismaCall(
|
|
296
|
+
return (call, ctx) => recognizePrismaCall(
|
|
297
|
+
call,
|
|
298
|
+
ctx,
|
|
299
|
+
storageSystemFor(opts, call.getSourceFile()),
|
|
300
|
+
scope
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
function storageSystemFor(opts, file) {
|
|
304
|
+
return opts.storageSystem ?? schemaStorageSystem(path2.dirname(file.getFilePath())) ?? DEFAULT_STORAGE_SYSTEM;
|
|
52
305
|
}
|
|
306
|
+
var DEFAULT_STORAGE_SYSTEM = "postgresql";
|
|
53
307
|
function recognizePrismaCall(call, ctx, storageSystem, scope) {
|
|
54
308
|
const callNode = call;
|
|
55
309
|
const recognizerCtx = ctx;
|
|
@@ -68,7 +322,7 @@ function recognizePrismaCall(call, ctx, storageSystem, scope) {
|
|
|
68
322
|
return null;
|
|
69
323
|
}
|
|
70
324
|
const receiverExpr = delegateExpr.getExpression();
|
|
71
|
-
if (!isPrismaClientReceiver(receiverExpr)) {
|
|
325
|
+
if (!isPrismaClientReceiver(receiverExpr, recognizerCtx.resolution)) {
|
|
72
326
|
return null;
|
|
73
327
|
}
|
|
74
328
|
const delegateName = delegateExpr.getName();
|
|
@@ -81,26 +335,31 @@ function recognizePrismaCall(call, ctx, storageSystem, scope) {
|
|
|
81
335
|
const optionsArg = readObjectArg(argsShape[0]);
|
|
82
336
|
const fields = extractFields(optionsArg, kind);
|
|
83
337
|
const selector = extractSelector(optionsArg);
|
|
84
|
-
const binding =
|
|
338
|
+
const binding = storageBinding2({
|
|
85
339
|
recognition: "@suss/framework-prisma",
|
|
86
340
|
storageSystem,
|
|
87
341
|
scope,
|
|
88
342
|
container: tableName
|
|
89
343
|
});
|
|
90
344
|
const callee = callNode.getExpression().getText();
|
|
345
|
+
const access = {
|
|
346
|
+
type: "interaction",
|
|
347
|
+
binding,
|
|
348
|
+
callee,
|
|
349
|
+
interaction: {
|
|
350
|
+
class: "storage-access",
|
|
351
|
+
kind,
|
|
352
|
+
fields,
|
|
353
|
+
...selector !== null ? { selector } : {},
|
|
354
|
+
operation: method
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
statePayloadSlots(recognizerCtx.ops, access, {
|
|
358
|
+
fields: kind === "write" ? fields : [],
|
|
359
|
+
selector: selector ?? []
|
|
360
|
+
});
|
|
91
361
|
return [
|
|
92
|
-
|
|
93
|
-
type: "interaction",
|
|
94
|
-
binding,
|
|
95
|
-
callee,
|
|
96
|
-
interaction: {
|
|
97
|
-
class: "storage-access",
|
|
98
|
-
kind,
|
|
99
|
-
fields,
|
|
100
|
-
...selector !== null ? { selector } : {},
|
|
101
|
-
operation: method
|
|
102
|
-
}
|
|
103
|
-
},
|
|
362
|
+
access,
|
|
104
363
|
...nestedReads(optionsArg).map(
|
|
105
364
|
(nested) => ({
|
|
106
365
|
type: "interaction",
|
|
@@ -126,15 +385,34 @@ function recognizePrismaCall(call, ctx, storageSystem, scope) {
|
|
|
126
385
|
fields: nested.fields,
|
|
127
386
|
relationPath: nested.relationPath,
|
|
128
387
|
...nested.relationKey === true ? { relationKey: true } : {},
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
// to the model in the binding.
|
|
388
|
+
// The outer method acts on the model in the binding. The model
|
|
389
|
+
// across the relation gets the nested operation.
|
|
132
390
|
operation: nested.operation
|
|
133
391
|
}
|
|
134
392
|
})
|
|
135
393
|
)
|
|
136
394
|
];
|
|
137
395
|
}
|
|
396
|
+
function statePayloadSlots(ops, effect, columns) {
|
|
397
|
+
const options = ops?.statesSlots === void 0 ? null : ops.valueAt(0);
|
|
398
|
+
if (ops === void 0 || options === null) {
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
const slots = [
|
|
402
|
+
...slotsUnder(options, ["where"], "selector", columns.selector),
|
|
403
|
+
...slotsUnder(options, WRITE_PAYLOAD_KEYS, "field", columns.fields)
|
|
404
|
+
];
|
|
405
|
+
if (slots.length > 0) {
|
|
406
|
+
ops.statesSlots?.(effect, slots);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function slotsUnder(options, keys, slot, columns) {
|
|
410
|
+
return keys.flatMap(
|
|
411
|
+
(key) => (options.property(key)?.entries("nothing") ?? []).flatMap(
|
|
412
|
+
(entry) => entry.key !== null && columns.includes(entry.key) ? [{ slot, name: entry.key, value: entry.value }] : []
|
|
413
|
+
)
|
|
414
|
+
);
|
|
415
|
+
}
|
|
138
416
|
function nestedReads(optionsArg) {
|
|
139
417
|
if (optionsArg === null) {
|
|
140
418
|
return [];
|
|
@@ -143,7 +421,7 @@ function nestedReads(optionsArg) {
|
|
|
143
421
|
collectRelations(optionsArg, [], found);
|
|
144
422
|
return found;
|
|
145
423
|
}
|
|
146
|
-
function collectRelations(shape,
|
|
424
|
+
function collectRelations(shape, path3, found) {
|
|
147
425
|
const select = readObjectArg(shape.fields.select);
|
|
148
426
|
if (select !== null) {
|
|
149
427
|
for (const [name, value] of Object.entries(select.fields)) {
|
|
@@ -151,7 +429,7 @@ function collectRelations(shape, path2, found) {
|
|
|
151
429
|
if (nested === null) {
|
|
152
430
|
continue;
|
|
153
431
|
}
|
|
154
|
-
recordRelation(name, nested,
|
|
432
|
+
recordRelation(name, nested, path3, found);
|
|
155
433
|
}
|
|
156
434
|
}
|
|
157
435
|
const include = readObjectArg(shape.fields.include);
|
|
@@ -161,14 +439,14 @@ function collectRelations(shape, path2, found) {
|
|
|
161
439
|
for (const [name, value] of Object.entries(include.fields)) {
|
|
162
440
|
const nested = readObjectArg(value);
|
|
163
441
|
if (nested === null) {
|
|
164
|
-
found.push({ relationPath: [...
|
|
442
|
+
found.push({ relationPath: [...path3, name], fields: ["*"] });
|
|
165
443
|
continue;
|
|
166
444
|
}
|
|
167
|
-
recordRelation(name, nested,
|
|
445
|
+
recordRelation(name, nested, path3, found);
|
|
168
446
|
}
|
|
169
447
|
}
|
|
170
|
-
function recordRelation(name, nested,
|
|
171
|
-
const relationPath = [...
|
|
448
|
+
function recordRelation(name, nested, path3, found) {
|
|
449
|
+
const relationPath = [...path3, name];
|
|
172
450
|
found.push({ relationPath, fields: extractFields(nested, "read") });
|
|
173
451
|
collectRelations(nested, relationPath, found);
|
|
174
452
|
}
|
|
@@ -222,13 +500,13 @@ function nestedWrites(optionsArg) {
|
|
|
222
500
|
}
|
|
223
501
|
return found;
|
|
224
502
|
}
|
|
225
|
-
function collectNestedWrites(row,
|
|
503
|
+
function collectNestedWrites(row, path3, found) {
|
|
226
504
|
for (const [field, value] of Object.entries(row.fields)) {
|
|
227
505
|
const nested = readObjectArg(value);
|
|
228
506
|
if (nested === null) {
|
|
229
507
|
continue;
|
|
230
508
|
}
|
|
231
|
-
const relationPath = [...
|
|
509
|
+
const relationPath = [...path3, field];
|
|
232
510
|
for (const [operation, payload] of Object.entries(nested.fields)) {
|
|
233
511
|
const does = NESTED_OPERATIONS.get(operation);
|
|
234
512
|
if (does === void 0) {
|
|
@@ -279,29 +557,51 @@ function fieldsOfRows(read) {
|
|
|
279
557
|
}
|
|
280
558
|
return out.size === 0 ? ["*"] : [...out];
|
|
281
559
|
}
|
|
282
|
-
function isPrismaClientReceiver(node) {
|
|
283
|
-
|
|
560
|
+
function isPrismaClientReceiver(node, resolution) {
|
|
561
|
+
if (receiverTypesOf(node).some(
|
|
562
|
+
(type) => declaredByPrisma(type) || extendsImportedClient(type, resolution)
|
|
563
|
+
)) {
|
|
564
|
+
return true;
|
|
565
|
+
}
|
|
566
|
+
return node.getType().isAny() && madeFromImportedClient(node, resolution);
|
|
284
567
|
}
|
|
285
|
-
function
|
|
286
|
-
if (
|
|
568
|
+
function madeFromImportedClient(node, resolution) {
|
|
569
|
+
if (resolution === void 0) {
|
|
287
570
|
return false;
|
|
288
571
|
}
|
|
289
|
-
|
|
290
|
-
const declarations = type.getSymbol()?.getDeclarations() ?? [];
|
|
291
|
-
if (declarations.some(
|
|
292
|
-
(declaration) => isPrismaClientPath(declaration.getSourceFile().getFilePath())
|
|
293
|
-
)) {
|
|
572
|
+
if (importedAsClient(node, resolution)) {
|
|
294
573
|
return true;
|
|
295
574
|
}
|
|
296
|
-
|
|
575
|
+
const written = writtenNodeOf(node, resolution);
|
|
576
|
+
return written !== null && written !== node && importedAsClient(written, resolution);
|
|
577
|
+
}
|
|
578
|
+
function importedAsClient(value, resolution) {
|
|
579
|
+
return resolution.importOriginsOf(value, clientModulesFor(value)).some((origin) => origin.path.join(".") === CLIENT_CLASS);
|
|
580
|
+
}
|
|
581
|
+
function clientModulesFor(value) {
|
|
582
|
+
return [
|
|
583
|
+
CLIENT_MODULE,
|
|
584
|
+
...generatedClientDirs(path2.dirname(value.getSourceFile().getFilePath()))
|
|
585
|
+
];
|
|
586
|
+
}
|
|
587
|
+
function declaredByPrisma(type) {
|
|
588
|
+
return type.declaredIn.some((filePath) => isPrismaClientPath(filePath));
|
|
589
|
+
}
|
|
590
|
+
var CLIENT_MODULE = "@prisma/client";
|
|
591
|
+
var CLIENT_CLASS = "PrismaClient";
|
|
592
|
+
function extendsImportedClient(type, resolution) {
|
|
593
|
+
if (resolution === void 0) {
|
|
594
|
+
return false;
|
|
595
|
+
}
|
|
596
|
+
return type.extendsWritten.some((base) => importedAsClient(base, resolution));
|
|
297
597
|
}
|
|
298
598
|
var GENERATED_CLIENT_MARKER = "schema.prisma";
|
|
299
599
|
function isPrismaClientPath(filePath) {
|
|
300
600
|
if (filePath.includes("/@prisma/client/") || filePath.includes("/.prisma/client/")) {
|
|
301
601
|
return true;
|
|
302
602
|
}
|
|
303
|
-
const beside =
|
|
304
|
-
return
|
|
603
|
+
const beside = path2.join(path2.dirname(filePath), GENERATED_CLIENT_MARKER);
|
|
604
|
+
return fs2.existsSync(beside) || isInGeneratedClient(filePath);
|
|
305
605
|
}
|
|
306
606
|
function capitalizeFirst(name) {
|
|
307
607
|
if (name.length === 0) {
|
|
@@ -383,8 +683,7 @@ function extractSelector(optionsArg) {
|
|
|
383
683
|
return keys.length > 0 ? keys : null;
|
|
384
684
|
}
|
|
385
685
|
var STATEMENT = { statement: { at: 0 } };
|
|
386
|
-
function rawStatements(options) {
|
|
387
|
-
const provider = options.storageSystem ?? "postgresql";
|
|
686
|
+
function rawStatements(options, provider) {
|
|
388
687
|
return sqlStatements({
|
|
389
688
|
system: provider,
|
|
390
689
|
dialect: provider,
|
|
@@ -397,8 +696,26 @@ function rawStatements(options) {
|
|
|
397
696
|
$executeRawUnsafe: STATEMENT
|
|
398
697
|
}).example('prisma.$queryRawUnsafe("SELECT id, email FROM users")');
|
|
399
698
|
}
|
|
699
|
+
function rawRecognizer(options) {
|
|
700
|
+
const bySystem = /* @__PURE__ */ new Map();
|
|
701
|
+
return (call, ctx) => {
|
|
702
|
+
const system = storageSystemFor(options, call.getSourceFile());
|
|
703
|
+
let recognizer = bySystem.get(system);
|
|
704
|
+
if (recognizer === void 0) {
|
|
705
|
+
recognizer = compile(
|
|
706
|
+
rawStatements(options, system).declared,
|
|
707
|
+
"@suss/framework-prisma"
|
|
708
|
+
);
|
|
709
|
+
bySystem.set(system, recognizer);
|
|
710
|
+
}
|
|
711
|
+
return recognizer(call, ctx);
|
|
712
|
+
};
|
|
713
|
+
}
|
|
400
714
|
function prismaFramework(options = {}) {
|
|
401
|
-
const raw = rawStatements(
|
|
715
|
+
const raw = rawStatements(
|
|
716
|
+
options,
|
|
717
|
+
options.storageSystem ?? DEFAULT_STORAGE_SYSTEM
|
|
718
|
+
);
|
|
402
719
|
return {
|
|
403
720
|
name: "prisma",
|
|
404
721
|
protocol: "in-process",
|
|
@@ -406,16 +723,18 @@ function prismaFramework(options = {}) {
|
|
|
406
723
|
discovery: [],
|
|
407
724
|
terminals: [],
|
|
408
725
|
inputMapping: { type: "positionalParams", params: [] },
|
|
409
|
-
//
|
|
410
|
-
//
|
|
726
|
+
// The type check would reject every call in a file that does not
|
|
727
|
+
// import `@prisma/client`, so those files are skipped.
|
|
411
728
|
requiresImport: ["@prisma/client"],
|
|
412
729
|
// A generator with its own `output` puts the client in the project,
|
|
413
|
-
// where the only way to reach it is a relative path.
|
|
730
|
+
// where the only way to reach it is a relative path. Prisma 7's
|
|
731
|
+
// `prisma-client` generator writes no schema beside it.
|
|
414
732
|
generatedModuleMarkers: [GENERATED_CLIENT_MARKER],
|
|
733
|
+
generatedModuleDirs: generatedClientDirs,
|
|
415
734
|
invocationRecognizers: [makeRecognizer(options)],
|
|
416
735
|
// A tagged template is not an invocation, so the raw chain runs on
|
|
417
736
|
// the access walk, which visits calls as well.
|
|
418
|
-
accessRecognizers: [
|
|
737
|
+
accessRecognizers: [rawRecognizer(options)],
|
|
419
738
|
declarations: declarationsIn([raw])
|
|
420
739
|
};
|
|
421
740
|
}
|
|
@@ -423,7 +742,7 @@ var declares = {
|
|
|
423
742
|
kind: "effects",
|
|
424
743
|
package: "@suss/framework-prisma",
|
|
425
744
|
dependencies: [{ ecosystem: "npm", name: "@prisma/client" }],
|
|
426
|
-
reads: "Prisma client calls
|
|
745
|
+
reads: "Prisma client calls. Each read and each write becomes a storage-access interaction."
|
|
427
746
|
};
|
|
428
747
|
var index_default = prismaFramework;
|
|
429
748
|
export {
|
package/dist/pytest.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { declares, default, optionsSchema } from '@suss/framework-pytest';
|
package/dist/pytest.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// ../framework/pytest/dist/index.js
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
var optionsSchema = z.object({
|
|
5
|
+
/**
|
|
6
|
+
* The test files to read, matched on whole path segments from the
|
|
7
|
+
* end. `suss extract --intent` fills it in from the tests the PRDs
|
|
8
|
+
* list, so a run reads those files and no others. Left out, every
|
|
9
|
+
* file pytest would collect is read.
|
|
10
|
+
*/
|
|
11
|
+
files: z.array(z.string().min(1)).optional()
|
|
12
|
+
}).strict();
|
|
13
|
+
var SHARED_FIXTURE_FILE = "conftest.py";
|
|
14
|
+
function pytestFramework(options = {}) {
|
|
15
|
+
return {
|
|
16
|
+
name: "pytest",
|
|
17
|
+
protocol: "in-process",
|
|
18
|
+
discovery: [],
|
|
19
|
+
// A test's fixtures can come from a conftest.py in any directory
|
|
20
|
+
// above it, so an edit to one has to re-read the tests below it.
|
|
21
|
+
discoveryInputs: (files) => files.filter((file) => path.basename(file) === SHARED_FIXTURE_FILE),
|
|
22
|
+
tests: [
|
|
23
|
+
{
|
|
24
|
+
filePatterns: ["test_*.py", "*_test.py"],
|
|
25
|
+
functionPrefix: "test",
|
|
26
|
+
classPrefix: "Test",
|
|
27
|
+
caseBaseClasses: [
|
|
28
|
+
"unittest.TestCase",
|
|
29
|
+
"unittest.IsolatedAsyncioTestCase"
|
|
30
|
+
],
|
|
31
|
+
setUp: {
|
|
32
|
+
module: ["setup_module"],
|
|
33
|
+
function: ["setup_function"],
|
|
34
|
+
testClass: ["setup_class", "setup_method"],
|
|
35
|
+
caseClass: ["setUpClass", "setUp", "asyncSetUp"]
|
|
36
|
+
},
|
|
37
|
+
usesFixtureMarkers: ["pytest.mark.usefixtures"],
|
|
38
|
+
fixtureDecorators: ["pytest.fixture"],
|
|
39
|
+
fixtureNameKeyword: "name",
|
|
40
|
+
autouseKeyword: "autouse",
|
|
41
|
+
sharedFixtureFiles: [SHARED_FIXTURE_FILE],
|
|
42
|
+
skipDecorators: [
|
|
43
|
+
"pytest.mark.skip",
|
|
44
|
+
"pytest.mark.skipif",
|
|
45
|
+
"pytest.mark.xfail",
|
|
46
|
+
"unittest.skip",
|
|
47
|
+
"unittest.skipIf",
|
|
48
|
+
"unittest.skipUnless",
|
|
49
|
+
"unittest.expectedFailure"
|
|
50
|
+
],
|
|
51
|
+
markerVariable: "pytestmark",
|
|
52
|
+
reservedParameters: ["self", "cls", "request"],
|
|
53
|
+
mocks: {
|
|
54
|
+
patchers: [
|
|
55
|
+
"unittest.mock.patch",
|
|
56
|
+
"unittest.mock.patch.object",
|
|
57
|
+
"mock.patch",
|
|
58
|
+
"mock.patch.object"
|
|
59
|
+
],
|
|
60
|
+
fixturePatchers: [
|
|
61
|
+
{ fixture: "mocker", methods: ["patch", "patch.object"] },
|
|
62
|
+
{ fixture: "monkeypatch", methods: ["setattr"] }
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
...options.files !== void 0 ? { files: options.files } : {}
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
var declares = {
|
|
71
|
+
kind: "framework",
|
|
72
|
+
package: "@suss/framework-pytest",
|
|
73
|
+
dependencies: [],
|
|
74
|
+
reads: "pytest tests (Python), named by their node ids, so a PRD scenario can say which test covers it."
|
|
75
|
+
};
|
|
76
|
+
var index_default = pytestFramework;
|
|
77
|
+
export {
|
|
78
|
+
declares,
|
|
79
|
+
index_default as default,
|
|
80
|
+
optionsSchema
|
|
81
|
+
};
|