@suss/packs 0.33.0 → 0.34.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 +10 -11
- package/dist/activerecord.d.ts +7 -0
- package/dist/activerecord.js +34 -4
- 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 +13 -27
- 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 +4 -5
- package/dist/fastapi.js +21 -5
- package/dist/fastify.js +10 -14
- package/dist/fetch.js +3 -4
- 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 +3 -5
- 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 +42 -22
- package/dist/net-http.js +5 -6
- package/dist/nextjs.js +16 -17
- package/dist/node.js +144 -28
- package/dist/package-exports.js +1 -1
- package/dist/pg.js +3 -2
- package/dist/prisma.js +281 -48
- package/dist/pytest.d.ts +1 -0
- package/dist/pytest.js +81 -0
- package/dist/rails.js +46 -27
- 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 +4 -5
- 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,10 +1,11 @@
|
|
|
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 { receiverTypesOf, writtenNodeOf } from "@suss/adapter-typescript";
|
|
8
9
|
import { storageBinding } from "@suss/behavioral-ir";
|
|
9
10
|
import { scopeOption, storageSystemOption } from "@suss/extractor";
|
|
10
11
|
import {
|
|
@@ -13,6 +14,191 @@ 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 } from "@mrleebo/prisma-ast";
|
|
20
|
+
import { ts } from "ts-morph";
|
|
21
|
+
var CONFIG_FILES = [
|
|
22
|
+
"prisma.config.ts",
|
|
23
|
+
"prisma.config.mts",
|
|
24
|
+
"prisma.config.cts",
|
|
25
|
+
"prisma.config.js",
|
|
26
|
+
"prisma.config.mjs",
|
|
27
|
+
"prisma.config.cjs",
|
|
28
|
+
path.join(".config", "prisma.ts")
|
|
29
|
+
];
|
|
30
|
+
var DEFAULT_SCHEMAS = [path.join("prisma", "schema.prisma"), "schema.prisma"];
|
|
31
|
+
var readProjects = /* @__PURE__ */ new Map();
|
|
32
|
+
function generatedClientDirs(fromDir) {
|
|
33
|
+
const root = projectRootOf(fromDir);
|
|
34
|
+
if (root === null) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
const known = readProjects.get(root);
|
|
38
|
+
if (known !== void 0 && unchanged(known.readFrom)) {
|
|
39
|
+
return known.dirs;
|
|
40
|
+
}
|
|
41
|
+
const schemaFiles = schemaFilesOf(root);
|
|
42
|
+
const readFrom = /* @__PURE__ */ new Map();
|
|
43
|
+
for (const file of [...projectFilesOf(root), ...schemaFiles]) {
|
|
44
|
+
readFrom.set(file, changedAt(file));
|
|
45
|
+
}
|
|
46
|
+
const dirs = schemaFiles.flatMap(outputDirsIn);
|
|
47
|
+
readProjects.set(root, { dirs, readFrom });
|
|
48
|
+
return dirs;
|
|
49
|
+
}
|
|
50
|
+
function isInGeneratedClient(filePath) {
|
|
51
|
+
return generatedClientDirs(path.dirname(filePath)).some(
|
|
52
|
+
(dir) => filePath.startsWith(`${dir}${path.sep}`)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
function projectFilesOf(root) {
|
|
56
|
+
return [
|
|
57
|
+
path.join(root, "package.json"),
|
|
58
|
+
...CONFIG_FILES.map((name) => path.join(root, name)),
|
|
59
|
+
...DEFAULT_SCHEMAS.map((name) => path.join(root, name))
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
function changedAt(file) {
|
|
63
|
+
try {
|
|
64
|
+
return fs.statSync(file).mtimeMs;
|
|
65
|
+
} catch {
|
|
66
|
+
return -1;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function unchanged(readFrom) {
|
|
70
|
+
for (const [file, at] of readFrom) {
|
|
71
|
+
if (changedAt(file) !== at) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
var rootsByDir = /* @__PURE__ */ new Map();
|
|
78
|
+
function projectRootOf(fromDir) {
|
|
79
|
+
const known = rootsByDir.get(fromDir);
|
|
80
|
+
if (known !== void 0) {
|
|
81
|
+
return known;
|
|
82
|
+
}
|
|
83
|
+
const root = findProjectRoot(fromDir);
|
|
84
|
+
rootsByDir.set(fromDir, root);
|
|
85
|
+
return root;
|
|
86
|
+
}
|
|
87
|
+
function findProjectRoot(fromDir) {
|
|
88
|
+
let dir = path.resolve(fromDir);
|
|
89
|
+
while (true) {
|
|
90
|
+
if (fs.existsSync(path.join(dir, "package.json")) || configFileIn(dir) !== null) {
|
|
91
|
+
return dir;
|
|
92
|
+
}
|
|
93
|
+
const parent = path.dirname(dir);
|
|
94
|
+
if (parent === dir) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
dir = parent;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function configFileIn(dir) {
|
|
101
|
+
for (const name of CONFIG_FILES) {
|
|
102
|
+
const candidate = path.join(dir, name);
|
|
103
|
+
if (fs.existsSync(candidate)) {
|
|
104
|
+
return candidate;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
function schemaFilesOf(root) {
|
|
110
|
+
const stated = [configuredSchema(root), packageJsonSchema(root)];
|
|
111
|
+
for (const schema of [
|
|
112
|
+
...stated.filter((one) => one !== null),
|
|
113
|
+
...DEFAULT_SCHEMAS.map((name) => path.join(root, name))
|
|
114
|
+
]) {
|
|
115
|
+
const files = prismaFilesAt(schema);
|
|
116
|
+
if (files.length > 0) {
|
|
117
|
+
return files;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
function prismaFilesAt(schemaPath) {
|
|
123
|
+
let stat;
|
|
124
|
+
try {
|
|
125
|
+
stat = fs.statSync(schemaPath);
|
|
126
|
+
} catch {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
if (stat.isFile()) {
|
|
130
|
+
return [schemaPath];
|
|
131
|
+
}
|
|
132
|
+
if (!stat.isDirectory()) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
return fs.readdirSync(schemaPath, { recursive: true, encoding: "utf8" }).filter((name) => name.endsWith(".prisma")).map((name) => path.join(schemaPath, name)).sort();
|
|
136
|
+
}
|
|
137
|
+
function configuredSchema(root) {
|
|
138
|
+
const config = configFileIn(root);
|
|
139
|
+
if (config === null) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const source = ts.createSourceFile(
|
|
143
|
+
config,
|
|
144
|
+
fs.readFileSync(config, "utf8"),
|
|
145
|
+
ts.ScriptTarget.Latest
|
|
146
|
+
);
|
|
147
|
+
let found = null;
|
|
148
|
+
const visit = (node) => {
|
|
149
|
+
if (found !== null) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (ts.isPropertyAssignment(node) && node.name.getText(source) === "schema" && ts.isStringLiteralLike(node.initializer)) {
|
|
153
|
+
found = node.initializer.text;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
ts.forEachChild(node, visit);
|
|
157
|
+
};
|
|
158
|
+
visit(source);
|
|
159
|
+
return found === null ? null : path.resolve(root, found);
|
|
160
|
+
}
|
|
161
|
+
function packageJsonSchema(root) {
|
|
162
|
+
const file = path.join(root, "package.json");
|
|
163
|
+
if (!fs.existsSync(file)) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const manifest = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
168
|
+
const schema = manifest.prisma?.schema;
|
|
169
|
+
return typeof schema === "string" ? path.resolve(root, schema) : null;
|
|
170
|
+
} catch {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function outputDirsIn(schemaFile) {
|
|
175
|
+
let list;
|
|
176
|
+
try {
|
|
177
|
+
list = getSchema(fs.readFileSync(schemaFile, "utf8")).list;
|
|
178
|
+
} catch {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
const dirs = [];
|
|
182
|
+
for (const block of list) {
|
|
183
|
+
const output = generatorOutput(block);
|
|
184
|
+
if (output !== null) {
|
|
185
|
+
dirs.push(path.resolve(path.dirname(schemaFile), output));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return dirs;
|
|
189
|
+
}
|
|
190
|
+
function generatorOutput(block) {
|
|
191
|
+
const generator = block;
|
|
192
|
+
if (generator.type !== "generator") {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
for (const assignment of generator.assignments ?? []) {
|
|
196
|
+
if (assignment.type === "assignment" && assignment.key === "output" && typeof assignment.value === "string") {
|
|
197
|
+
return assignment.value.replace(/^"|"$/g, "");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
16
202
|
var PRISMA_READ_METHODS = /* @__PURE__ */ new Set([
|
|
17
203
|
"findUnique",
|
|
18
204
|
"findFirst",
|
|
@@ -33,15 +219,14 @@ var PRISMA_WRITE_METHODS = /* @__PURE__ */ new Set([
|
|
|
33
219
|
]);
|
|
34
220
|
var optionsSchema = z.object({
|
|
35
221
|
/**
|
|
36
|
-
*
|
|
37
|
-
* `storageSystem` on schema
|
|
38
|
-
*
|
|
39
|
-
* the dominant Prisma deployment.
|
|
222
|
+
* The storage system the calls target, `"postgresql"` when unset. It
|
|
223
|
+
* has to match the `storageSystem` on the schema reader's summaries,
|
|
224
|
+
* or the calls do not pair.
|
|
40
225
|
*/
|
|
41
226
|
storageSystem: storageSystemOption.optional(),
|
|
42
227
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
228
|
+
* Has to match the schema reader's scope. It is `"default"` when
|
|
229
|
+
* unset, the same default `prismaSchemaToSummaries` uses.
|
|
45
230
|
*/
|
|
46
231
|
scope: scopeOption.optional()
|
|
47
232
|
}).strict();
|
|
@@ -68,7 +253,7 @@ function recognizePrismaCall(call, ctx, storageSystem, scope) {
|
|
|
68
253
|
return null;
|
|
69
254
|
}
|
|
70
255
|
const receiverExpr = delegateExpr.getExpression();
|
|
71
|
-
if (!isPrismaClientReceiver(receiverExpr)) {
|
|
256
|
+
if (!isPrismaClientReceiver(receiverExpr, recognizerCtx.resolution)) {
|
|
72
257
|
return null;
|
|
73
258
|
}
|
|
74
259
|
const delegateName = delegateExpr.getName();
|
|
@@ -88,19 +273,24 @@ function recognizePrismaCall(call, ctx, storageSystem, scope) {
|
|
|
88
273
|
container: tableName
|
|
89
274
|
});
|
|
90
275
|
const callee = callNode.getExpression().getText();
|
|
276
|
+
const access = {
|
|
277
|
+
type: "interaction",
|
|
278
|
+
binding,
|
|
279
|
+
callee,
|
|
280
|
+
interaction: {
|
|
281
|
+
class: "storage-access",
|
|
282
|
+
kind,
|
|
283
|
+
fields,
|
|
284
|
+
...selector !== null ? { selector } : {},
|
|
285
|
+
operation: method
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
statePayloadSlots(recognizerCtx.ops, access, {
|
|
289
|
+
fields: kind === "write" ? fields : [],
|
|
290
|
+
selector: selector ?? []
|
|
291
|
+
});
|
|
91
292
|
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
|
-
},
|
|
293
|
+
access,
|
|
104
294
|
...nestedReads(optionsArg).map(
|
|
105
295
|
(nested) => ({
|
|
106
296
|
type: "interaction",
|
|
@@ -126,15 +316,34 @@ function recognizePrismaCall(call, ctx, storageSystem, scope) {
|
|
|
126
316
|
fields: nested.fields,
|
|
127
317
|
relationPath: nested.relationPath,
|
|
128
318
|
...nested.relationKey === true ? { relationKey: true } : {},
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
// to the model in the binding.
|
|
319
|
+
// The outer method acts on the model in the binding. The model
|
|
320
|
+
// across the relation gets the nested operation.
|
|
132
321
|
operation: nested.operation
|
|
133
322
|
}
|
|
134
323
|
})
|
|
135
324
|
)
|
|
136
325
|
];
|
|
137
326
|
}
|
|
327
|
+
function statePayloadSlots(ops, effect, columns) {
|
|
328
|
+
const options = ops?.statesSlots === void 0 ? null : ops.valueAt(0);
|
|
329
|
+
if (ops === void 0 || options === null) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
const slots = [
|
|
333
|
+
...slotsUnder(options, ["where"], "selector", columns.selector),
|
|
334
|
+
...slotsUnder(options, WRITE_PAYLOAD_KEYS, "field", columns.fields)
|
|
335
|
+
];
|
|
336
|
+
if (slots.length > 0) {
|
|
337
|
+
ops.statesSlots?.(effect, slots);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function slotsUnder(options, keys, slot, columns) {
|
|
341
|
+
return keys.flatMap(
|
|
342
|
+
(key) => (options.property(key)?.entries("nothing") ?? []).flatMap(
|
|
343
|
+
(entry) => entry.key !== null && columns.includes(entry.key) ? [{ slot, name: entry.key, value: entry.value }] : []
|
|
344
|
+
)
|
|
345
|
+
);
|
|
346
|
+
}
|
|
138
347
|
function nestedReads(optionsArg) {
|
|
139
348
|
if (optionsArg === null) {
|
|
140
349
|
return [];
|
|
@@ -143,7 +352,7 @@ function nestedReads(optionsArg) {
|
|
|
143
352
|
collectRelations(optionsArg, [], found);
|
|
144
353
|
return found;
|
|
145
354
|
}
|
|
146
|
-
function collectRelations(shape,
|
|
355
|
+
function collectRelations(shape, path3, found) {
|
|
147
356
|
const select = readObjectArg(shape.fields.select);
|
|
148
357
|
if (select !== null) {
|
|
149
358
|
for (const [name, value] of Object.entries(select.fields)) {
|
|
@@ -151,7 +360,7 @@ function collectRelations(shape, path2, found) {
|
|
|
151
360
|
if (nested === null) {
|
|
152
361
|
continue;
|
|
153
362
|
}
|
|
154
|
-
recordRelation(name, nested,
|
|
363
|
+
recordRelation(name, nested, path3, found);
|
|
155
364
|
}
|
|
156
365
|
}
|
|
157
366
|
const include = readObjectArg(shape.fields.include);
|
|
@@ -161,14 +370,14 @@ function collectRelations(shape, path2, found) {
|
|
|
161
370
|
for (const [name, value] of Object.entries(include.fields)) {
|
|
162
371
|
const nested = readObjectArg(value);
|
|
163
372
|
if (nested === null) {
|
|
164
|
-
found.push({ relationPath: [...
|
|
373
|
+
found.push({ relationPath: [...path3, name], fields: ["*"] });
|
|
165
374
|
continue;
|
|
166
375
|
}
|
|
167
|
-
recordRelation(name, nested,
|
|
376
|
+
recordRelation(name, nested, path3, found);
|
|
168
377
|
}
|
|
169
378
|
}
|
|
170
|
-
function recordRelation(name, nested,
|
|
171
|
-
const relationPath = [...
|
|
379
|
+
function recordRelation(name, nested, path3, found) {
|
|
380
|
+
const relationPath = [...path3, name];
|
|
172
381
|
found.push({ relationPath, fields: extractFields(nested, "read") });
|
|
173
382
|
collectRelations(nested, relationPath, found);
|
|
174
383
|
}
|
|
@@ -222,13 +431,13 @@ function nestedWrites(optionsArg) {
|
|
|
222
431
|
}
|
|
223
432
|
return found;
|
|
224
433
|
}
|
|
225
|
-
function collectNestedWrites(row,
|
|
434
|
+
function collectNestedWrites(row, path3, found) {
|
|
226
435
|
for (const [field, value] of Object.entries(row.fields)) {
|
|
227
436
|
const nested = readObjectArg(value);
|
|
228
437
|
if (nested === null) {
|
|
229
438
|
continue;
|
|
230
439
|
}
|
|
231
|
-
const relationPath = [...
|
|
440
|
+
const relationPath = [...path3, field];
|
|
232
441
|
for (const [operation, payload] of Object.entries(nested.fields)) {
|
|
233
442
|
const does = NESTED_OPERATIONS.get(operation);
|
|
234
443
|
if (does === void 0) {
|
|
@@ -279,29 +488,51 @@ function fieldsOfRows(read) {
|
|
|
279
488
|
}
|
|
280
489
|
return out.size === 0 ? ["*"] : [...out];
|
|
281
490
|
}
|
|
282
|
-
function isPrismaClientReceiver(node) {
|
|
283
|
-
|
|
491
|
+
function isPrismaClientReceiver(node, resolution) {
|
|
492
|
+
if (receiverTypesOf(node).some(
|
|
493
|
+
(type) => declaredByPrisma(type) || extendsImportedClient(type, resolution)
|
|
494
|
+
)) {
|
|
495
|
+
return true;
|
|
496
|
+
}
|
|
497
|
+
return node.getType().isAny() && madeFromImportedClient(node, resolution);
|
|
284
498
|
}
|
|
285
|
-
function
|
|
286
|
-
if (
|
|
499
|
+
function madeFromImportedClient(node, resolution) {
|
|
500
|
+
if (resolution === void 0) {
|
|
287
501
|
return false;
|
|
288
502
|
}
|
|
289
|
-
|
|
290
|
-
const declarations = type.getSymbol()?.getDeclarations() ?? [];
|
|
291
|
-
if (declarations.some(
|
|
292
|
-
(declaration) => isPrismaClientPath(declaration.getSourceFile().getFilePath())
|
|
293
|
-
)) {
|
|
503
|
+
if (importedAsClient(node, resolution)) {
|
|
294
504
|
return true;
|
|
295
505
|
}
|
|
296
|
-
|
|
506
|
+
const written = writtenNodeOf(node, resolution);
|
|
507
|
+
return written !== null && written !== node && importedAsClient(written, resolution);
|
|
508
|
+
}
|
|
509
|
+
function importedAsClient(value, resolution) {
|
|
510
|
+
return resolution.importOriginsOf(value, clientModulesFor(value)).some((origin) => origin.path.join(".") === CLIENT_CLASS);
|
|
511
|
+
}
|
|
512
|
+
function clientModulesFor(value) {
|
|
513
|
+
return [
|
|
514
|
+
CLIENT_MODULE,
|
|
515
|
+
...generatedClientDirs(path2.dirname(value.getSourceFile().getFilePath()))
|
|
516
|
+
];
|
|
517
|
+
}
|
|
518
|
+
function declaredByPrisma(type) {
|
|
519
|
+
return type.declaredIn.some((filePath) => isPrismaClientPath(filePath));
|
|
520
|
+
}
|
|
521
|
+
var CLIENT_MODULE = "@prisma/client";
|
|
522
|
+
var CLIENT_CLASS = "PrismaClient";
|
|
523
|
+
function extendsImportedClient(type, resolution) {
|
|
524
|
+
if (resolution === void 0) {
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
527
|
+
return type.extendsWritten.some((base) => importedAsClient(base, resolution));
|
|
297
528
|
}
|
|
298
529
|
var GENERATED_CLIENT_MARKER = "schema.prisma";
|
|
299
530
|
function isPrismaClientPath(filePath) {
|
|
300
531
|
if (filePath.includes("/@prisma/client/") || filePath.includes("/.prisma/client/")) {
|
|
301
532
|
return true;
|
|
302
533
|
}
|
|
303
|
-
const beside =
|
|
304
|
-
return
|
|
534
|
+
const beside = path2.join(path2.dirname(filePath), GENERATED_CLIENT_MARKER);
|
|
535
|
+
return fs2.existsSync(beside) || isInGeneratedClient(filePath);
|
|
305
536
|
}
|
|
306
537
|
function capitalizeFirst(name) {
|
|
307
538
|
if (name.length === 0) {
|
|
@@ -406,12 +637,14 @@ function prismaFramework(options = {}) {
|
|
|
406
637
|
discovery: [],
|
|
407
638
|
terminals: [],
|
|
408
639
|
inputMapping: { type: "positionalParams", params: [] },
|
|
409
|
-
//
|
|
410
|
-
//
|
|
640
|
+
// The type check would reject every call in a file that does not
|
|
641
|
+
// import `@prisma/client`, so those files are skipped.
|
|
411
642
|
requiresImport: ["@prisma/client"],
|
|
412
643
|
// A generator with its own `output` puts the client in the project,
|
|
413
|
-
// where the only way to reach it is a relative path.
|
|
644
|
+
// where the only way to reach it is a relative path. Prisma 7's
|
|
645
|
+
// `prisma-client` generator writes no schema beside it.
|
|
414
646
|
generatedModuleMarkers: [GENERATED_CLIENT_MARKER],
|
|
647
|
+
generatedModuleDirs: generatedClientDirs,
|
|
415
648
|
invocationRecognizers: [makeRecognizer(options)],
|
|
416
649
|
// A tagged template is not an invocation, so the raw chain runs on
|
|
417
650
|
// the access walk, which visits calls as well.
|
|
@@ -423,7 +656,7 @@ var declares = {
|
|
|
423
656
|
kind: "effects",
|
|
424
657
|
package: "@suss/framework-prisma",
|
|
425
658
|
dependencies: [{ ecosystem: "npm", name: "@prisma/client" }],
|
|
426
|
-
reads: "Prisma client calls
|
|
659
|
+
reads: "Prisma client calls. Each read and each write becomes a storage-access interaction."
|
|
427
660
|
};
|
|
428
661
|
var index_default = prismaFramework;
|
|
429
662
|
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
|
+
};
|
package/dist/rails.js
CHANGED
|
@@ -304,20 +304,20 @@ var RouteAccumulator = class {
|
|
|
304
304
|
drawDirectory;
|
|
305
305
|
byKey = /* @__PURE__ */ new Map();
|
|
306
306
|
unread = /* @__PURE__ */ new Map();
|
|
307
|
-
/** Per file,
|
|
307
|
+
/** Per file, the gem block calls whose bodies were walked as if the call were not there. */
|
|
308
308
|
walkedBlocks = /* @__PURE__ */ new Map();
|
|
309
309
|
missingDrawn = [];
|
|
310
310
|
/** The block each `concern :name do ... end` declared, for `concerns` to replay. */
|
|
311
311
|
concerns = /* @__PURE__ */ new Map();
|
|
312
312
|
/** Files `draw(:name)` has already read, so a file drawing itself stops. */
|
|
313
313
|
drawn = /* @__PURE__ */ new Set();
|
|
314
|
-
/** Every engine a `mount` may refer to, by
|
|
314
|
+
/** Every engine a `mount` may refer to, keyed by class name. */
|
|
315
315
|
engines = /* @__PURE__ */ new Map();
|
|
316
316
|
/** Engines whose route set is being walked, so an engine that mounts itself stops. */
|
|
317
317
|
mounting = /* @__PURE__ */ new Set();
|
|
318
|
-
/** The file whose statements are being walked, as a gap
|
|
318
|
+
/** The file whose statements are being walked, as a gap message prints it. */
|
|
319
319
|
file = "";
|
|
320
|
-
/** The first route written for an action wins,
|
|
320
|
+
/** The first route written for an action wins, because Rails matches routes in the order they are declared. */
|
|
321
321
|
add(controllerKey, action, route) {
|
|
322
322
|
const key = `${controllerKey}#${action}`;
|
|
323
323
|
if (!this.byKey.has(key)) {
|
|
@@ -880,37 +880,46 @@ var RACK_STATUS_CODE_NAMES = {
|
|
|
880
880
|
network_authentication_required: 511
|
|
881
881
|
};
|
|
882
882
|
var optionsSchema = z.object({
|
|
883
|
-
/**
|
|
883
|
+
/**
|
|
884
|
+
* The directory a bare controller name is looked up under, `app` in a
|
|
885
|
+
* scaffolded Rails app.
|
|
886
|
+
*/
|
|
884
887
|
root: z.string().optional(),
|
|
885
888
|
/**
|
|
886
|
-
* The
|
|
887
|
-
*
|
|
888
|
-
*
|
|
889
|
-
* the file itself.
|
|
889
|
+
* The CLI fills this in with the config file's directory, or with the
|
|
890
|
+
* directory the run reads when there is no file. A relative `root` or
|
|
891
|
+
* `routesFile` resolves against it. A config file does not set it.
|
|
890
892
|
*/
|
|
891
893
|
configDirectory: z.string().optional(),
|
|
892
|
-
/**
|
|
894
|
+
/**
|
|
895
|
+
* Base classes beyond `ApplicationController` whose subclasses are
|
|
896
|
+
* controllers. A config file may not set this; a dependency stub
|
|
897
|
+
* fills it.
|
|
898
|
+
*/
|
|
893
899
|
baseClassNames: z.array(z.string()).optional(),
|
|
894
900
|
/**
|
|
895
|
-
*
|
|
896
|
-
*
|
|
897
|
-
*
|
|
901
|
+
* Controller methods a gem such as Devise adds, beyond the ones Rails
|
|
902
|
+
* defines. They are left off an action's effects the same way Rails'
|
|
903
|
+
* own are.
|
|
898
904
|
*/
|
|
899
905
|
inheritedMethodNames: z.array(z.string()).optional(),
|
|
900
|
-
/**
|
|
906
|
+
/**
|
|
907
|
+
* The routes file, `config/routes.rb` in a scaffolded app. A relative
|
|
908
|
+
* path resolves against `configDirectory` when there is one.
|
|
909
|
+
*/
|
|
901
910
|
routesFile: z.string().optional(),
|
|
902
911
|
/**
|
|
903
|
-
* Directories
|
|
904
|
-
*
|
|
905
|
-
*
|
|
906
|
-
*
|
|
912
|
+
* Directories that each contain an engine, with its `Rails::Engine`
|
|
913
|
+
* class under `lib/` and its routes in `config/routes.rb`. A `*` in a
|
|
914
|
+
* segment matches any one directory, so `plugins/*` covers every
|
|
915
|
+
* plugin. Relative paths resolve against `configDirectory`.
|
|
907
916
|
*/
|
|
908
917
|
engineRoots: z.array(z.string()).optional(),
|
|
909
918
|
/**
|
|
910
|
-
*
|
|
911
|
-
*
|
|
912
|
-
*
|
|
913
|
-
*
|
|
919
|
+
* Other files that add routes through a `Rails.application.routes.draw`,
|
|
920
|
+
* `.append` or `.prepend` block, most often a plugin's `plugin.rb`.
|
|
921
|
+
* They take `*` the same way `engineRoots` does, and relative paths
|
|
922
|
+
* resolve against `configDirectory`.
|
|
914
923
|
*/
|
|
915
924
|
routesFiles: z.array(z.string()).optional()
|
|
916
925
|
}).strict();
|
|
@@ -1063,9 +1072,18 @@ function railsFramework(options = {}) {
|
|
|
1063
1072
|
discovery: [pattern],
|
|
1064
1073
|
bodyBlocks: BODY_BLOCKS,
|
|
1065
1074
|
inflections,
|
|
1066
|
-
//
|
|
1067
|
-
|
|
1068
|
-
//
|
|
1075
|
+
// An action reads the request through these, and is handed nothing.
|
|
1076
|
+
requestAccessors: ["params", "request"],
|
|
1077
|
+
// Rails merges the path, the query string and the body into `params`,
|
|
1078
|
+
// so an action's read of one cannot be told from the others and all
|
|
1079
|
+
// of them are spelled `params`.
|
|
1080
|
+
requestSpelling: {
|
|
1081
|
+
headers: { path: ["request", "headers"], saysWhichField: true },
|
|
1082
|
+
params: { path: ["params"], saysWhichField: true }
|
|
1083
|
+
},
|
|
1084
|
+
// These files change an action's binding without being walked, so the
|
|
1085
|
+
// cache key has to list them. This runs before the grammar loads, so
|
|
1086
|
+
// nothing here parses Ruby.
|
|
1069
1087
|
discoveryInputs: () => [
|
|
1070
1088
|
routesFile,
|
|
1071
1089
|
...drawableRoutesFiles(routesFile),
|
|
@@ -1096,7 +1114,7 @@ var declares = {
|
|
|
1096
1114
|
{ ecosystem: "rubygems", name: "rails" },
|
|
1097
1115
|
{ ecosystem: "rubygems", name: "railties" }
|
|
1098
1116
|
],
|
|
1099
|
-
reads: "Rails controller actions (Ruby), bound to the method and path `config/routes.rb` gives each one
|
|
1117
|
+
reads: "Rails controller actions (Ruby), bound to the method and path `config/routes.rb` gives each one. An action the routes file does not reach is still discovered, with no boundary.",
|
|
1100
1118
|
configuration: {
|
|
1101
1119
|
file: "suss.rails.json",
|
|
1102
1120
|
example: {
|
|
@@ -1106,7 +1124,8 @@ var declares = {
|
|
|
1106
1124
|
routesFiles: ["plugins/*/plugin.rb"]
|
|
1107
1125
|
},
|
|
1108
1126
|
required: false,
|
|
1109
|
-
why: "the app directory a controller is defined under and the routes file suss reads each action's method and path from; rails new scaffolds both at these paths. engineRoots lists where the project keeps engines it mounts, and routesFiles any other file that adds routes, when it has either."
|
|
1127
|
+
why: "the app directory a controller is defined under and the routes file suss reads each action's method and path from; rails new scaffolds both at these paths. engineRoots lists where the project keeps engines it mounts, and routesFiles any other file that adds routes, when it has either.",
|
|
1128
|
+
defaults: { root: "app", routesFile: "config/routes.rb" }
|
|
1110
1129
|
}
|
|
1111
1130
|
};
|
|
1112
1131
|
var index_default = railsFramework;
|
package/dist/react-query.js
CHANGED
|
@@ -134,7 +134,7 @@ var declares = {
|
|
|
134
134
|
kind: "framework",
|
|
135
135
|
package: "@suss/framework-react-query",
|
|
136
136
|
dependencies: [{ ecosystem: "npm", name: "@tanstack/react-query" }],
|
|
137
|
-
reads: "TanStack Query hooks
|
|
137
|
+
reads: "TanStack Query hooks. The pack links a component to the query function that its `useQuery` or `useMutation` call runs."
|
|
138
138
|
};
|
|
139
139
|
var index_default = reactQueryFramework;
|
|
140
140
|
export {
|