@suss/recognize 0.11.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/LICENSE +205 -0
- package/README.md +131 -0
- package/dist/ast.cjs +37 -0
- package/dist/ast.d.cts +32 -0
- package/dist/ast.d.ts +32 -0
- package/dist/ast.js +12 -0
- package/dist/chain-DTSij2Mn.d.cts +197 -0
- package/dist/chain-DTSij2Mn.d.ts +197 -0
- package/dist/index.cjs +266 -0
- package/dist/index.d.cts +127 -0
- package/dist/index.d.ts +127 -0
- package/dist/index.js +232 -0
- package/package.json +63 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The operations a chain performs at one call site, and what a language
|
|
3
|
+
* adapter has to be able to work out for it.
|
|
4
|
+
*
|
|
5
|
+
* A chain is data, so something has to turn "did this module declare
|
|
6
|
+
* the method" into a yes or no about a particular program. That is the
|
|
7
|
+
* adapter's job, and this interface is the whole of what a chain asks
|
|
8
|
+
* for. An adapter implements it once and every declared pack runs
|
|
9
|
+
* there, which is what lets one pack drive several languages.
|
|
10
|
+
*
|
|
11
|
+
* Nothing here mentions a syntax tree. A link that needs the tree goes
|
|
12
|
+
* through `@suss/recognize/ast`, a separate import so that reaching for
|
|
13
|
+
* it shows up in a diff and in the pack health report.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* How a pack pins down the receiver a call is on.
|
|
17
|
+
*
|
|
18
|
+
* The migration plan for #542 lists four more: `factoryMade` (`app =
|
|
19
|
+
* express()`), `imported`, `anchored` (a chain read from its anchor
|
|
20
|
+
* call), `inherits` (a Ruby or Python receiver matched by ancestry) and
|
|
21
|
+
* `global` (`process.env`, bare `fetch`). Each arrives as a member here
|
|
22
|
+
* with an entry in every adapter's dispatch table. The README beside
|
|
23
|
+
* this file says which starts are not receiver-shaped at all.
|
|
24
|
+
*/
|
|
25
|
+
type ReceiverOrigin = DeclaredBy | ConstructedFrom;
|
|
26
|
+
/**
|
|
27
|
+
* A receiver whose method one of these modules declared.
|
|
28
|
+
*
|
|
29
|
+
* This is the origin for a client the source never spells out. `const
|
|
30
|
+
* redis = await this.getClient()` says nothing about ioredis, and the
|
|
31
|
+
* declaration behind `redis.get` says everything.
|
|
32
|
+
*/
|
|
33
|
+
interface DeclaredBy {
|
|
34
|
+
readonly origin: "declaredBy";
|
|
35
|
+
/** The modules whose declarations settle the call. */
|
|
36
|
+
readonly importedFrom: readonly string[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A client made from a module's export, however the program caches it.
|
|
40
|
+
* Asked of the receiver itself, so it still works where the method is untyped
|
|
41
|
+
* and `declaredBy` finds nothing to read.
|
|
42
|
+
*/
|
|
43
|
+
interface ConstructedFrom {
|
|
44
|
+
readonly origin: "constructed";
|
|
45
|
+
/** The modules whose export the client was made from. */
|
|
46
|
+
readonly importedFrom: readonly string[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A receiver whose method one of these modules declared. Pass every
|
|
50
|
+
* module that ships the same client, the way three packages all speak
|
|
51
|
+
* one wire protocol.
|
|
52
|
+
*/
|
|
53
|
+
declare function declaredBy(...importedFrom: readonly string[]): DeclaredBy;
|
|
54
|
+
/** A client made from one of these modules' exports. */
|
|
55
|
+
declare function constructedFrom(...importedFrom: readonly string[]): ConstructedFrom;
|
|
56
|
+
/**
|
|
57
|
+
* What a reader gives back for a name nothing in the source settles.
|
|
58
|
+
* `"nothing"` gives null, `"reference"` gives the value to go and ask
|
|
59
|
+
* about instead.
|
|
60
|
+
*/
|
|
61
|
+
type UnsettledName = "nothing" | "reference";
|
|
62
|
+
/**
|
|
63
|
+
* One call site, as the questions a chain asks about it.
|
|
64
|
+
*
|
|
65
|
+
* An adapter builds one of these per call and hands it to the
|
|
66
|
+
* recognizer through its context. Every member is about the call in
|
|
67
|
+
* hand, so there is no node to pass around and no place for a pack to
|
|
68
|
+
* reach past what it declared.
|
|
69
|
+
*/
|
|
70
|
+
interface CallOps {
|
|
71
|
+
/** Which method the call reaches for, spelled as the source spells it. */
|
|
72
|
+
method(): string | null;
|
|
73
|
+
/** Whether the receiver came from where the origin says. */
|
|
74
|
+
receiverIsFrom(origin: ReceiverOrigin): boolean;
|
|
75
|
+
/** How many arguments the call passes. */
|
|
76
|
+
argumentCount(): number;
|
|
77
|
+
/** The name the argument in this position gives. */
|
|
78
|
+
nameAt(index: number, unsettled: UnsettledName): string | null;
|
|
79
|
+
/** The callee, as the source writes it. */
|
|
80
|
+
calleeText(): string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The property an adapter puts its ops on, in the context it hands a
|
|
84
|
+
* recognizer. A context without it belongs to an adapter that has not
|
|
85
|
+
* implemented the ops, and a declared pack matches nothing there.
|
|
86
|
+
*/
|
|
87
|
+
interface OpsCarrier {
|
|
88
|
+
ops?: CallOps;
|
|
89
|
+
}
|
|
90
|
+
/** The ops in a recognizer context, or null when the adapter has none. */
|
|
91
|
+
declare function opsIn(ctx: unknown): CallOps | null;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The spine every pack shares, as a chain of links.
|
|
95
|
+
*
|
|
96
|
+
* A pack does four jobs, and all four start the same way: match the
|
|
97
|
+
* receiver, match the method, read the arguments, then yield. What
|
|
98
|
+
* differs is the ending. Recognition yields effects, discovery yields a
|
|
99
|
+
* unit, a terminal yields a response write, and a claimed callback
|
|
100
|
+
* yields a sub-unit. Only the effects ending is built; the others are
|
|
101
|
+
* further members of `Ending` with an entry in the compile table.
|
|
102
|
+
*
|
|
103
|
+
* A link's answer is data wherever it can be. A link given a function
|
|
104
|
+
* instead is code, that link alone, and pack health says which ones.
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
/** A link whose answer a pack wrote as code rather than as data. */
|
|
108
|
+
interface LinkFunction<A extends unknown[], R> {
|
|
109
|
+
(...args: A): R;
|
|
110
|
+
/** Set when the function was built through `@suss/recognize/ast`. */
|
|
111
|
+
readonly reachesAst?: boolean;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* What a match starts from.
|
|
115
|
+
*
|
|
116
|
+
* Most of what the shipped packs match starts from a receiver, and none
|
|
117
|
+
* of what discovery matches does: an exported name, a file path, a
|
|
118
|
+
* decorator, a parameter's type, a template file beside the module and
|
|
119
|
+
* a function's return type each start somewhere else. So the start is
|
|
120
|
+
* its own axis, and a receiver origin is one value on it.
|
|
121
|
+
*/
|
|
122
|
+
type MatchStart = FromReceiver;
|
|
123
|
+
/** A match that starts from the receiver a call is on. */
|
|
124
|
+
interface FromReceiver {
|
|
125
|
+
readonly starts: "receiver";
|
|
126
|
+
readonly origin: ReceiverOrigin;
|
|
127
|
+
}
|
|
128
|
+
/** Where the match begins. */
|
|
129
|
+
interface StartLink {
|
|
130
|
+
readonly asks: "start";
|
|
131
|
+
readonly at: MatchStart;
|
|
132
|
+
}
|
|
133
|
+
/** Which methods count, and what each one does. */
|
|
134
|
+
interface MethodsLink<TMeaning> {
|
|
135
|
+
readonly asks: "methods";
|
|
136
|
+
readonly table: Readonly<Record<string, TMeaning>>;
|
|
137
|
+
/**
|
|
138
|
+
* Whether two spellings of one method name are the same method. One
|
|
139
|
+
* client library lower-cases where another camel-cases, and both are
|
|
140
|
+
* sending the same command.
|
|
141
|
+
*/
|
|
142
|
+
readonly ignoringCase: boolean;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Which container a call's selector belongs to.
|
|
146
|
+
*
|
|
147
|
+
* The call in hand comes second so that a rule written over the
|
|
148
|
+
* selector alone, which is most of them, ignores it. A rule that has to
|
|
149
|
+
* read the syntax tree goes through `astLink`, which is what puts the
|
|
150
|
+
* call to use.
|
|
151
|
+
*/
|
|
152
|
+
interface ContainerLink {
|
|
153
|
+
readonly asks: "container";
|
|
154
|
+
readonly from: LinkFunction<[readonly string[], CallOps], string | null>;
|
|
155
|
+
}
|
|
156
|
+
/** One question in a chain, and the answer the pack gave for it. */
|
|
157
|
+
type Link<TMeaning> = StartLink | MethodsLink<TMeaning> | ContainerLink;
|
|
158
|
+
/** Which argument or arguments say what a call reached. */
|
|
159
|
+
type ArgumentPick = {
|
|
160
|
+
readonly at: number;
|
|
161
|
+
} | {
|
|
162
|
+
readonly from: number;
|
|
163
|
+
};
|
|
164
|
+
/** What one method of a storage client does. */
|
|
165
|
+
interface StorageMethod {
|
|
166
|
+
readonly kind: "read" | "write";
|
|
167
|
+
/** Which argument or arguments say what the call reached. */
|
|
168
|
+
readonly selector?: ArgumentPick;
|
|
169
|
+
/** Which argument says which field inside it, when the method takes one. */
|
|
170
|
+
readonly fields?: ArgumentPick;
|
|
171
|
+
}
|
|
172
|
+
/** What a chain produces when every link matches. */
|
|
173
|
+
type Ending = StorageEnding;
|
|
174
|
+
/** A storage access: one call, one thing it read or wrote. */
|
|
175
|
+
interface StorageEnding {
|
|
176
|
+
readonly yields: "storageAccess";
|
|
177
|
+
/** The store, in the words OpenTelemetry's semantic conventions use. */
|
|
178
|
+
readonly system: string;
|
|
179
|
+
/** The wire, when it differs from the store's own name. */
|
|
180
|
+
readonly transport?: string;
|
|
181
|
+
/** Which of the store's namespaces the call reached. */
|
|
182
|
+
readonly scope: string;
|
|
183
|
+
/** What a reader gives back for a name nothing in the source settles. */
|
|
184
|
+
readonly unsettledName: "nothing" | "reference";
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* A pack's declaration for one kind of call: the links, the ending, and
|
|
188
|
+
* a line of code it matches.
|
|
189
|
+
*/
|
|
190
|
+
interface Chain<TMeaning = StorageMethod> {
|
|
191
|
+
readonly links: readonly Link<TMeaning>[];
|
|
192
|
+
readonly ending: Ending;
|
|
193
|
+
/** A line of code this matches, which the pack's tests run. */
|
|
194
|
+
readonly example: string | null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export { type ArgumentPick as A, type CallOps as C, type DeclaredBy as D, type Ending as E, type FromReceiver as F, type LinkFunction as L, type MatchStart as M, type OpsCarrier as O, type ReceiverOrigin as R, type StorageMethod as S, type UnsettledName as U, type Chain as a, type ConstructedFrom as b, type ContainerLink as c, type Link as d, type MethodsLink as e, type StartLink as f, type StorageEnding as g, constructedFrom as h, declaredBy as i, opsIn as o };
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
compile: () => compile,
|
|
24
|
+
constructedFrom: () => constructedFrom,
|
|
25
|
+
declaredBy: () => declaredBy,
|
|
26
|
+
examplesMissing: () => examplesMissing,
|
|
27
|
+
opsIn: () => opsIn,
|
|
28
|
+
pack: () => pack,
|
|
29
|
+
runExamples: () => runExamples,
|
|
30
|
+
storageCalls: () => storageCalls
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(index_exports);
|
|
33
|
+
|
|
34
|
+
// src/compile.ts
|
|
35
|
+
var import_behavioral_ir = require("@suss/behavioral-ir");
|
|
36
|
+
|
|
37
|
+
// src/ops.ts
|
|
38
|
+
function declaredBy(...importedFrom) {
|
|
39
|
+
return { origin: "declaredBy", importedFrom };
|
|
40
|
+
}
|
|
41
|
+
function constructedFrom(...importedFrom) {
|
|
42
|
+
return { origin: "constructed", importedFrom };
|
|
43
|
+
}
|
|
44
|
+
function opsIn(ctx) {
|
|
45
|
+
const carrier = ctx;
|
|
46
|
+
return carrier?.ops ?? null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/compile.ts
|
|
50
|
+
function compile(chain, recognition) {
|
|
51
|
+
return (_call, ctx) => {
|
|
52
|
+
const ops = opsIn(ctx);
|
|
53
|
+
if (ops === null) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const method = ops.method();
|
|
57
|
+
if (method === null) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const meaning = meaningOf(chain, method);
|
|
61
|
+
if (meaning === null || !startsHere(chain, ops)) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return YIELD[chain.ending.yields]({
|
|
65
|
+
ops,
|
|
66
|
+
method,
|
|
67
|
+
meaning,
|
|
68
|
+
chain,
|
|
69
|
+
recognition
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function meaningOf(chain, method) {
|
|
74
|
+
const link = chain.links.find(
|
|
75
|
+
(candidate) => candidate.asks === "methods"
|
|
76
|
+
);
|
|
77
|
+
if (link === void 0) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
if (!link.ignoringCase) {
|
|
81
|
+
return link.table[method] ?? null;
|
|
82
|
+
}
|
|
83
|
+
return link.table[method.toLowerCase()] ?? null;
|
|
84
|
+
}
|
|
85
|
+
var START = {
|
|
86
|
+
receiver: (start, ops) => ops.receiverIsFrom(start.origin)
|
|
87
|
+
};
|
|
88
|
+
function startsHere(chain, ops) {
|
|
89
|
+
return chain.links.every(
|
|
90
|
+
(link) => link.asks === "start" ? START[link.at.starts](link.at, ops) : true
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
var YIELD = {
|
|
94
|
+
storageAccess
|
|
95
|
+
};
|
|
96
|
+
function storageAccess(matched) {
|
|
97
|
+
const { ops, method, meaning, chain, recognition } = matched;
|
|
98
|
+
const ending = chain.ending;
|
|
99
|
+
const selector = namesAt(ops, meaning.selector, ending.unsettledName);
|
|
100
|
+
const fields = namesAt(ops, meaning.fields, ending.unsettledName);
|
|
101
|
+
const container = containerOf(chain, selector, ops);
|
|
102
|
+
return [
|
|
103
|
+
{
|
|
104
|
+
type: "interaction",
|
|
105
|
+
binding: (0, import_behavioral_ir.storageBinding)({
|
|
106
|
+
recognition,
|
|
107
|
+
storageSystem: ending.system,
|
|
108
|
+
...ending.transport === void 0 ? {} : { transport: ending.transport },
|
|
109
|
+
scope: ending.scope,
|
|
110
|
+
container,
|
|
111
|
+
accessPath: null
|
|
112
|
+
}),
|
|
113
|
+
callee: ops.calleeText(),
|
|
114
|
+
interaction: {
|
|
115
|
+
class: "storage-access",
|
|
116
|
+
kind: meaning.kind,
|
|
117
|
+
fields,
|
|
118
|
+
operation: method,
|
|
119
|
+
...selector.length > 0 ? { selector } : {}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
];
|
|
123
|
+
}
|
|
124
|
+
function containerOf(chain, selector, ops) {
|
|
125
|
+
const link = chain.links.find(
|
|
126
|
+
(candidate) => candidate.asks === "container"
|
|
127
|
+
);
|
|
128
|
+
if (link === void 0) {
|
|
129
|
+
return selector[0] ?? null;
|
|
130
|
+
}
|
|
131
|
+
return link.from(selector, ops);
|
|
132
|
+
}
|
|
133
|
+
function namesAt(ops, pick, unsettled) {
|
|
134
|
+
if (pick === void 0) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
const last = "at" in pick ? pick.at + 1 : ops.argumentCount();
|
|
138
|
+
const first = "at" in pick ? pick.at : pick.from;
|
|
139
|
+
const names = [];
|
|
140
|
+
for (let index = first; index < last; index += 1) {
|
|
141
|
+
const name = ops.nameAt(index, unsettled);
|
|
142
|
+
if (name !== null) {
|
|
143
|
+
names.push(name);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return names;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/example.ts
|
|
150
|
+
function runExamples(pack2, run) {
|
|
151
|
+
const declared = pack2.declarations?.declarations ?? [];
|
|
152
|
+
const ran = [];
|
|
153
|
+
for (const declaration of declared) {
|
|
154
|
+
if (declaration.example === null) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
ran.push({
|
|
158
|
+
match: declaration.name,
|
|
159
|
+
example: declaration.example,
|
|
160
|
+
effects: run(declaration.example)
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return ran;
|
|
164
|
+
}
|
|
165
|
+
function examplesMissing(pack2) {
|
|
166
|
+
return (pack2.declarations?.declarations ?? []).filter((declaration) => declaration.example === null).map((declaration) => declaration.name);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/pack.ts
|
|
170
|
+
function pack(name, matches, spec) {
|
|
171
|
+
const chains = matches.map((match) => match.declared);
|
|
172
|
+
const recognizers = chains.map(
|
|
173
|
+
(chain) => compile(chain, spec.recognizedAs)
|
|
174
|
+
);
|
|
175
|
+
return {
|
|
176
|
+
name,
|
|
177
|
+
languages: spec.languages,
|
|
178
|
+
...spec.version === void 0 ? {} : { version: spec.version },
|
|
179
|
+
protocol: protocolOf(chains),
|
|
180
|
+
discovery: [],
|
|
181
|
+
terminals: [],
|
|
182
|
+
inputMapping: { type: "positionalParams", params: [] },
|
|
183
|
+
requiresImport: gateOf(chains),
|
|
184
|
+
invocationRecognizers: recognizers,
|
|
185
|
+
declarations: { declarations: chains.map(describe) }
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function protocolOf(chains) {
|
|
189
|
+
const wires = new Set(
|
|
190
|
+
chains.map((chain) => chain.ending.transport ?? chain.ending.system)
|
|
191
|
+
);
|
|
192
|
+
const [only] = [...wires];
|
|
193
|
+
if (wires.size !== 1 || only === void 0) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`a pack reaches ${wires.size} wires (${[...wires].join(", ")}); split it into one pack per wire`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
return only;
|
|
199
|
+
}
|
|
200
|
+
function gateOf(chains) {
|
|
201
|
+
const modules = /* @__PURE__ */ new Set();
|
|
202
|
+
for (const chain of chains) {
|
|
203
|
+
for (const link of chain.links) {
|
|
204
|
+
if (link.asks !== "start" || link.at.starts !== "receiver") {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
for (const module2 of link.at.origin.importedFrom) {
|
|
208
|
+
modules.add(module2);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return [...modules];
|
|
213
|
+
}
|
|
214
|
+
function describe(chain) {
|
|
215
|
+
const functions = chain.links.filter(isFunctionLink);
|
|
216
|
+
return {
|
|
217
|
+
name: chain.ending.system,
|
|
218
|
+
dataLinks: chain.links.length - functions.length,
|
|
219
|
+
functionLinks: functions.map((link) => link.asks),
|
|
220
|
+
astLinks: functions.filter((link) => link.from.reachesAst === true).map((link) => link.asks),
|
|
221
|
+
example: chain.example
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function isFunctionLink(link) {
|
|
225
|
+
return link.asks === "container";
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/storage.ts
|
|
229
|
+
function storageCalls(spec) {
|
|
230
|
+
const ending = {
|
|
231
|
+
yields: "storageAccess",
|
|
232
|
+
system: spec.system,
|
|
233
|
+
...spec.transport === void 0 ? {} : { transport: spec.transport },
|
|
234
|
+
scope: spec.scope ?? "default",
|
|
235
|
+
unsettledName: spec.unsettledName ?? "reference"
|
|
236
|
+
};
|
|
237
|
+
return chainFrom({
|
|
238
|
+
links: [{ asks: "start", at: { starts: "receiver", origin: spec.client } }],
|
|
239
|
+
ending,
|
|
240
|
+
example: null
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
function chainFrom(declared) {
|
|
244
|
+
const adding = (link) => chainFrom({ ...declared, links: [...declared.links, link] });
|
|
245
|
+
return {
|
|
246
|
+
declared,
|
|
247
|
+
methods: (table, options) => adding({
|
|
248
|
+
asks: "methods",
|
|
249
|
+
table,
|
|
250
|
+
ignoringCase: options?.ignoringCase ?? false
|
|
251
|
+
}),
|
|
252
|
+
container: (from) => adding({ asks: "container", from }),
|
|
253
|
+
example: (code) => chainFrom({ ...declared, example: code })
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
257
|
+
0 && (module.exports = {
|
|
258
|
+
compile,
|
|
259
|
+
constructedFrom,
|
|
260
|
+
declaredBy,
|
|
261
|
+
examplesMissing,
|
|
262
|
+
opsIn,
|
|
263
|
+
pack,
|
|
264
|
+
runExamples,
|
|
265
|
+
storageCalls
|
|
266
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { InvocationRecognizer, PatternPack } from '@suss/extractor';
|
|
2
|
+
export { DeclaredMatch, PackDeclarations, PatternPack } from '@suss/extractor';
|
|
3
|
+
import { a as Chain, S as StorageMethod, L as LinkFunction, C as CallOps, R as ReceiverOrigin } from './chain-DTSij2Mn.cjs';
|
|
4
|
+
export { A as ArgumentPick, b as ConstructedFrom, c as ContainerLink, D as DeclaredBy, E as Ending, F as FromReceiver, d as Link, M as MatchStart, e as MethodsLink, O as OpsCarrier, f as StartLink, g as StorageEnding, U as UnsettledName, h as constructedFrom, i as declaredBy, o as opsIn } from './chain-DTSij2Mn.cjs';
|
|
5
|
+
import { Effect } from '@suss/behavioral-ir';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Turning a chain into the recognizer hook the adapters already call.
|
|
9
|
+
*
|
|
10
|
+
* The links are guards, so the compiled hook checks the cheap one
|
|
11
|
+
* first: looking a method up in a table costs nothing, while following
|
|
12
|
+
* a receiver to the library that made it walks declarations. The chain
|
|
13
|
+
* says which questions to ask, not the order to ask them in. A link
|
|
14
|
+
* that changes what the receiver is, the way Prisma's model property
|
|
15
|
+
* does, will need that order back, and the note here is where to start.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** A chain, as the hook the adapter dispatches to on every call. */
|
|
19
|
+
declare function compile(chain: Chain<StorageMethod>, recognition: string): InvocationRecognizer;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Running the example that comes with every declaration.
|
|
23
|
+
*
|
|
24
|
+
* A pack's documentation goes stale the moment the library moves, and
|
|
25
|
+
* nothing catches it. An example that the pack's own tests compile and
|
|
26
|
+
* run against the declaration cannot: the day it stops matching, the
|
|
27
|
+
* pack fails rather than the docs quietly lying.
|
|
28
|
+
*
|
|
29
|
+
* The run itself needs a compiler, which this package does not have, so
|
|
30
|
+
* the caller passes one. A pack's test builds a small project with the
|
|
31
|
+
* client library on disk and hands back what the recognizers emitted.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/** What a pack's example produced, and what the pack said it would. */
|
|
35
|
+
interface RanExample {
|
|
36
|
+
/** What the declaration matches, in the pack's own words. */
|
|
37
|
+
match: string;
|
|
38
|
+
/** The line of code that came with the declaration. */
|
|
39
|
+
example: string;
|
|
40
|
+
effects: Effect[];
|
|
41
|
+
}
|
|
42
|
+
/** How a pack's test compiles one line and runs the pack over it. */
|
|
43
|
+
type RunOverCode = (code: string) => Effect[];
|
|
44
|
+
/**
|
|
45
|
+
* Every example a pack declares, run. A declaration with no example is
|
|
46
|
+
* left out, which is what the pack health check catches separately.
|
|
47
|
+
*/
|
|
48
|
+
declare function runExamples(pack: PatternPack, run: RunOverCode): RanExample[];
|
|
49
|
+
/** The declarations a pack shipped without a line of code to run. */
|
|
50
|
+
declare function examplesMissing(pack: PatternPack): string[];
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The entry point for a pack that recognizes storage calls, and the
|
|
54
|
+
* chain it builds.
|
|
55
|
+
*
|
|
56
|
+
* The entry point says what comes out rather than how, so a pack author
|
|
57
|
+
* writes `storageCalls` and never meets an `Effect`. Each method on the
|
|
58
|
+
* returned value adds one link and gives back a new chain, so a chain
|
|
59
|
+
* is finished data by the time `pack` receives it.
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
/** What a pack says about the store its calls reach. */
|
|
63
|
+
interface StorageCallsSpec {
|
|
64
|
+
/** The store, in the words OpenTelemetry's semantic conventions use. */
|
|
65
|
+
system: string;
|
|
66
|
+
/** The wire, when it differs from the store's own name. */
|
|
67
|
+
transport?: string;
|
|
68
|
+
/** Which of the store's namespaces the calls reach. Defaults to "default". */
|
|
69
|
+
scope?: string;
|
|
70
|
+
/** How the pack pins down the client its calls are on. */
|
|
71
|
+
client: ReceiverOrigin;
|
|
72
|
+
/**
|
|
73
|
+
* What a reader gives back for a name nothing settles. Defaults to
|
|
74
|
+
* "reference", which says which value to go and ask about.
|
|
75
|
+
*/
|
|
76
|
+
unsettledName?: "nothing" | "reference";
|
|
77
|
+
}
|
|
78
|
+
/** A storage chain, and the links that can still be added to it. */
|
|
79
|
+
interface StorageCalls {
|
|
80
|
+
/** Which methods count, and what each one reads or writes. */
|
|
81
|
+
methods(table: Readonly<Record<string, StorageMethod>>, options?: {
|
|
82
|
+
ignoringCase?: boolean;
|
|
83
|
+
}): StorageCalls;
|
|
84
|
+
/**
|
|
85
|
+
* Which container a call's selector belongs to. Without this the
|
|
86
|
+
* first name a call reached is the container.
|
|
87
|
+
*/
|
|
88
|
+
container(from: LinkFunction<[readonly string[], CallOps], string | null>): StorageCalls;
|
|
89
|
+
/** A line of code this matches, which the pack's tests run. */
|
|
90
|
+
example(code: string): StorageCalls;
|
|
91
|
+
/** The links and the ending, as data. */
|
|
92
|
+
readonly declared: Chain<StorageMethod>;
|
|
93
|
+
}
|
|
94
|
+
/** A pack that recognizes calls against a store. */
|
|
95
|
+
declare function storageCalls(spec: StorageCallsSpec): StorageCalls;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Assembling declared chains into the pack the adapters load.
|
|
99
|
+
*
|
|
100
|
+
* Everything a `PatternPack` needs beyond the chains is either already
|
|
101
|
+
* in them or the same for every declared pack: the modules the client
|
|
102
|
+
* came from are the import gate, the store's wire is the protocol, and
|
|
103
|
+
* a pack of recognizers discovers nothing. What is left is the pack's
|
|
104
|
+
* own name, the languages it runs on, and the name its effects are
|
|
105
|
+
* recognized under, which is the npm package callers install.
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
/** What a pack says about itself, beyond the calls it matches. */
|
|
109
|
+
interface PackSpec {
|
|
110
|
+
/** The languages the pack runs on, as the adapters name them. */
|
|
111
|
+
languages: string[];
|
|
112
|
+
/**
|
|
113
|
+
* The name effects from this pack are recorded under, which is the
|
|
114
|
+
* npm package a user installs.
|
|
115
|
+
*/
|
|
116
|
+
recognizedAs: string;
|
|
117
|
+
/**
|
|
118
|
+
* What the pack calls this build of itself. The extraction cache keys
|
|
119
|
+
* on it, so a pack that never stamps one serves an earlier build's
|
|
120
|
+
* results after an edit.
|
|
121
|
+
*/
|
|
122
|
+
version?: string;
|
|
123
|
+
}
|
|
124
|
+
/** A pack, assembled from the calls it says it matches. */
|
|
125
|
+
declare function pack(name: string, matches: readonly StorageCalls[], spec: PackSpec): PatternPack;
|
|
126
|
+
|
|
127
|
+
export { CallOps, Chain, LinkFunction, type PackSpec, type RanExample, ReceiverOrigin, type RunOverCode, type StorageCalls, type StorageCallsSpec, StorageMethod, compile, examplesMissing, pack, runExamples, storageCalls };
|