@suss/adapter-python 0.5.11 → 0.7.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/dist/index.cjs +153 -29
- package/dist/index.d.cts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +153 -29
- package/package.json +5 -4
package/dist/index.cjs
CHANGED
|
@@ -15747,7 +15747,7 @@ function date4(params) {
|
|
|
15747
15747
|
// ../../../node_modules/zod/v4/classic/external.js
|
|
15748
15748
|
config(en_default());
|
|
15749
15749
|
|
|
15750
|
-
// ../../ir-core/dist/chunk-
|
|
15750
|
+
// ../../ir-core/dist/chunk-TEXSN75M.js
|
|
15751
15751
|
function gqlIdentityKey(typeName, fieldName) {
|
|
15752
15752
|
return `gql:${typeName}.${fieldName}`;
|
|
15753
15753
|
}
|
|
@@ -16116,27 +16116,41 @@ var runtimeConfigSemantics = defineBoundarySemantics({
|
|
|
16116
16116
|
identityKey: () => null
|
|
16117
16117
|
}
|
|
16118
16118
|
});
|
|
16119
|
-
var
|
|
16120
|
-
name: external_exports.literal("storage
|
|
16121
|
-
storageSystem: external_exports.enum(["postgres", "mysql", "sqlite"]),
|
|
16119
|
+
var StorageSemanticsSchema = external_exports.object({
|
|
16120
|
+
name: external_exports.literal("storage"),
|
|
16122
16121
|
/**
|
|
16123
|
-
*
|
|
16124
|
-
*
|
|
16122
|
+
* Which store this is: postgres, mysql, sqlite, dynamodb. Two
|
|
16123
|
+
* products' containers can share a name, so this keeps them apart,
|
|
16124
|
+
* and it is what a finding message calls the store.
|
|
16125
|
+
*/
|
|
16126
|
+
storageSystem: external_exports.string(),
|
|
16127
|
+
/**
|
|
16128
|
+
* The ORM, schema, or deployment scope. A single-database setup uses
|
|
16129
|
+
* `"default"`, and a monorepo with several schemas gives each one its
|
|
16130
|
+
* own value.
|
|
16125
16131
|
*/
|
|
16126
16132
|
scope: external_exports.string(),
|
|
16127
16133
|
/**
|
|
16128
|
-
* The table or
|
|
16129
|
-
* the source states one this reader could not
|
|
16130
|
-
* pairs with nothing, rather than with
|
|
16131
|
-
* happens to spell.
|
|
16134
|
+
* The table, bucket, collection, or index, as the source declares
|
|
16135
|
+
* it, or null when the source states one this reader could not
|
|
16136
|
+
* settle. A null container pairs with nothing, rather than with
|
|
16137
|
+
* whatever its source text happens to spell.
|
|
16138
|
+
*/
|
|
16139
|
+
container: external_exports.string().nullable(),
|
|
16140
|
+
/**
|
|
16141
|
+
* A secondary way into the container, a DynamoDB global secondary
|
|
16142
|
+
* index or an Elasticsearch alias, each with its own key fields.
|
|
16143
|
+
* Null means the container's own primary way in. A query through an
|
|
16144
|
+
* index and a query through the table are different accesses, so
|
|
16145
|
+
* they pair separately.
|
|
16132
16146
|
*/
|
|
16133
|
-
|
|
16147
|
+
accessPath: external_exports.string().nullable()
|
|
16134
16148
|
});
|
|
16135
|
-
var
|
|
16136
|
-
name: "storage
|
|
16137
|
-
schema:
|
|
16149
|
+
var storageSemantics = defineBoundarySemantics({
|
|
16150
|
+
name: "storage",
|
|
16151
|
+
schema: StorageSemanticsSchema,
|
|
16138
16152
|
behavior: {
|
|
16139
|
-
/** A query returns rows, not a status and a body. */
|
|
16153
|
+
/** A query returns rows or items, not a status and a body. */
|
|
16140
16154
|
exchangesHttpResponses: false,
|
|
16141
16155
|
reportsUnpairedItself: false,
|
|
16142
16156
|
identityKey: () => null
|
|
@@ -16148,7 +16162,7 @@ var SemanticsSchema = external_exports.discriminatedUnion("name", [
|
|
|
16148
16162
|
graphqlResolverSemantics.schema,
|
|
16149
16163
|
graphqlOperationSemantics.schema,
|
|
16150
16164
|
runtimeConfigSemantics.schema,
|
|
16151
|
-
|
|
16165
|
+
storageSemantics.schema,
|
|
16152
16166
|
messageBusSemantics.schema
|
|
16153
16167
|
]);
|
|
16154
16168
|
var DEFINITIONS = [
|
|
@@ -16157,7 +16171,7 @@ var DEFINITIONS = [
|
|
|
16157
16171
|
graphqlResolverSemantics,
|
|
16158
16172
|
graphqlOperationSemantics,
|
|
16159
16173
|
runtimeConfigSemantics,
|
|
16160
|
-
|
|
16174
|
+
storageSemantics,
|
|
16161
16175
|
messageBusSemantics
|
|
16162
16176
|
];
|
|
16163
16177
|
var BY_NAME = new Map(
|
|
@@ -16297,19 +16311,97 @@ function namedOrNull(value, field2) {
|
|
|
16297
16311
|
}
|
|
16298
16312
|
return value;
|
|
16299
16313
|
}
|
|
16300
|
-
function
|
|
16314
|
+
function storageBinding(opts) {
|
|
16301
16315
|
return {
|
|
16302
|
-
transport: opts.storageSystem,
|
|
16316
|
+
transport: opts.transport ?? opts.storageSystem,
|
|
16303
16317
|
semantics: {
|
|
16304
|
-
name: "storage
|
|
16318
|
+
name: "storage",
|
|
16305
16319
|
storageSystem: opts.storageSystem,
|
|
16306
16320
|
scope: opts.scope,
|
|
16307
|
-
|
|
16321
|
+
container: namedOrNull(opts.container, "storage container"),
|
|
16322
|
+
accessPath: opts.accessPath ?? null
|
|
16308
16323
|
},
|
|
16309
16324
|
recognition: opts.recognition
|
|
16310
16325
|
};
|
|
16311
16326
|
}
|
|
16312
16327
|
|
|
16328
|
+
// src/rawSql.ts
|
|
16329
|
+
var import_sql = require("@suss/sql");
|
|
16330
|
+
function rawSqlEffects(calls, options) {
|
|
16331
|
+
if (options.patterns.length === 0) {
|
|
16332
|
+
return [];
|
|
16333
|
+
}
|
|
16334
|
+
const effects = [];
|
|
16335
|
+
for (const call of calls) {
|
|
16336
|
+
const pattern = patternFor(call, options);
|
|
16337
|
+
if (pattern === void 0) {
|
|
16338
|
+
continue;
|
|
16339
|
+
}
|
|
16340
|
+
const statement = statementIn(call);
|
|
16341
|
+
if (statement === null) {
|
|
16342
|
+
continue;
|
|
16343
|
+
}
|
|
16344
|
+
for (const access of (0, import_sql.readSqlAccess)(statement, {
|
|
16345
|
+
dialect: pattern.storageSystem
|
|
16346
|
+
})) {
|
|
16347
|
+
effects.push({
|
|
16348
|
+
type: "interaction",
|
|
16349
|
+
binding: storageBinding({
|
|
16350
|
+
recognition: `python-${pattern.module}`,
|
|
16351
|
+
storageSystem: pattern.storageSystem,
|
|
16352
|
+
scope: pattern.module,
|
|
16353
|
+
container: access.table
|
|
16354
|
+
}),
|
|
16355
|
+
callee: field(call, "function")?.text ?? "",
|
|
16356
|
+
interaction: {
|
|
16357
|
+
class: "storage-access",
|
|
16358
|
+
kind: access.kind,
|
|
16359
|
+
fields: access.fields,
|
|
16360
|
+
...access.selector.length > 0 ? { selector: access.selector } : {},
|
|
16361
|
+
operation: field(call, "function")?.text ?? ""
|
|
16362
|
+
}
|
|
16363
|
+
});
|
|
16364
|
+
}
|
|
16365
|
+
}
|
|
16366
|
+
return effects;
|
|
16367
|
+
}
|
|
16368
|
+
function patternFor(call, options) {
|
|
16369
|
+
const callee = field(call, "function");
|
|
16370
|
+
if (callee === null || callee.type !== "identifier") {
|
|
16371
|
+
return void 0;
|
|
16372
|
+
}
|
|
16373
|
+
const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${callee.text}`);
|
|
16374
|
+
if (from === void 0) {
|
|
16375
|
+
return void 0;
|
|
16376
|
+
}
|
|
16377
|
+
const module2 = String(from[1]);
|
|
16378
|
+
return options.patterns.find(
|
|
16379
|
+
(pattern) => pattern.module === module2 && pattern.functions.includes(callee.text)
|
|
16380
|
+
);
|
|
16381
|
+
}
|
|
16382
|
+
function statementIn(call) {
|
|
16383
|
+
const args = field(call, "arguments");
|
|
16384
|
+
const first = args?.namedChildren.find((child) => child !== null) ?? null;
|
|
16385
|
+
if (first === null || first.type !== "string") {
|
|
16386
|
+
return null;
|
|
16387
|
+
}
|
|
16388
|
+
const parts = [""];
|
|
16389
|
+
for (const child of first.namedChildren) {
|
|
16390
|
+
if (child === null) {
|
|
16391
|
+
continue;
|
|
16392
|
+
}
|
|
16393
|
+
if (child.type === "string_content") {
|
|
16394
|
+
parts[parts.length - 1] += child.text;
|
|
16395
|
+
continue;
|
|
16396
|
+
}
|
|
16397
|
+
if (child.type === "interpolation") {
|
|
16398
|
+
parts.push("");
|
|
16399
|
+
}
|
|
16400
|
+
}
|
|
16401
|
+
const statement = (0, import_sql.sqlFromParts)(parts);
|
|
16402
|
+
return statement.trim() === "" ? null : statement;
|
|
16403
|
+
}
|
|
16404
|
+
|
|
16313
16405
|
// src/facts/resolve.ts
|
|
16314
16406
|
var import_datalog2 = require("@suss/datalog");
|
|
16315
16407
|
|
|
@@ -17160,11 +17252,11 @@ function effectFor(pattern, chain, options) {
|
|
|
17160
17252
|
];
|
|
17161
17253
|
return {
|
|
17162
17254
|
type: "interaction",
|
|
17163
|
-
binding:
|
|
17255
|
+
binding: storageBinding({
|
|
17164
17256
|
recognition: "python-storage",
|
|
17165
17257
|
storageSystem: pattern.storageSystem,
|
|
17166
17258
|
scope: pattern.module,
|
|
17167
|
-
|
|
17259
|
+
container: chain.subject
|
|
17168
17260
|
}),
|
|
17169
17261
|
callee: chain.last.text,
|
|
17170
17262
|
interaction: {
|
|
@@ -17907,10 +17999,17 @@ function buildRouteUnit(options) {
|
|
|
17907
17999
|
() => readReturnAnnotation(definitionNode, enclosingScope, ctx)
|
|
17908
18000
|
);
|
|
17909
18001
|
const effects = invocationEffects(definitionNode);
|
|
17910
|
-
const storage = storageLookup === void 0 ? [] :
|
|
17911
|
-
...
|
|
17912
|
-
|
|
17913
|
-
|
|
18002
|
+
const storage = storageLookup === void 0 ? [] : [
|
|
18003
|
+
...storageEffects(bodyCalls(definitionNode), {
|
|
18004
|
+
...storageLookup,
|
|
18005
|
+
filePath: storageLookup.factsPath
|
|
18006
|
+
}),
|
|
18007
|
+
...rawSqlEffects(bodyCalls(definitionNode), {
|
|
18008
|
+
facts: storageLookup.facts,
|
|
18009
|
+
filePath: storageLookup.factsPath,
|
|
18010
|
+
patterns: storageLookup.rawSql ?? []
|
|
18011
|
+
})
|
|
18012
|
+
];
|
|
17914
18013
|
const perReturn = branchesFromReturns(
|
|
17915
18014
|
readsReturnedStatus,
|
|
17916
18015
|
definitionNode,
|
|
@@ -19266,6 +19365,7 @@ async function extractPythonProject(options) {
|
|
|
19266
19365
|
(pack) => pack.discovery.some((pattern) => pattern.routerComposition !== void 0)
|
|
19267
19366
|
);
|
|
19268
19367
|
const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
|
|
19368
|
+
const rawSqlPatterns = options.packs.flatMap((pack) => pack.rawSql ?? []);
|
|
19269
19369
|
const needsValues = mountsRouters || storagePatterns.length > 0;
|
|
19270
19370
|
const definitions = /* @__PURE__ */ new Map();
|
|
19271
19371
|
for (const { file: file2, root, module: moduleBinding } of bound) {
|
|
@@ -19295,14 +19395,15 @@ async function extractPythonProject(options) {
|
|
|
19295
19395
|
absoluteFile: file2,
|
|
19296
19396
|
routerIndex,
|
|
19297
19397
|
gapHandling,
|
|
19298
|
-
...storagePatterns.length > 0 ? {
|
|
19398
|
+
...storagePatterns.length > 0 || rawSqlPatterns.length > 0 ? {
|
|
19299
19399
|
storage: {
|
|
19300
19400
|
facts: db,
|
|
19301
19401
|
factsPath: file2,
|
|
19302
19402
|
patterns: storagePatterns,
|
|
19303
19403
|
definitionAt: (key) => definitions.get(key),
|
|
19304
19404
|
couldMatch,
|
|
19305
|
-
leadsToStorage
|
|
19405
|
+
leadsToStorage,
|
|
19406
|
+
rawSql: rawSqlPatterns
|
|
19306
19407
|
}
|
|
19307
19408
|
} : {}
|
|
19308
19409
|
});
|
|
@@ -19313,8 +19414,31 @@ async function extractPythonProject(options) {
|
|
|
19313
19414
|
emitEntryFact(db, file2, raw.identity.range, raw.identity.name);
|
|
19314
19415
|
}
|
|
19315
19416
|
}
|
|
19417
|
+
stampModuleImports(summaries, db, displayPathOf);
|
|
19316
19418
|
return { summaries, facts: db };
|
|
19317
19419
|
}
|
|
19420
|
+
function stampModuleImports(summaries, db, displayPathOf) {
|
|
19421
|
+
const importsByFile = /* @__PURE__ */ new Map();
|
|
19422
|
+
for (const [from, , to] of db.facts("pyImportResolved")) {
|
|
19423
|
+
if (typeof from !== "string" || typeof to !== "string") {
|
|
19424
|
+
continue;
|
|
19425
|
+
}
|
|
19426
|
+
const key = displayPathOf(from);
|
|
19427
|
+
const seen = importsByFile.get(key) ?? /* @__PURE__ */ new Set();
|
|
19428
|
+
seen.add(displayPathOf(to));
|
|
19429
|
+
importsByFile.set(key, seen);
|
|
19430
|
+
}
|
|
19431
|
+
for (const summary of summaries) {
|
|
19432
|
+
const imports = importsByFile.get(summary.location.file);
|
|
19433
|
+
if (imports === void 0 || imports.size === 0) {
|
|
19434
|
+
continue;
|
|
19435
|
+
}
|
|
19436
|
+
summary.metadata = {
|
|
19437
|
+
...summary.metadata ?? {},
|
|
19438
|
+
moduleImports: [...imports].sort()
|
|
19439
|
+
};
|
|
19440
|
+
}
|
|
19441
|
+
}
|
|
19318
19442
|
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
19319
19443
|
"__pycache__",
|
|
19320
19444
|
".venv",
|
package/dist/index.d.cts
CHANGED
|
@@ -189,6 +189,8 @@ interface PythonPack {
|
|
|
189
189
|
projectModules?: string[];
|
|
190
190
|
/** What the library's own database queries look like. The README says how one is matched. */
|
|
191
191
|
storage?: StoragePattern[];
|
|
192
|
+
/** How the library lets a project hand the database SQL it wrote itself. */
|
|
193
|
+
rawSql?: RawSqlPattern[];
|
|
192
194
|
}
|
|
193
195
|
/**
|
|
194
196
|
* A call talks to the database when the method behind it says it returns one
|
|
@@ -212,6 +214,20 @@ interface StoragePattern {
|
|
|
212
214
|
/** Which database the library is talking to, for the boundary binding. */
|
|
213
215
|
storageSystem: "postgres" | "mysql" | "sqlite";
|
|
214
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* The function a library gives a project for handing the database a
|
|
219
|
+
* statement written as SQL, and where it comes from. SQLAlchemy exports
|
|
220
|
+
* `text` from `sqlalchemy`. A local function of the same name is
|
|
221
|
+
* somebody else's, so the import is what settles a match.
|
|
222
|
+
*/
|
|
223
|
+
interface RawSqlPattern {
|
|
224
|
+
/** The module the function is imported from. */
|
|
225
|
+
module: string;
|
|
226
|
+
/** The functions that take a statement written as SQL. */
|
|
227
|
+
functions: string[];
|
|
228
|
+
/** Which database the library is talking to, for the boundary binding. */
|
|
229
|
+
storageSystem: "postgres" | "mysql" | "sqlite";
|
|
230
|
+
}
|
|
215
231
|
type PythonDiscoveryPattern = DecoratedClassRoute | DecoratedFunctionRoute;
|
|
216
232
|
/** Conventions both kinds of route share. Each one describes what the library does, never a project's choice. */
|
|
217
233
|
interface RouteConventions {
|
|
@@ -370,6 +386,8 @@ interface StorageLookup {
|
|
|
370
386
|
readonly definitionAt: (key: string) => Node | undefined;
|
|
371
387
|
readonly couldMatch: ReadonlySet<string>;
|
|
372
388
|
readonly leadsToStorage: ReadonlySet<string>;
|
|
389
|
+
/** What a pack says about statements a project writes as SQL itself. */
|
|
390
|
+
readonly rawSql?: readonly RawSqlPattern[];
|
|
373
391
|
}
|
|
374
392
|
|
|
375
393
|
/**
|
|
@@ -551,4 +569,4 @@ declare function extractPythonProject(options: ExtractPythonOptions): Promise<Ex
|
|
|
551
569
|
/** Every `.py` file under `root`, depth-first, skipping the usual non-source directories. */
|
|
552
570
|
declare function findPythonFiles(root: string): string[];
|
|
553
571
|
|
|
554
|
-
export { type AnnotationContext, type Binding, type BoundPythonFile, type DecoratedClassRoute, type DecoratedFunctionRoute, type DecoratorArg, type DecoratorClassification, type DiscoveryOptions, type ExtractPythonOptions, type ExtractPythonResult, type ModuleBinding, type ModuleResolution, type ModuleResolverOptions, type MountObjectCarrier, type MountObjectPrefix, type MountPrefixEffect, type NoValuePrefix, type PathRepeatedSlashes, type PrefixTrailingSlash, type PythonDiscoveryPattern, type PythonPack, type RelativeModuleSpec, type RouteConventions, type RoutePrefixResolution, type RouterComposition, type RouterIndex, type Scope, type ScopeKind, type StoragePattern, annotationToShape, bindModule, buildRouterIndex, classifyDecorator, containedValues, discoverUnits, emitEntryFact, emitModuleImportFacts, emitValueFacts, extractPythonProject, findPythonFiles, nodeId, objectReturnedBy, parsePython, resolveAbsoluteModule, resolveCalls, resolveModule, resolveName, resolveRelativeModule, shapeFromName, unitKey };
|
|
572
|
+
export { type AnnotationContext, type Binding, type BoundPythonFile, type DecoratedClassRoute, type DecoratedFunctionRoute, type DecoratorArg, type DecoratorClassification, type DiscoveryOptions, type ExtractPythonOptions, type ExtractPythonResult, type ModuleBinding, type ModuleResolution, type ModuleResolverOptions, type MountObjectCarrier, type MountObjectPrefix, type MountPrefixEffect, type NoValuePrefix, type PathRepeatedSlashes, type PrefixTrailingSlash, type PythonDiscoveryPattern, type PythonPack, type RawSqlPattern, type RelativeModuleSpec, type RouteConventions, type RoutePrefixResolution, type RouterComposition, type RouterIndex, type Scope, type ScopeKind, type StoragePattern, annotationToShape, bindModule, buildRouterIndex, classifyDecorator, containedValues, discoverUnits, emitEntryFact, emitModuleImportFacts, emitValueFacts, extractPythonProject, findPythonFiles, nodeId, objectReturnedBy, parsePython, resolveAbsoluteModule, resolveCalls, resolveModule, resolveName, resolveRelativeModule, shapeFromName, unitKey };
|
package/dist/index.d.ts
CHANGED
|
@@ -189,6 +189,8 @@ interface PythonPack {
|
|
|
189
189
|
projectModules?: string[];
|
|
190
190
|
/** What the library's own database queries look like. The README says how one is matched. */
|
|
191
191
|
storage?: StoragePattern[];
|
|
192
|
+
/** How the library lets a project hand the database SQL it wrote itself. */
|
|
193
|
+
rawSql?: RawSqlPattern[];
|
|
192
194
|
}
|
|
193
195
|
/**
|
|
194
196
|
* A call talks to the database when the method behind it says it returns one
|
|
@@ -212,6 +214,20 @@ interface StoragePattern {
|
|
|
212
214
|
/** Which database the library is talking to, for the boundary binding. */
|
|
213
215
|
storageSystem: "postgres" | "mysql" | "sqlite";
|
|
214
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* The function a library gives a project for handing the database a
|
|
219
|
+
* statement written as SQL, and where it comes from. SQLAlchemy exports
|
|
220
|
+
* `text` from `sqlalchemy`. A local function of the same name is
|
|
221
|
+
* somebody else's, so the import is what settles a match.
|
|
222
|
+
*/
|
|
223
|
+
interface RawSqlPattern {
|
|
224
|
+
/** The module the function is imported from. */
|
|
225
|
+
module: string;
|
|
226
|
+
/** The functions that take a statement written as SQL. */
|
|
227
|
+
functions: string[];
|
|
228
|
+
/** Which database the library is talking to, for the boundary binding. */
|
|
229
|
+
storageSystem: "postgres" | "mysql" | "sqlite";
|
|
230
|
+
}
|
|
215
231
|
type PythonDiscoveryPattern = DecoratedClassRoute | DecoratedFunctionRoute;
|
|
216
232
|
/** Conventions both kinds of route share. Each one describes what the library does, never a project's choice. */
|
|
217
233
|
interface RouteConventions {
|
|
@@ -370,6 +386,8 @@ interface StorageLookup {
|
|
|
370
386
|
readonly definitionAt: (key: string) => Node | undefined;
|
|
371
387
|
readonly couldMatch: ReadonlySet<string>;
|
|
372
388
|
readonly leadsToStorage: ReadonlySet<string>;
|
|
389
|
+
/** What a pack says about statements a project writes as SQL itself. */
|
|
390
|
+
readonly rawSql?: readonly RawSqlPattern[];
|
|
373
391
|
}
|
|
374
392
|
|
|
375
393
|
/**
|
|
@@ -551,4 +569,4 @@ declare function extractPythonProject(options: ExtractPythonOptions): Promise<Ex
|
|
|
551
569
|
/** Every `.py` file under `root`, depth-first, skipping the usual non-source directories. */
|
|
552
570
|
declare function findPythonFiles(root: string): string[];
|
|
553
571
|
|
|
554
|
-
export { type AnnotationContext, type Binding, type BoundPythonFile, type DecoratedClassRoute, type DecoratedFunctionRoute, type DecoratorArg, type DecoratorClassification, type DiscoveryOptions, type ExtractPythonOptions, type ExtractPythonResult, type ModuleBinding, type ModuleResolution, type ModuleResolverOptions, type MountObjectCarrier, type MountObjectPrefix, type MountPrefixEffect, type NoValuePrefix, type PathRepeatedSlashes, type PrefixTrailingSlash, type PythonDiscoveryPattern, type PythonPack, type RelativeModuleSpec, type RouteConventions, type RoutePrefixResolution, type RouterComposition, type RouterIndex, type Scope, type ScopeKind, type StoragePattern, annotationToShape, bindModule, buildRouterIndex, classifyDecorator, containedValues, discoverUnits, emitEntryFact, emitModuleImportFacts, emitValueFacts, extractPythonProject, findPythonFiles, nodeId, objectReturnedBy, parsePython, resolveAbsoluteModule, resolveCalls, resolveModule, resolveName, resolveRelativeModule, shapeFromName, unitKey };
|
|
572
|
+
export { type AnnotationContext, type Binding, type BoundPythonFile, type DecoratedClassRoute, type DecoratedFunctionRoute, type DecoratorArg, type DecoratorClassification, type DiscoveryOptions, type ExtractPythonOptions, type ExtractPythonResult, type ModuleBinding, type ModuleResolution, type ModuleResolverOptions, type MountObjectCarrier, type MountObjectPrefix, type MountPrefixEffect, type NoValuePrefix, type PathRepeatedSlashes, type PrefixTrailingSlash, type PythonDiscoveryPattern, type PythonPack, type RawSqlPattern, type RelativeModuleSpec, type RouteConventions, type RoutePrefixResolution, type RouterComposition, type RouterIndex, type Scope, type ScopeKind, type StoragePattern, annotationToShape, bindModule, buildRouterIndex, classifyDecorator, containedValues, discoverUnits, emitEntryFact, emitModuleImportFacts, emitValueFacts, extractPythonProject, findPythonFiles, nodeId, objectReturnedBy, parsePython, resolveAbsoluteModule, resolveCalls, resolveModule, resolveName, resolveRelativeModule, shapeFromName, unitKey };
|
package/dist/index.js
CHANGED
|
@@ -15703,7 +15703,7 @@ function date4(params) {
|
|
|
15703
15703
|
// ../../../node_modules/zod/v4/classic/external.js
|
|
15704
15704
|
config(en_default());
|
|
15705
15705
|
|
|
15706
|
-
// ../../ir-core/dist/chunk-
|
|
15706
|
+
// ../../ir-core/dist/chunk-TEXSN75M.js
|
|
15707
15707
|
function gqlIdentityKey(typeName, fieldName) {
|
|
15708
15708
|
return `gql:${typeName}.${fieldName}`;
|
|
15709
15709
|
}
|
|
@@ -16072,27 +16072,41 @@ var runtimeConfigSemantics = defineBoundarySemantics({
|
|
|
16072
16072
|
identityKey: () => null
|
|
16073
16073
|
}
|
|
16074
16074
|
});
|
|
16075
|
-
var
|
|
16076
|
-
name: external_exports.literal("storage
|
|
16077
|
-
storageSystem: external_exports.enum(["postgres", "mysql", "sqlite"]),
|
|
16075
|
+
var StorageSemanticsSchema = external_exports.object({
|
|
16076
|
+
name: external_exports.literal("storage"),
|
|
16078
16077
|
/**
|
|
16079
|
-
*
|
|
16080
|
-
*
|
|
16078
|
+
* Which store this is: postgres, mysql, sqlite, dynamodb. Two
|
|
16079
|
+
* products' containers can share a name, so this keeps them apart,
|
|
16080
|
+
* and it is what a finding message calls the store.
|
|
16081
|
+
*/
|
|
16082
|
+
storageSystem: external_exports.string(),
|
|
16083
|
+
/**
|
|
16084
|
+
* The ORM, schema, or deployment scope. A single-database setup uses
|
|
16085
|
+
* `"default"`, and a monorepo with several schemas gives each one its
|
|
16086
|
+
* own value.
|
|
16081
16087
|
*/
|
|
16082
16088
|
scope: external_exports.string(),
|
|
16083
16089
|
/**
|
|
16084
|
-
* The table or
|
|
16085
|
-
* the source states one this reader could not
|
|
16086
|
-
* pairs with nothing, rather than with
|
|
16087
|
-
* happens to spell.
|
|
16090
|
+
* The table, bucket, collection, or index, as the source declares
|
|
16091
|
+
* it, or null when the source states one this reader could not
|
|
16092
|
+
* settle. A null container pairs with nothing, rather than with
|
|
16093
|
+
* whatever its source text happens to spell.
|
|
16094
|
+
*/
|
|
16095
|
+
container: external_exports.string().nullable(),
|
|
16096
|
+
/**
|
|
16097
|
+
* A secondary way into the container, a DynamoDB global secondary
|
|
16098
|
+
* index or an Elasticsearch alias, each with its own key fields.
|
|
16099
|
+
* Null means the container's own primary way in. A query through an
|
|
16100
|
+
* index and a query through the table are different accesses, so
|
|
16101
|
+
* they pair separately.
|
|
16088
16102
|
*/
|
|
16089
|
-
|
|
16103
|
+
accessPath: external_exports.string().nullable()
|
|
16090
16104
|
});
|
|
16091
|
-
var
|
|
16092
|
-
name: "storage
|
|
16093
|
-
schema:
|
|
16105
|
+
var storageSemantics = defineBoundarySemantics({
|
|
16106
|
+
name: "storage",
|
|
16107
|
+
schema: StorageSemanticsSchema,
|
|
16094
16108
|
behavior: {
|
|
16095
|
-
/** A query returns rows, not a status and a body. */
|
|
16109
|
+
/** A query returns rows or items, not a status and a body. */
|
|
16096
16110
|
exchangesHttpResponses: false,
|
|
16097
16111
|
reportsUnpairedItself: false,
|
|
16098
16112
|
identityKey: () => null
|
|
@@ -16104,7 +16118,7 @@ var SemanticsSchema = external_exports.discriminatedUnion("name", [
|
|
|
16104
16118
|
graphqlResolverSemantics.schema,
|
|
16105
16119
|
graphqlOperationSemantics.schema,
|
|
16106
16120
|
runtimeConfigSemantics.schema,
|
|
16107
|
-
|
|
16121
|
+
storageSemantics.schema,
|
|
16108
16122
|
messageBusSemantics.schema
|
|
16109
16123
|
]);
|
|
16110
16124
|
var DEFINITIONS = [
|
|
@@ -16113,7 +16127,7 @@ var DEFINITIONS = [
|
|
|
16113
16127
|
graphqlResolverSemantics,
|
|
16114
16128
|
graphqlOperationSemantics,
|
|
16115
16129
|
runtimeConfigSemantics,
|
|
16116
|
-
|
|
16130
|
+
storageSemantics,
|
|
16117
16131
|
messageBusSemantics
|
|
16118
16132
|
];
|
|
16119
16133
|
var BY_NAME = new Map(
|
|
@@ -16253,19 +16267,97 @@ function namedOrNull(value, field2) {
|
|
|
16253
16267
|
}
|
|
16254
16268
|
return value;
|
|
16255
16269
|
}
|
|
16256
|
-
function
|
|
16270
|
+
function storageBinding(opts) {
|
|
16257
16271
|
return {
|
|
16258
|
-
transport: opts.storageSystem,
|
|
16272
|
+
transport: opts.transport ?? opts.storageSystem,
|
|
16259
16273
|
semantics: {
|
|
16260
|
-
name: "storage
|
|
16274
|
+
name: "storage",
|
|
16261
16275
|
storageSystem: opts.storageSystem,
|
|
16262
16276
|
scope: opts.scope,
|
|
16263
|
-
|
|
16277
|
+
container: namedOrNull(opts.container, "storage container"),
|
|
16278
|
+
accessPath: opts.accessPath ?? null
|
|
16264
16279
|
},
|
|
16265
16280
|
recognition: opts.recognition
|
|
16266
16281
|
};
|
|
16267
16282
|
}
|
|
16268
16283
|
|
|
16284
|
+
// src/rawSql.ts
|
|
16285
|
+
import { readSqlAccess, sqlFromParts } from "@suss/sql";
|
|
16286
|
+
function rawSqlEffects(calls, options) {
|
|
16287
|
+
if (options.patterns.length === 0) {
|
|
16288
|
+
return [];
|
|
16289
|
+
}
|
|
16290
|
+
const effects = [];
|
|
16291
|
+
for (const call of calls) {
|
|
16292
|
+
const pattern = patternFor(call, options);
|
|
16293
|
+
if (pattern === void 0) {
|
|
16294
|
+
continue;
|
|
16295
|
+
}
|
|
16296
|
+
const statement = statementIn(call);
|
|
16297
|
+
if (statement === null) {
|
|
16298
|
+
continue;
|
|
16299
|
+
}
|
|
16300
|
+
for (const access of readSqlAccess(statement, {
|
|
16301
|
+
dialect: pattern.storageSystem
|
|
16302
|
+
})) {
|
|
16303
|
+
effects.push({
|
|
16304
|
+
type: "interaction",
|
|
16305
|
+
binding: storageBinding({
|
|
16306
|
+
recognition: `python-${pattern.module}`,
|
|
16307
|
+
storageSystem: pattern.storageSystem,
|
|
16308
|
+
scope: pattern.module,
|
|
16309
|
+
container: access.table
|
|
16310
|
+
}),
|
|
16311
|
+
callee: field(call, "function")?.text ?? "",
|
|
16312
|
+
interaction: {
|
|
16313
|
+
class: "storage-access",
|
|
16314
|
+
kind: access.kind,
|
|
16315
|
+
fields: access.fields,
|
|
16316
|
+
...access.selector.length > 0 ? { selector: access.selector } : {},
|
|
16317
|
+
operation: field(call, "function")?.text ?? ""
|
|
16318
|
+
}
|
|
16319
|
+
});
|
|
16320
|
+
}
|
|
16321
|
+
}
|
|
16322
|
+
return effects;
|
|
16323
|
+
}
|
|
16324
|
+
function patternFor(call, options) {
|
|
16325
|
+
const callee = field(call, "function");
|
|
16326
|
+
if (callee === null || callee.type !== "identifier") {
|
|
16327
|
+
return void 0;
|
|
16328
|
+
}
|
|
16329
|
+
const from = options.facts.facts("pyImportedName").find((row) => String(row[0]) === `${options.filePath}#${callee.text}`);
|
|
16330
|
+
if (from === void 0) {
|
|
16331
|
+
return void 0;
|
|
16332
|
+
}
|
|
16333
|
+
const module = String(from[1]);
|
|
16334
|
+
return options.patterns.find(
|
|
16335
|
+
(pattern) => pattern.module === module && pattern.functions.includes(callee.text)
|
|
16336
|
+
);
|
|
16337
|
+
}
|
|
16338
|
+
function statementIn(call) {
|
|
16339
|
+
const args = field(call, "arguments");
|
|
16340
|
+
const first = args?.namedChildren.find((child) => child !== null) ?? null;
|
|
16341
|
+
if (first === null || first.type !== "string") {
|
|
16342
|
+
return null;
|
|
16343
|
+
}
|
|
16344
|
+
const parts = [""];
|
|
16345
|
+
for (const child of first.namedChildren) {
|
|
16346
|
+
if (child === null) {
|
|
16347
|
+
continue;
|
|
16348
|
+
}
|
|
16349
|
+
if (child.type === "string_content") {
|
|
16350
|
+
parts[parts.length - 1] += child.text;
|
|
16351
|
+
continue;
|
|
16352
|
+
}
|
|
16353
|
+
if (child.type === "interpolation") {
|
|
16354
|
+
parts.push("");
|
|
16355
|
+
}
|
|
16356
|
+
}
|
|
16357
|
+
const statement = sqlFromParts(parts);
|
|
16358
|
+
return statement.trim() === "" ? null : statement;
|
|
16359
|
+
}
|
|
16360
|
+
|
|
16269
16361
|
// src/facts/resolve.ts
|
|
16270
16362
|
import { deriveOnDemand, evaluate } from "@suss/datalog";
|
|
16271
16363
|
|
|
@@ -17116,11 +17208,11 @@ function effectFor(pattern, chain, options) {
|
|
|
17116
17208
|
];
|
|
17117
17209
|
return {
|
|
17118
17210
|
type: "interaction",
|
|
17119
|
-
binding:
|
|
17211
|
+
binding: storageBinding({
|
|
17120
17212
|
recognition: "python-storage",
|
|
17121
17213
|
storageSystem: pattern.storageSystem,
|
|
17122
17214
|
scope: pattern.module,
|
|
17123
|
-
|
|
17215
|
+
container: chain.subject
|
|
17124
17216
|
}),
|
|
17125
17217
|
callee: chain.last.text,
|
|
17126
17218
|
interaction: {
|
|
@@ -17863,10 +17955,17 @@ function buildRouteUnit(options) {
|
|
|
17863
17955
|
() => readReturnAnnotation(definitionNode, enclosingScope, ctx)
|
|
17864
17956
|
);
|
|
17865
17957
|
const effects = invocationEffects(definitionNode);
|
|
17866
|
-
const storage = storageLookup === void 0 ? [] :
|
|
17867
|
-
...
|
|
17868
|
-
|
|
17869
|
-
|
|
17958
|
+
const storage = storageLookup === void 0 ? [] : [
|
|
17959
|
+
...storageEffects(bodyCalls(definitionNode), {
|
|
17960
|
+
...storageLookup,
|
|
17961
|
+
filePath: storageLookup.factsPath
|
|
17962
|
+
}),
|
|
17963
|
+
...rawSqlEffects(bodyCalls(definitionNode), {
|
|
17964
|
+
facts: storageLookup.facts,
|
|
17965
|
+
filePath: storageLookup.factsPath,
|
|
17966
|
+
patterns: storageLookup.rawSql ?? []
|
|
17967
|
+
})
|
|
17968
|
+
];
|
|
17870
17969
|
const perReturn = branchesFromReturns(
|
|
17871
17970
|
readsReturnedStatus,
|
|
17872
17971
|
definitionNode,
|
|
@@ -19222,6 +19321,7 @@ async function extractPythonProject(options) {
|
|
|
19222
19321
|
(pack) => pack.discovery.some((pattern) => pattern.routerComposition !== void 0)
|
|
19223
19322
|
);
|
|
19224
19323
|
const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
|
|
19324
|
+
const rawSqlPatterns = options.packs.flatMap((pack) => pack.rawSql ?? []);
|
|
19225
19325
|
const needsValues = mountsRouters || storagePatterns.length > 0;
|
|
19226
19326
|
const definitions = /* @__PURE__ */ new Map();
|
|
19227
19327
|
for (const { file: file2, root, module: moduleBinding } of bound) {
|
|
@@ -19251,14 +19351,15 @@ async function extractPythonProject(options) {
|
|
|
19251
19351
|
absoluteFile: file2,
|
|
19252
19352
|
routerIndex,
|
|
19253
19353
|
gapHandling,
|
|
19254
|
-
...storagePatterns.length > 0 ? {
|
|
19354
|
+
...storagePatterns.length > 0 || rawSqlPatterns.length > 0 ? {
|
|
19255
19355
|
storage: {
|
|
19256
19356
|
facts: db,
|
|
19257
19357
|
factsPath: file2,
|
|
19258
19358
|
patterns: storagePatterns,
|
|
19259
19359
|
definitionAt: (key) => definitions.get(key),
|
|
19260
19360
|
couldMatch,
|
|
19261
|
-
leadsToStorage
|
|
19361
|
+
leadsToStorage,
|
|
19362
|
+
rawSql: rawSqlPatterns
|
|
19262
19363
|
}
|
|
19263
19364
|
} : {}
|
|
19264
19365
|
});
|
|
@@ -19269,8 +19370,31 @@ async function extractPythonProject(options) {
|
|
|
19269
19370
|
emitEntryFact(db, file2, raw.identity.range, raw.identity.name);
|
|
19270
19371
|
}
|
|
19271
19372
|
}
|
|
19373
|
+
stampModuleImports(summaries, db, displayPathOf);
|
|
19272
19374
|
return { summaries, facts: db };
|
|
19273
19375
|
}
|
|
19376
|
+
function stampModuleImports(summaries, db, displayPathOf) {
|
|
19377
|
+
const importsByFile = /* @__PURE__ */ new Map();
|
|
19378
|
+
for (const [from, , to] of db.facts("pyImportResolved")) {
|
|
19379
|
+
if (typeof from !== "string" || typeof to !== "string") {
|
|
19380
|
+
continue;
|
|
19381
|
+
}
|
|
19382
|
+
const key = displayPathOf(from);
|
|
19383
|
+
const seen = importsByFile.get(key) ?? /* @__PURE__ */ new Set();
|
|
19384
|
+
seen.add(displayPathOf(to));
|
|
19385
|
+
importsByFile.set(key, seen);
|
|
19386
|
+
}
|
|
19387
|
+
for (const summary of summaries) {
|
|
19388
|
+
const imports = importsByFile.get(summary.location.file);
|
|
19389
|
+
if (imports === void 0 || imports.size === 0) {
|
|
19390
|
+
continue;
|
|
19391
|
+
}
|
|
19392
|
+
summary.metadata = {
|
|
19393
|
+
...summary.metadata ?? {},
|
|
19394
|
+
moduleImports: [...imports].sort()
|
|
19395
|
+
};
|
|
19396
|
+
}
|
|
19397
|
+
}
|
|
19274
19398
|
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
19275
19399
|
"__pycache__",
|
|
19276
19400
|
".venv",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/adapter-python",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Python language adapter for suss — parses source with tree-sitter (WASM), resolves names with a lexical binder, and emits raw extractions for the suss pipeline.",
|
|
6
6
|
"keywords": [
|
|
@@ -47,9 +47,10 @@
|
|
|
47
47
|
"typecheck": "tsc --noEmit"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@suss/behavioral-ir": "0.
|
|
51
|
-
"@suss/datalog": "0.
|
|
52
|
-
"@suss/extractor": "0.
|
|
50
|
+
"@suss/behavioral-ir": "0.7.0",
|
|
51
|
+
"@suss/datalog": "0.7.0",
|
|
52
|
+
"@suss/extractor": "0.7.0",
|
|
53
|
+
"@suss/sql": "0.7.0",
|
|
53
54
|
"web-tree-sitter": "^0.26.11"
|
|
54
55
|
},
|
|
55
56
|
"devDependencies": {
|