@suss/adapter-python 0.5.10 → 0.6.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 CHANGED
@@ -1739,7 +1739,7 @@ __export(core_exports2, {
1739
1739
  parse: () => parse,
1740
1740
  parseAsync: () => parseAsync,
1741
1741
  prettifyError: () => prettifyError,
1742
- process: () => process,
1742
+ process: () => process2,
1743
1743
  regexes: () => regexes_exports,
1744
1744
  registry: () => registry,
1745
1745
  safeDecode: () => safeDecode,
@@ -12664,7 +12664,7 @@ function initializeContext(params) {
12664
12664
  external: params?.external ?? void 0
12665
12665
  };
12666
12666
  }
12667
- function process(schema, ctx, _params = { path: [], schemaPath: [] }) {
12667
+ function process2(schema, ctx, _params = { path: [], schemaPath: [] }) {
12668
12668
  var _a3;
12669
12669
  const def = schema._zod.def;
12670
12670
  const seen = ctx.seen.get(schema);
@@ -12701,7 +12701,7 @@ function process(schema, ctx, _params = { path: [], schemaPath: [] }) {
12701
12701
  if (parent) {
12702
12702
  if (!result.ref)
12703
12703
  result.ref = parent;
12704
- process(parent, ctx, params);
12704
+ process2(parent, ctx, params);
12705
12705
  ctx.seen.get(parent).isParent = true;
12706
12706
  }
12707
12707
  }
@@ -12989,14 +12989,14 @@ function isTransforming(_schema, _ctx) {
12989
12989
  }
12990
12990
  var createToJSONSchemaMethod = (schema, processors = {}) => (params) => {
12991
12991
  const ctx = initializeContext({ ...params, processors });
12992
- process(schema, ctx);
12992
+ process2(schema, ctx);
12993
12993
  extractDefs(ctx, schema);
12994
12994
  return finalize(ctx, schema);
12995
12995
  };
12996
12996
  var createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) => {
12997
12997
  const { libraryOptions, target } = params ?? {};
12998
12998
  const ctx = initializeContext({ ...libraryOptions ?? {}, target, io, processors });
12999
- process(schema, ctx);
12999
+ process2(schema, ctx);
13000
13000
  extractDefs(ctx, schema);
13001
13001
  return finalize(ctx, schema);
13002
13002
  };
@@ -13242,7 +13242,7 @@ var arrayProcessor = (schema, ctx, _json, params) => {
13242
13242
  if (typeof maximum === "number")
13243
13243
  json2.maxItems = maximum;
13244
13244
  json2.type = "array";
13245
- json2.items = process(def.element, ctx, {
13245
+ json2.items = process2(def.element, ctx, {
13246
13246
  ...params,
13247
13247
  path: [...params.path, "items"]
13248
13248
  });
@@ -13254,7 +13254,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
13254
13254
  json2.properties = {};
13255
13255
  const shape = def.shape;
13256
13256
  for (const key in shape) {
13257
- json2.properties[key] = process(shape[key], ctx, {
13257
+ json2.properties[key] = process2(shape[key], ctx, {
13258
13258
  ...params,
13259
13259
  path: [...params.path, "properties", key]
13260
13260
  });
@@ -13277,7 +13277,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
13277
13277
  if (ctx.io === "output")
13278
13278
  json2.additionalProperties = false;
13279
13279
  } else if (def.catchall) {
13280
- json2.additionalProperties = process(def.catchall, ctx, {
13280
+ json2.additionalProperties = process2(def.catchall, ctx, {
13281
13281
  ...params,
13282
13282
  path: [...params.path, "additionalProperties"]
13283
13283
  });
@@ -13286,7 +13286,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
13286
13286
  var unionProcessor = (schema, ctx, json2, params) => {
13287
13287
  const def = schema._zod.def;
13288
13288
  const isExclusive = def.inclusive === false;
13289
- const options = def.options.map((x, i) => process(x, ctx, {
13289
+ const options = def.options.map((x, i) => process2(x, ctx, {
13290
13290
  ...params,
13291
13291
  path: [...params.path, isExclusive ? "oneOf" : "anyOf", i]
13292
13292
  }));
@@ -13298,11 +13298,11 @@ var unionProcessor = (schema, ctx, json2, params) => {
13298
13298
  };
13299
13299
  var intersectionProcessor = (schema, ctx, json2, params) => {
13300
13300
  const def = schema._zod.def;
13301
- const a = process(def.left, ctx, {
13301
+ const a = process2(def.left, ctx, {
13302
13302
  ...params,
13303
13303
  path: [...params.path, "allOf", 0]
13304
13304
  });
13305
- const b = process(def.right, ctx, {
13305
+ const b = process2(def.right, ctx, {
13306
13306
  ...params,
13307
13307
  path: [...params.path, "allOf", 1]
13308
13308
  });
@@ -13319,11 +13319,11 @@ var tupleProcessor = (schema, ctx, _json, params) => {
13319
13319
  json2.type = "array";
13320
13320
  const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
13321
13321
  const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
13322
- const prefixItems = def.items.map((x, i) => process(x, ctx, {
13322
+ const prefixItems = def.items.map((x, i) => process2(x, ctx, {
13323
13323
  ...params,
13324
13324
  path: [...params.path, prefixPath, i]
13325
13325
  }));
13326
- const rest = def.rest ? process(def.rest, ctx, {
13326
+ const rest = def.rest ? process2(def.rest, ctx, {
13327
13327
  ...params,
13328
13328
  path: [...params.path, restPath, ...ctx.target === "openapi-3.0" ? [def.items.length] : []]
13329
13329
  }) : null;
@@ -13363,7 +13363,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
13363
13363
  const keyBag = keyType._zod.bag;
13364
13364
  const patterns = keyBag?.patterns;
13365
13365
  if (def.mode === "loose" && patterns && patterns.size > 0) {
13366
- const valueSchema = process(def.valueType, ctx, {
13366
+ const valueSchema = process2(def.valueType, ctx, {
13367
13367
  ...params,
13368
13368
  path: [...params.path, "patternProperties", "*"]
13369
13369
  });
@@ -13373,12 +13373,12 @@ var recordProcessor = (schema, ctx, _json, params) => {
13373
13373
  }
13374
13374
  } else {
13375
13375
  if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") {
13376
- json2.propertyNames = process(def.keyType, ctx, {
13376
+ json2.propertyNames = process2(def.keyType, ctx, {
13377
13377
  ...params,
13378
13378
  path: [...params.path, "propertyNames"]
13379
13379
  });
13380
13380
  }
13381
- json2.additionalProperties = process(def.valueType, ctx, {
13381
+ json2.additionalProperties = process2(def.valueType, ctx, {
13382
13382
  ...params,
13383
13383
  path: [...params.path, "additionalProperties"]
13384
13384
  });
@@ -13393,7 +13393,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
13393
13393
  };
13394
13394
  var nullableProcessor = (schema, ctx, json2, params) => {
13395
13395
  const def = schema._zod.def;
13396
- const inner = process(def.innerType, ctx, params);
13396
+ const inner = process2(def.innerType, ctx, params);
13397
13397
  const seen = ctx.seen.get(schema);
13398
13398
  if (ctx.target === "openapi-3.0") {
13399
13399
  seen.ref = def.innerType;
@@ -13404,20 +13404,20 @@ var nullableProcessor = (schema, ctx, json2, params) => {
13404
13404
  };
13405
13405
  var nonoptionalProcessor = (schema, ctx, _json, params) => {
13406
13406
  const def = schema._zod.def;
13407
- process(def.innerType, ctx, params);
13407
+ process2(def.innerType, ctx, params);
13408
13408
  const seen = ctx.seen.get(schema);
13409
13409
  seen.ref = def.innerType;
13410
13410
  };
13411
13411
  var defaultProcessor = (schema, ctx, json2, params) => {
13412
13412
  const def = schema._zod.def;
13413
- process(def.innerType, ctx, params);
13413
+ process2(def.innerType, ctx, params);
13414
13414
  const seen = ctx.seen.get(schema);
13415
13415
  seen.ref = def.innerType;
13416
13416
  json2.default = JSON.parse(JSON.stringify(def.defaultValue));
13417
13417
  };
13418
13418
  var prefaultProcessor = (schema, ctx, json2, params) => {
13419
13419
  const def = schema._zod.def;
13420
- process(def.innerType, ctx, params);
13420
+ process2(def.innerType, ctx, params);
13421
13421
  const seen = ctx.seen.get(schema);
13422
13422
  seen.ref = def.innerType;
13423
13423
  if (ctx.io === "input")
@@ -13425,7 +13425,7 @@ var prefaultProcessor = (schema, ctx, json2, params) => {
13425
13425
  };
13426
13426
  var catchProcessor = (schema, ctx, json2, params) => {
13427
13427
  const def = schema._zod.def;
13428
- process(def.innerType, ctx, params);
13428
+ process2(def.innerType, ctx, params);
13429
13429
  const seen = ctx.seen.get(schema);
13430
13430
  seen.ref = def.innerType;
13431
13431
  let catchValue;
@@ -13440,32 +13440,32 @@ var pipeProcessor = (schema, ctx, _json, params) => {
13440
13440
  const def = schema._zod.def;
13441
13441
  const inIsTransform = def.in._zod.traits.has("$ZodTransform");
13442
13442
  const innerType = ctx.io === "input" ? inIsTransform ? def.out : def.in : def.out;
13443
- process(innerType, ctx, params);
13443
+ process2(innerType, ctx, params);
13444
13444
  const seen = ctx.seen.get(schema);
13445
13445
  seen.ref = innerType;
13446
13446
  };
13447
13447
  var readonlyProcessor = (schema, ctx, json2, params) => {
13448
13448
  const def = schema._zod.def;
13449
- process(def.innerType, ctx, params);
13449
+ process2(def.innerType, ctx, params);
13450
13450
  const seen = ctx.seen.get(schema);
13451
13451
  seen.ref = def.innerType;
13452
13452
  json2.readOnly = true;
13453
13453
  };
13454
13454
  var promiseProcessor = (schema, ctx, _json, params) => {
13455
13455
  const def = schema._zod.def;
13456
- process(def.innerType, ctx, params);
13456
+ process2(def.innerType, ctx, params);
13457
13457
  const seen = ctx.seen.get(schema);
13458
13458
  seen.ref = def.innerType;
13459
13459
  };
13460
13460
  var optionalProcessor = (schema, ctx, _json, params) => {
13461
13461
  const def = schema._zod.def;
13462
- process(def.innerType, ctx, params);
13462
+ process2(def.innerType, ctx, params);
13463
13463
  const seen = ctx.seen.get(schema);
13464
13464
  seen.ref = def.innerType;
13465
13465
  };
13466
13466
  var lazyProcessor = (schema, ctx, _json, params) => {
13467
13467
  const innerType = schema._zod.innerType;
13468
- process(innerType, ctx, params);
13468
+ process2(innerType, ctx, params);
13469
13469
  const seen = ctx.seen.get(schema);
13470
13470
  seen.ref = innerType;
13471
13471
  };
@@ -13517,7 +13517,7 @@ function toJSONSchema(input, params) {
13517
13517
  const defs = {};
13518
13518
  for (const entry of registry2._idmap.entries()) {
13519
13519
  const [_, schema] = entry;
13520
- process(schema, ctx2);
13520
+ process2(schema, ctx2);
13521
13521
  }
13522
13522
  const schemas = {};
13523
13523
  const external = {
@@ -13540,7 +13540,7 @@ function toJSONSchema(input, params) {
13540
13540
  return { schemas };
13541
13541
  }
13542
13542
  const ctx = initializeContext({ ...params, processors: allProcessors });
13543
- process(input, ctx);
13543
+ process2(input, ctx);
13544
13544
  extractDefs(ctx, input);
13545
13545
  return finalize(ctx, input);
13546
13546
  }
@@ -13598,7 +13598,7 @@ var JSONSchemaGenerator = class {
13598
13598
  * This must be called before emit().
13599
13599
  */
13600
13600
  process(schema, _params = { path: [], schemaPath: [] }) {
13601
- return process(schema, this.ctx, _params);
13601
+ return process2(schema, this.ctx, _params);
13602
13602
  }
13603
13603
  /**
13604
13604
  * Emit the final JSON Schema after processing.
@@ -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-WKR5SU4N.js
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 StorageRelationalSemanticsSchema = external_exports.object({
16120
- name: external_exports.literal("storage-relational"),
16121
- storageSystem: external_exports.enum(["postgres", "mysql", "sqlite"]),
16119
+ var StorageSemanticsSchema = external_exports.object({
16120
+ name: external_exports.literal("storage"),
16122
16121
  /**
16123
- * The ORM or driver scope. A single-database setup uses `"default"`,
16124
- * and a monorepo with several schemas gives each one its own value.
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 model name as the schema declares it, or null when
16129
- * the source states one this reader could not settle. A null table
16130
- * pairs with nothing, rather than with whatever its source text
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.
16132
16138
  */
16133
- table: external_exports.string().nullable()
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.
16146
+ */
16147
+ accessPath: external_exports.string().nullable()
16134
16148
  });
16135
- var storageRelationalSemantics = defineBoundarySemantics({
16136
- name: "storage-relational",
16137
- schema: StorageRelationalSemanticsSchema,
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
- storageRelationalSemantics.schema,
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
- storageRelationalSemantics,
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 storageRelationalBinding(opts) {
16314
+ function storageBinding(opts) {
16301
16315
  return {
16302
- transport: opts.storageSystem,
16316
+ transport: opts.transport ?? opts.storageSystem,
16303
16317
  semantics: {
16304
- name: "storage-relational",
16318
+ name: "storage",
16305
16319
  storageSystem: opts.storageSystem,
16306
16320
  scope: opts.scope,
16307
- table: namedOrNull(opts.table, "storage table")
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: storageRelationalBinding({
17255
+ binding: storageBinding({
17164
17256
  recognition: "python-storage",
17165
17257
  storageSystem: pattern.storageSystem,
17166
17258
  scope: pattern.module,
17167
- table: chain.subject
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 ? [] : storageEffects(bodyCalls(definitionNode), {
17911
- ...storageLookup,
17912
- filePath: storageLookup.factsPath
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,
@@ -19231,11 +19330,26 @@ function carrierPrefix(carrier, constructorArg, objectName, scope, scan) {
19231
19330
  }
19232
19331
 
19233
19332
  // src/project.ts
19333
+ function reportUnresolvedProjectModules(options) {
19334
+ for (const pack of options.packs) {
19335
+ for (const module2 of pack.projectModules ?? []) {
19336
+ const resolved = resolveAbsoluteModule(module2, { roots: options.roots });
19337
+ if (resolved.status === "resolved") {
19338
+ continue;
19339
+ }
19340
+ process.stderr.write(
19341
+ `[suss] ${pack.name}: the configured module ${module2} does not resolve under ${options.roots.join(", ")}, so nothing it wraps will be discovered.
19342
+ `
19343
+ );
19344
+ }
19345
+ }
19346
+ }
19234
19347
  async function extractPythonProject(options) {
19235
19348
  const db = new import_datalog3.Database();
19236
19349
  const summaries = [];
19237
19350
  const gapHandling = options.gapHandling ?? "permissive";
19238
19351
  const displayPathOf = (file2) => options.workspaceRoot !== void 0 ? import_node_path3.default.relative(options.workspaceRoot, file2) : file2;
19352
+ reportUnresolvedProjectModules(options);
19239
19353
  const bound = [];
19240
19354
  for (const file2 of options.files) {
19241
19355
  const source = import_node_fs3.default.readFileSync(file2, "utf8");
@@ -19251,6 +19365,7 @@ async function extractPythonProject(options) {
19251
19365
  (pack) => pack.discovery.some((pattern) => pattern.routerComposition !== void 0)
19252
19366
  );
19253
19367
  const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
19368
+ const rawSqlPatterns = options.packs.flatMap((pack) => pack.rawSql ?? []);
19254
19369
  const needsValues = mountsRouters || storagePatterns.length > 0;
19255
19370
  const definitions = /* @__PURE__ */ new Map();
19256
19371
  for (const { file: file2, root, module: moduleBinding } of bound) {
@@ -19280,14 +19395,15 @@ async function extractPythonProject(options) {
19280
19395
  absoluteFile: file2,
19281
19396
  routerIndex,
19282
19397
  gapHandling,
19283
- ...storagePatterns.length > 0 ? {
19398
+ ...storagePatterns.length > 0 || rawSqlPatterns.length > 0 ? {
19284
19399
  storage: {
19285
19400
  facts: db,
19286
19401
  factsPath: file2,
19287
19402
  patterns: storagePatterns,
19288
19403
  definitionAt: (key) => definitions.get(key),
19289
19404
  couldMatch,
19290
- leadsToStorage
19405
+ leadsToStorage,
19406
+ rawSql: rawSqlPatterns
19291
19407
  }
19292
19408
  } : {}
19293
19409
  });
@@ -19298,8 +19414,31 @@ async function extractPythonProject(options) {
19298
19414
  emitEntryFact(db, file2, raw.identity.range, raw.identity.name);
19299
19415
  }
19300
19416
  }
19417
+ stampModuleImports(summaries, db, displayPathOf);
19301
19418
  return { summaries, facts: db };
19302
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
+ }
19303
19442
  var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([
19304
19443
  "__pycache__",
19305
19444
  ".venv",
package/dist/index.d.cts CHANGED
@@ -178,8 +178,19 @@ interface PythonPack {
178
178
  /** Wire protocol for the produced boundary bindings, e.g. "http". */
179
179
  protocol: string;
180
180
  discovery: PythonDiscoveryPattern[];
181
+ /**
182
+ * Modules the project itself supplies, the wrappers a person names
183
+ * when configuring the pack. The library's own module is not one of
184
+ * these: it lives outside the project and would never resolve. The
185
+ * adapter checks each of these against the project's roots, because
186
+ * a wrapper that resolves to nothing does not match any decorator
187
+ * and never says why (#188).
188
+ */
189
+ projectModules?: string[];
181
190
  /** What the library's own database queries look like. The README says how one is matched. */
182
191
  storage?: StoragePattern[];
192
+ /** How the library lets a project hand the database SQL it wrote itself. */
193
+ rawSql?: RawSqlPattern[];
183
194
  }
184
195
  /**
185
196
  * A call talks to the database when the method behind it says it returns one
@@ -203,6 +214,20 @@ interface StoragePattern {
203
214
  /** Which database the library is talking to, for the boundary binding. */
204
215
  storageSystem: "postgres" | "mysql" | "sqlite";
205
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
+ }
206
231
  type PythonDiscoveryPattern = DecoratedClassRoute | DecoratedFunctionRoute;
207
232
  /** Conventions both kinds of route share. Each one describes what the library does, never a project's choice. */
208
233
  interface RouteConventions {
@@ -361,6 +386,8 @@ interface StorageLookup {
361
386
  readonly definitionAt: (key: string) => Node | undefined;
362
387
  readonly couldMatch: ReadonlySet<string>;
363
388
  readonly leadsToStorage: ReadonlySet<string>;
389
+ /** What a pack says about statements a project writes as SQL itself. */
390
+ readonly rawSql?: readonly RawSqlPattern[];
364
391
  }
365
392
 
366
393
  /**
@@ -542,4 +569,4 @@ declare function extractPythonProject(options: ExtractPythonOptions): Promise<Ex
542
569
  /** Every `.py` file under `root`, depth-first, skipping the usual non-source directories. */
543
570
  declare function findPythonFiles(root: string): string[];
544
571
 
545
- 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
@@ -178,8 +178,19 @@ interface PythonPack {
178
178
  /** Wire protocol for the produced boundary bindings, e.g. "http". */
179
179
  protocol: string;
180
180
  discovery: PythonDiscoveryPattern[];
181
+ /**
182
+ * Modules the project itself supplies, the wrappers a person names
183
+ * when configuring the pack. The library's own module is not one of
184
+ * these: it lives outside the project and would never resolve. The
185
+ * adapter checks each of these against the project's roots, because
186
+ * a wrapper that resolves to nothing does not match any decorator
187
+ * and never says why (#188).
188
+ */
189
+ projectModules?: string[];
181
190
  /** What the library's own database queries look like. The README says how one is matched. */
182
191
  storage?: StoragePattern[];
192
+ /** How the library lets a project hand the database SQL it wrote itself. */
193
+ rawSql?: RawSqlPattern[];
183
194
  }
184
195
  /**
185
196
  * A call talks to the database when the method behind it says it returns one
@@ -203,6 +214,20 @@ interface StoragePattern {
203
214
  /** Which database the library is talking to, for the boundary binding. */
204
215
  storageSystem: "postgres" | "mysql" | "sqlite";
205
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
+ }
206
231
  type PythonDiscoveryPattern = DecoratedClassRoute | DecoratedFunctionRoute;
207
232
  /** Conventions both kinds of route share. Each one describes what the library does, never a project's choice. */
208
233
  interface RouteConventions {
@@ -361,6 +386,8 @@ interface StorageLookup {
361
386
  readonly definitionAt: (key: string) => Node | undefined;
362
387
  readonly couldMatch: ReadonlySet<string>;
363
388
  readonly leadsToStorage: ReadonlySet<string>;
389
+ /** What a pack says about statements a project writes as SQL itself. */
390
+ readonly rawSql?: readonly RawSqlPattern[];
364
391
  }
365
392
 
366
393
  /**
@@ -542,4 +569,4 @@ declare function extractPythonProject(options: ExtractPythonOptions): Promise<Ex
542
569
  /** Every `.py` file under `root`, depth-first, skipping the usual non-source directories. */
543
570
  declare function findPythonFiles(root: string): string[];
544
571
 
545
- 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
@@ -1695,7 +1695,7 @@ __export(core_exports2, {
1695
1695
  parse: () => parse,
1696
1696
  parseAsync: () => parseAsync,
1697
1697
  prettifyError: () => prettifyError,
1698
- process: () => process,
1698
+ process: () => process2,
1699
1699
  regexes: () => regexes_exports,
1700
1700
  registry: () => registry,
1701
1701
  safeDecode: () => safeDecode,
@@ -12620,7 +12620,7 @@ function initializeContext(params) {
12620
12620
  external: params?.external ?? void 0
12621
12621
  };
12622
12622
  }
12623
- function process(schema, ctx, _params = { path: [], schemaPath: [] }) {
12623
+ function process2(schema, ctx, _params = { path: [], schemaPath: [] }) {
12624
12624
  var _a3;
12625
12625
  const def = schema._zod.def;
12626
12626
  const seen = ctx.seen.get(schema);
@@ -12657,7 +12657,7 @@ function process(schema, ctx, _params = { path: [], schemaPath: [] }) {
12657
12657
  if (parent) {
12658
12658
  if (!result.ref)
12659
12659
  result.ref = parent;
12660
- process(parent, ctx, params);
12660
+ process2(parent, ctx, params);
12661
12661
  ctx.seen.get(parent).isParent = true;
12662
12662
  }
12663
12663
  }
@@ -12945,14 +12945,14 @@ function isTransforming(_schema, _ctx) {
12945
12945
  }
12946
12946
  var createToJSONSchemaMethod = (schema, processors = {}) => (params) => {
12947
12947
  const ctx = initializeContext({ ...params, processors });
12948
- process(schema, ctx);
12948
+ process2(schema, ctx);
12949
12949
  extractDefs(ctx, schema);
12950
12950
  return finalize(ctx, schema);
12951
12951
  };
12952
12952
  var createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) => {
12953
12953
  const { libraryOptions, target } = params ?? {};
12954
12954
  const ctx = initializeContext({ ...libraryOptions ?? {}, target, io, processors });
12955
- process(schema, ctx);
12955
+ process2(schema, ctx);
12956
12956
  extractDefs(ctx, schema);
12957
12957
  return finalize(ctx, schema);
12958
12958
  };
@@ -13198,7 +13198,7 @@ var arrayProcessor = (schema, ctx, _json, params) => {
13198
13198
  if (typeof maximum === "number")
13199
13199
  json2.maxItems = maximum;
13200
13200
  json2.type = "array";
13201
- json2.items = process(def.element, ctx, {
13201
+ json2.items = process2(def.element, ctx, {
13202
13202
  ...params,
13203
13203
  path: [...params.path, "items"]
13204
13204
  });
@@ -13210,7 +13210,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
13210
13210
  json2.properties = {};
13211
13211
  const shape = def.shape;
13212
13212
  for (const key in shape) {
13213
- json2.properties[key] = process(shape[key], ctx, {
13213
+ json2.properties[key] = process2(shape[key], ctx, {
13214
13214
  ...params,
13215
13215
  path: [...params.path, "properties", key]
13216
13216
  });
@@ -13233,7 +13233,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
13233
13233
  if (ctx.io === "output")
13234
13234
  json2.additionalProperties = false;
13235
13235
  } else if (def.catchall) {
13236
- json2.additionalProperties = process(def.catchall, ctx, {
13236
+ json2.additionalProperties = process2(def.catchall, ctx, {
13237
13237
  ...params,
13238
13238
  path: [...params.path, "additionalProperties"]
13239
13239
  });
@@ -13242,7 +13242,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
13242
13242
  var unionProcessor = (schema, ctx, json2, params) => {
13243
13243
  const def = schema._zod.def;
13244
13244
  const isExclusive = def.inclusive === false;
13245
- const options = def.options.map((x, i) => process(x, ctx, {
13245
+ const options = def.options.map((x, i) => process2(x, ctx, {
13246
13246
  ...params,
13247
13247
  path: [...params.path, isExclusive ? "oneOf" : "anyOf", i]
13248
13248
  }));
@@ -13254,11 +13254,11 @@ var unionProcessor = (schema, ctx, json2, params) => {
13254
13254
  };
13255
13255
  var intersectionProcessor = (schema, ctx, json2, params) => {
13256
13256
  const def = schema._zod.def;
13257
- const a = process(def.left, ctx, {
13257
+ const a = process2(def.left, ctx, {
13258
13258
  ...params,
13259
13259
  path: [...params.path, "allOf", 0]
13260
13260
  });
13261
- const b = process(def.right, ctx, {
13261
+ const b = process2(def.right, ctx, {
13262
13262
  ...params,
13263
13263
  path: [...params.path, "allOf", 1]
13264
13264
  });
@@ -13275,11 +13275,11 @@ var tupleProcessor = (schema, ctx, _json, params) => {
13275
13275
  json2.type = "array";
13276
13276
  const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
13277
13277
  const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
13278
- const prefixItems = def.items.map((x, i) => process(x, ctx, {
13278
+ const prefixItems = def.items.map((x, i) => process2(x, ctx, {
13279
13279
  ...params,
13280
13280
  path: [...params.path, prefixPath, i]
13281
13281
  }));
13282
- const rest = def.rest ? process(def.rest, ctx, {
13282
+ const rest = def.rest ? process2(def.rest, ctx, {
13283
13283
  ...params,
13284
13284
  path: [...params.path, restPath, ...ctx.target === "openapi-3.0" ? [def.items.length] : []]
13285
13285
  }) : null;
@@ -13319,7 +13319,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
13319
13319
  const keyBag = keyType._zod.bag;
13320
13320
  const patterns = keyBag?.patterns;
13321
13321
  if (def.mode === "loose" && patterns && patterns.size > 0) {
13322
- const valueSchema = process(def.valueType, ctx, {
13322
+ const valueSchema = process2(def.valueType, ctx, {
13323
13323
  ...params,
13324
13324
  path: [...params.path, "patternProperties", "*"]
13325
13325
  });
@@ -13329,12 +13329,12 @@ var recordProcessor = (schema, ctx, _json, params) => {
13329
13329
  }
13330
13330
  } else {
13331
13331
  if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") {
13332
- json2.propertyNames = process(def.keyType, ctx, {
13332
+ json2.propertyNames = process2(def.keyType, ctx, {
13333
13333
  ...params,
13334
13334
  path: [...params.path, "propertyNames"]
13335
13335
  });
13336
13336
  }
13337
- json2.additionalProperties = process(def.valueType, ctx, {
13337
+ json2.additionalProperties = process2(def.valueType, ctx, {
13338
13338
  ...params,
13339
13339
  path: [...params.path, "additionalProperties"]
13340
13340
  });
@@ -13349,7 +13349,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
13349
13349
  };
13350
13350
  var nullableProcessor = (schema, ctx, json2, params) => {
13351
13351
  const def = schema._zod.def;
13352
- const inner = process(def.innerType, ctx, params);
13352
+ const inner = process2(def.innerType, ctx, params);
13353
13353
  const seen = ctx.seen.get(schema);
13354
13354
  if (ctx.target === "openapi-3.0") {
13355
13355
  seen.ref = def.innerType;
@@ -13360,20 +13360,20 @@ var nullableProcessor = (schema, ctx, json2, params) => {
13360
13360
  };
13361
13361
  var nonoptionalProcessor = (schema, ctx, _json, params) => {
13362
13362
  const def = schema._zod.def;
13363
- process(def.innerType, ctx, params);
13363
+ process2(def.innerType, ctx, params);
13364
13364
  const seen = ctx.seen.get(schema);
13365
13365
  seen.ref = def.innerType;
13366
13366
  };
13367
13367
  var defaultProcessor = (schema, ctx, json2, params) => {
13368
13368
  const def = schema._zod.def;
13369
- process(def.innerType, ctx, params);
13369
+ process2(def.innerType, ctx, params);
13370
13370
  const seen = ctx.seen.get(schema);
13371
13371
  seen.ref = def.innerType;
13372
13372
  json2.default = JSON.parse(JSON.stringify(def.defaultValue));
13373
13373
  };
13374
13374
  var prefaultProcessor = (schema, ctx, json2, params) => {
13375
13375
  const def = schema._zod.def;
13376
- process(def.innerType, ctx, params);
13376
+ process2(def.innerType, ctx, params);
13377
13377
  const seen = ctx.seen.get(schema);
13378
13378
  seen.ref = def.innerType;
13379
13379
  if (ctx.io === "input")
@@ -13381,7 +13381,7 @@ var prefaultProcessor = (schema, ctx, json2, params) => {
13381
13381
  };
13382
13382
  var catchProcessor = (schema, ctx, json2, params) => {
13383
13383
  const def = schema._zod.def;
13384
- process(def.innerType, ctx, params);
13384
+ process2(def.innerType, ctx, params);
13385
13385
  const seen = ctx.seen.get(schema);
13386
13386
  seen.ref = def.innerType;
13387
13387
  let catchValue;
@@ -13396,32 +13396,32 @@ var pipeProcessor = (schema, ctx, _json, params) => {
13396
13396
  const def = schema._zod.def;
13397
13397
  const inIsTransform = def.in._zod.traits.has("$ZodTransform");
13398
13398
  const innerType = ctx.io === "input" ? inIsTransform ? def.out : def.in : def.out;
13399
- process(innerType, ctx, params);
13399
+ process2(innerType, ctx, params);
13400
13400
  const seen = ctx.seen.get(schema);
13401
13401
  seen.ref = innerType;
13402
13402
  };
13403
13403
  var readonlyProcessor = (schema, ctx, json2, params) => {
13404
13404
  const def = schema._zod.def;
13405
- process(def.innerType, ctx, params);
13405
+ process2(def.innerType, ctx, params);
13406
13406
  const seen = ctx.seen.get(schema);
13407
13407
  seen.ref = def.innerType;
13408
13408
  json2.readOnly = true;
13409
13409
  };
13410
13410
  var promiseProcessor = (schema, ctx, _json, params) => {
13411
13411
  const def = schema._zod.def;
13412
- process(def.innerType, ctx, params);
13412
+ process2(def.innerType, ctx, params);
13413
13413
  const seen = ctx.seen.get(schema);
13414
13414
  seen.ref = def.innerType;
13415
13415
  };
13416
13416
  var optionalProcessor = (schema, ctx, _json, params) => {
13417
13417
  const def = schema._zod.def;
13418
- process(def.innerType, ctx, params);
13418
+ process2(def.innerType, ctx, params);
13419
13419
  const seen = ctx.seen.get(schema);
13420
13420
  seen.ref = def.innerType;
13421
13421
  };
13422
13422
  var lazyProcessor = (schema, ctx, _json, params) => {
13423
13423
  const innerType = schema._zod.innerType;
13424
- process(innerType, ctx, params);
13424
+ process2(innerType, ctx, params);
13425
13425
  const seen = ctx.seen.get(schema);
13426
13426
  seen.ref = innerType;
13427
13427
  };
@@ -13473,7 +13473,7 @@ function toJSONSchema(input, params) {
13473
13473
  const defs = {};
13474
13474
  for (const entry of registry2._idmap.entries()) {
13475
13475
  const [_, schema] = entry;
13476
- process(schema, ctx2);
13476
+ process2(schema, ctx2);
13477
13477
  }
13478
13478
  const schemas = {};
13479
13479
  const external = {
@@ -13496,7 +13496,7 @@ function toJSONSchema(input, params) {
13496
13496
  return { schemas };
13497
13497
  }
13498
13498
  const ctx = initializeContext({ ...params, processors: allProcessors });
13499
- process(input, ctx);
13499
+ process2(input, ctx);
13500
13500
  extractDefs(ctx, input);
13501
13501
  return finalize(ctx, input);
13502
13502
  }
@@ -13554,7 +13554,7 @@ var JSONSchemaGenerator = class {
13554
13554
  * This must be called before emit().
13555
13555
  */
13556
13556
  process(schema, _params = { path: [], schemaPath: [] }) {
13557
- return process(schema, this.ctx, _params);
13557
+ return process2(schema, this.ctx, _params);
13558
13558
  }
13559
13559
  /**
13560
13560
  * Emit the final JSON Schema after processing.
@@ -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-WKR5SU4N.js
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 StorageRelationalSemanticsSchema = external_exports.object({
16076
- name: external_exports.literal("storage-relational"),
16077
- storageSystem: external_exports.enum(["postgres", "mysql", "sqlite"]),
16075
+ var StorageSemanticsSchema = external_exports.object({
16076
+ name: external_exports.literal("storage"),
16078
16077
  /**
16079
- * The ORM or driver scope. A single-database setup uses `"default"`,
16080
- * and a monorepo with several schemas gives each one its own value.
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 model name as the schema declares it, or null when
16085
- * the source states one this reader could not settle. A null table
16086
- * pairs with nothing, rather than with whatever its source text
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.
16088
16094
  */
16089
- table: external_exports.string().nullable()
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.
16102
+ */
16103
+ accessPath: external_exports.string().nullable()
16090
16104
  });
16091
- var storageRelationalSemantics = defineBoundarySemantics({
16092
- name: "storage-relational",
16093
- schema: StorageRelationalSemanticsSchema,
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
- storageRelationalSemantics.schema,
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
- storageRelationalSemantics,
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 storageRelationalBinding(opts) {
16270
+ function storageBinding(opts) {
16257
16271
  return {
16258
- transport: opts.storageSystem,
16272
+ transport: opts.transport ?? opts.storageSystem,
16259
16273
  semantics: {
16260
- name: "storage-relational",
16274
+ name: "storage",
16261
16275
  storageSystem: opts.storageSystem,
16262
16276
  scope: opts.scope,
16263
- table: namedOrNull(opts.table, "storage table")
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: storageRelationalBinding({
17211
+ binding: storageBinding({
17120
17212
  recognition: "python-storage",
17121
17213
  storageSystem: pattern.storageSystem,
17122
17214
  scope: pattern.module,
17123
- table: chain.subject
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 ? [] : storageEffects(bodyCalls(definitionNode), {
17867
- ...storageLookup,
17868
- filePath: storageLookup.factsPath
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,
@@ -19187,11 +19286,26 @@ function carrierPrefix(carrier, constructorArg, objectName, scope, scan) {
19187
19286
  }
19188
19287
 
19189
19288
  // src/project.ts
19289
+ function reportUnresolvedProjectModules(options) {
19290
+ for (const pack of options.packs) {
19291
+ for (const module of pack.projectModules ?? []) {
19292
+ const resolved = resolveAbsoluteModule(module, { roots: options.roots });
19293
+ if (resolved.status === "resolved") {
19294
+ continue;
19295
+ }
19296
+ process.stderr.write(
19297
+ `[suss] ${pack.name}: the configured module ${module} does not resolve under ${options.roots.join(", ")}, so nothing it wraps will be discovered.
19298
+ `
19299
+ );
19300
+ }
19301
+ }
19302
+ }
19190
19303
  async function extractPythonProject(options) {
19191
19304
  const db = new Database();
19192
19305
  const summaries = [];
19193
19306
  const gapHandling = options.gapHandling ?? "permissive";
19194
19307
  const displayPathOf = (file2) => options.workspaceRoot !== void 0 ? path3.relative(options.workspaceRoot, file2) : file2;
19308
+ reportUnresolvedProjectModules(options);
19195
19309
  const bound = [];
19196
19310
  for (const file2 of options.files) {
19197
19311
  const source = fs3.readFileSync(file2, "utf8");
@@ -19207,6 +19321,7 @@ async function extractPythonProject(options) {
19207
19321
  (pack) => pack.discovery.some((pattern) => pattern.routerComposition !== void 0)
19208
19322
  );
19209
19323
  const storagePatterns = options.packs.flatMap((pack) => pack.storage ?? []);
19324
+ const rawSqlPatterns = options.packs.flatMap((pack) => pack.rawSql ?? []);
19210
19325
  const needsValues = mountsRouters || storagePatterns.length > 0;
19211
19326
  const definitions = /* @__PURE__ */ new Map();
19212
19327
  for (const { file: file2, root, module: moduleBinding } of bound) {
@@ -19236,14 +19351,15 @@ async function extractPythonProject(options) {
19236
19351
  absoluteFile: file2,
19237
19352
  routerIndex,
19238
19353
  gapHandling,
19239
- ...storagePatterns.length > 0 ? {
19354
+ ...storagePatterns.length > 0 || rawSqlPatterns.length > 0 ? {
19240
19355
  storage: {
19241
19356
  facts: db,
19242
19357
  factsPath: file2,
19243
19358
  patterns: storagePatterns,
19244
19359
  definitionAt: (key) => definitions.get(key),
19245
19360
  couldMatch,
19246
- leadsToStorage
19361
+ leadsToStorage,
19362
+ rawSql: rawSqlPatterns
19247
19363
  }
19248
19364
  } : {}
19249
19365
  });
@@ -19254,8 +19370,31 @@ async function extractPythonProject(options) {
19254
19370
  emitEntryFact(db, file2, raw.identity.range, raw.identity.name);
19255
19371
  }
19256
19372
  }
19373
+ stampModuleImports(summaries, db, displayPathOf);
19257
19374
  return { summaries, facts: db };
19258
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
+ }
19259
19398
  var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([
19260
19399
  "__pycache__",
19261
19400
  ".venv",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/adapter-python",
3
- "version": "0.5.10",
3
+ "version": "0.6.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.5.10",
51
- "@suss/datalog": "0.5.10",
52
- "@suss/extractor": "0.5.10",
50
+ "@suss/behavioral-ir": "0.6.0",
51
+ "@suss/datalog": "0.6.0",
52
+ "@suss/extractor": "0.6.0",
53
+ "@suss/sql": "0.6.0",
53
54
  "web-tree-sitter": "^0.26.11"
54
55
  },
55
56
  "devDependencies": {