@suss/packs 0.28.0 → 0.30.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/activerecord.js +108 -2
- package/dist/{chunk-SKGM3QL5.js → chunk-NYWZHQBJ.js} +48 -0
- package/dist/rails.js +67 -10
- package/dist/sqlalchemy.js +1 -1
- package/dist/sqlmodel.js +18 -1
- package/package.json +10 -10
package/dist/activerecord.js
CHANGED
|
@@ -8,6 +8,96 @@ var optionsSchema = z.object({
|
|
|
8
8
|
*/
|
|
9
9
|
storageSystem: storageSystemOption
|
|
10
10
|
}).strict();
|
|
11
|
+
var RETURNS_A_RECORD = [
|
|
12
|
+
"find",
|
|
13
|
+
"find_by",
|
|
14
|
+
"find_by!",
|
|
15
|
+
"find_or_create_by",
|
|
16
|
+
"find_or_create_by!",
|
|
17
|
+
"find_or_initialize_by",
|
|
18
|
+
"first",
|
|
19
|
+
"first!",
|
|
20
|
+
"last",
|
|
21
|
+
"last!",
|
|
22
|
+
"take",
|
|
23
|
+
"take!",
|
|
24
|
+
"sole",
|
|
25
|
+
"find_sole_by",
|
|
26
|
+
"new",
|
|
27
|
+
"build",
|
|
28
|
+
"create",
|
|
29
|
+
"create!",
|
|
30
|
+
"reload"
|
|
31
|
+
];
|
|
32
|
+
var RUNS_A_QUERY = [
|
|
33
|
+
"find",
|
|
34
|
+
"find_by",
|
|
35
|
+
"find_by!",
|
|
36
|
+
"find_sole_by",
|
|
37
|
+
"sole",
|
|
38
|
+
"first",
|
|
39
|
+
"first!",
|
|
40
|
+
"last",
|
|
41
|
+
"last!",
|
|
42
|
+
"take",
|
|
43
|
+
"take!",
|
|
44
|
+
"exists?",
|
|
45
|
+
"count",
|
|
46
|
+
"size",
|
|
47
|
+
"any?",
|
|
48
|
+
"none?",
|
|
49
|
+
"many?",
|
|
50
|
+
"empty?",
|
|
51
|
+
"pluck",
|
|
52
|
+
"pick",
|
|
53
|
+
"ids",
|
|
54
|
+
"sum",
|
|
55
|
+
"average",
|
|
56
|
+
"minimum",
|
|
57
|
+
"maximum",
|
|
58
|
+
"calculate",
|
|
59
|
+
"to_a",
|
|
60
|
+
"each",
|
|
61
|
+
"map",
|
|
62
|
+
"find_each",
|
|
63
|
+
"find_in_batches",
|
|
64
|
+
"in_batches",
|
|
65
|
+
"load",
|
|
66
|
+
"reload",
|
|
67
|
+
"find_or_initialize_by"
|
|
68
|
+
];
|
|
69
|
+
var RETURNS_A_RELATION = [
|
|
70
|
+
"where",
|
|
71
|
+
"rewhere",
|
|
72
|
+
"or",
|
|
73
|
+
"order",
|
|
74
|
+
"reorder",
|
|
75
|
+
"limit",
|
|
76
|
+
"offset",
|
|
77
|
+
"includes",
|
|
78
|
+
"preload",
|
|
79
|
+
"eager_load",
|
|
80
|
+
"joins",
|
|
81
|
+
"left_joins",
|
|
82
|
+
"left_outer_joins",
|
|
83
|
+
"distinct",
|
|
84
|
+
"select",
|
|
85
|
+
"group",
|
|
86
|
+
"having",
|
|
87
|
+
"merge",
|
|
88
|
+
"readonly",
|
|
89
|
+
"lock",
|
|
90
|
+
"none",
|
|
91
|
+
"all",
|
|
92
|
+
"unscoped",
|
|
93
|
+
"unscope",
|
|
94
|
+
"extending",
|
|
95
|
+
"from",
|
|
96
|
+
"references",
|
|
97
|
+
"only",
|
|
98
|
+
"except"
|
|
99
|
+
];
|
|
100
|
+
var READS = [...RUNS_A_QUERY, ...RETURNS_A_RELATION, "not"];
|
|
11
101
|
function activeRecordStorage(options) {
|
|
12
102
|
return [
|
|
13
103
|
{
|
|
@@ -29,8 +119,24 @@ function activeRecordStorage(options) {
|
|
|
29
119
|
"destroy_all",
|
|
30
120
|
"delete",
|
|
31
121
|
"delete_all",
|
|
32
|
-
"touch"
|
|
122
|
+
"touch",
|
|
123
|
+
// These two read first and store only when the read found
|
|
124
|
+
// nothing, and a write is the stronger of the two claims.
|
|
125
|
+
"find_or_create_by",
|
|
126
|
+
"find_or_create_by!"
|
|
33
127
|
],
|
|
128
|
+
reads: READS,
|
|
129
|
+
givesBack: [...RETURNS_A_RECORD, ...RETURNS_A_RELATION],
|
|
130
|
+
byPrimaryKey: {
|
|
131
|
+
methods: ["find", "exists?", "update", "destroy", "delete"],
|
|
132
|
+
column: "id"
|
|
133
|
+
},
|
|
134
|
+
columnArguments: ["select", "pluck", "pick"],
|
|
135
|
+
associations: {
|
|
136
|
+
singular: ["has_one", "belongs_to"],
|
|
137
|
+
plural: ["has_many", "has_and_belongs_to_many"],
|
|
138
|
+
classNameKeyword: "class_name"
|
|
139
|
+
},
|
|
34
140
|
storageSystem: options.storageSystem
|
|
35
141
|
}
|
|
36
142
|
];
|
|
@@ -55,7 +161,7 @@ var declares = {
|
|
|
55
161
|
{ ecosystem: "rubygems", name: "activerecord" },
|
|
56
162
|
{ ecosystem: "rubygems", name: "rails" }
|
|
57
163
|
],
|
|
58
|
-
reads: "ActiveRecord calls (Ruby): a call matches when the
|
|
164
|
+
reads: "ActiveRecord calls (Ruby): a call matches when its method is one ActiveRecord defines as a read or a write and the class behind its receiver reaches `ActiveRecord::Base`, following what each class extends through the project.",
|
|
59
165
|
configuration: {
|
|
60
166
|
file: "suss.activerecord.json",
|
|
61
167
|
example: { storageSystem: "postgresql" },
|
|
@@ -55,6 +55,52 @@ function sqlalchemyStorage(options) {
|
|
|
55
55
|
}
|
|
56
56
|
];
|
|
57
57
|
}
|
|
58
|
+
var MODEL_BASE_NAMES = [
|
|
59
|
+
"DeclarativeBase",
|
|
60
|
+
"declarative_base",
|
|
61
|
+
"DeclarativeBaseNoMeta"
|
|
62
|
+
];
|
|
63
|
+
var NARROWS_A_QUERY = [
|
|
64
|
+
"filter",
|
|
65
|
+
"filter_by",
|
|
66
|
+
"where",
|
|
67
|
+
"order_by",
|
|
68
|
+
"limit",
|
|
69
|
+
"offset",
|
|
70
|
+
"options",
|
|
71
|
+
"join",
|
|
72
|
+
"outerjoin",
|
|
73
|
+
"distinct",
|
|
74
|
+
"group_by",
|
|
75
|
+
"having"
|
|
76
|
+
];
|
|
77
|
+
var RUNS_A_QUERY = [
|
|
78
|
+
"first",
|
|
79
|
+
"one",
|
|
80
|
+
"one_or_none",
|
|
81
|
+
"scalar",
|
|
82
|
+
"scalar_one",
|
|
83
|
+
"scalar_one_or_none",
|
|
84
|
+
"scalars",
|
|
85
|
+
"get",
|
|
86
|
+
"all"
|
|
87
|
+
];
|
|
88
|
+
var SESSION_ENTRY_METHODS = [
|
|
89
|
+
{ method: "get", argument: 0 },
|
|
90
|
+
{ method: "query", argument: 0 },
|
|
91
|
+
{ method: "execute", argument: 0 }
|
|
92
|
+
];
|
|
93
|
+
function sqlalchemyModels() {
|
|
94
|
+
return [
|
|
95
|
+
{
|
|
96
|
+
baseNames: MODEL_BASE_NAMES,
|
|
97
|
+
givesBack: [...NARROWS_A_QUERY, ...RUNS_A_QUERY],
|
|
98
|
+
entryMethods: SESSION_ENTRY_METHODS,
|
|
99
|
+
entryFunctions: [{ module: "sqlalchemy", name: "select", argument: 0 }],
|
|
100
|
+
relationships: [{ module: "sqlalchemy.orm", name: "relationship" }]
|
|
101
|
+
}
|
|
102
|
+
];
|
|
103
|
+
}
|
|
58
104
|
function sqlalchemyRawSql(options) {
|
|
59
105
|
return [
|
|
60
106
|
{
|
|
@@ -75,6 +121,7 @@ function sqlalchemyFramework(options) {
|
|
|
75
121
|
protocol: options.storageSystem,
|
|
76
122
|
discovery: [],
|
|
77
123
|
storage: sqlalchemyStorage(options),
|
|
124
|
+
models: sqlalchemyModels(),
|
|
78
125
|
rawSql: sqlalchemyRawSql(options)
|
|
79
126
|
};
|
|
80
127
|
}
|
|
@@ -95,6 +142,7 @@ var index_default = sqlalchemyFramework;
|
|
|
95
142
|
export {
|
|
96
143
|
optionsSchema,
|
|
97
144
|
sqlalchemyStorage,
|
|
145
|
+
sqlalchemyModels,
|
|
98
146
|
sqlalchemyRawSql,
|
|
99
147
|
declares,
|
|
100
148
|
index_default
|
package/dist/rails.js
CHANGED
|
@@ -133,25 +133,66 @@ function isolatedNamespaceOf(classNode, acronyms) {
|
|
|
133
133
|
return "";
|
|
134
134
|
}
|
|
135
135
|
var ACRONYM_CALL = /\.acronym\s*\(?\s*(["'])([A-Za-z\d]+)\1/g;
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
var IRREGULAR_CALL = /\.irregular\s*\(?\s*(["'])([^"']*)\1\s*,\s*(["'])([^"']*)\3/g;
|
|
137
|
+
var UNCOUNTABLE_CALL = /\.uncountable\s*\(?\s*([^\n]*)/g;
|
|
138
|
+
var SINGULAR_CALL = /\.singular\s*\(?\s*(?:(["'])([^"']*)\1|\/([^/]*)\/([a-z]*))\s*,\s*(["'])([^"']*)\5/g;
|
|
139
|
+
var WORD_LIST = /%[wi][([{]([^)\]}]*)[)\]}]/;
|
|
140
|
+
var EVERY_LITERAL = /(["'])([^"']*)\1/g;
|
|
141
|
+
var ANY_CALL = /\.(acronym|irregular|uncountable|singular)[\s("'/]/;
|
|
142
|
+
function group(match, at) {
|
|
143
|
+
return match[at] ?? "";
|
|
144
|
+
}
|
|
145
|
+
function readInflections(configDirectory) {
|
|
146
|
+
const acronyms = /* @__PURE__ */ new Set();
|
|
147
|
+
const irregular = [];
|
|
148
|
+
const uncountable = /* @__PURE__ */ new Set();
|
|
149
|
+
const singular = [];
|
|
138
150
|
for (const file of inflectionFiles(configDirectory)) {
|
|
139
|
-
const source = fs2.readFileSync(file, "utf8");
|
|
151
|
+
const source = uncommented(fs2.readFileSync(file, "utf8"));
|
|
140
152
|
for (const match of source.matchAll(ACRONYM_CALL)) {
|
|
141
|
-
|
|
153
|
+
acronyms.add(group(match, 2));
|
|
154
|
+
}
|
|
155
|
+
for (const match of source.matchAll(IRREGULAR_CALL)) {
|
|
156
|
+
irregular.push([group(match, 4), group(match, 2)]);
|
|
157
|
+
}
|
|
158
|
+
for (const match of source.matchAll(UNCOUNTABLE_CALL)) {
|
|
159
|
+
for (const word of wordsIn(group(match, 1))) {
|
|
160
|
+
uncountable.add(word);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
for (const match of source.matchAll(SINGULAR_CALL)) {
|
|
164
|
+
singular.push([ruleOf(match), group(match, 6)]);
|
|
142
165
|
}
|
|
143
166
|
}
|
|
144
|
-
|
|
145
|
-
|
|
167
|
+
acronyms.delete("");
|
|
168
|
+
uncountable.delete("");
|
|
169
|
+
return {
|
|
170
|
+
acronyms: [...acronyms].sort(),
|
|
171
|
+
irregular,
|
|
172
|
+
uncountable: [...uncountable].sort(),
|
|
173
|
+
singular
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function ruleOf(match) {
|
|
177
|
+
const literal = match[2];
|
|
178
|
+
return literal === void 0 ? `/${group(match, 3)}/${group(match, 4)}` : literal;
|
|
179
|
+
}
|
|
180
|
+
function wordsIn(argument) {
|
|
181
|
+
const list = WORD_LIST.exec(argument);
|
|
182
|
+
if (list !== null) {
|
|
183
|
+
return group(list, 1).split(/\s+/).filter((word) => word.length > 0);
|
|
184
|
+
}
|
|
185
|
+
return [...argument.matchAll(EVERY_LITERAL)].map((match) => group(match, 2));
|
|
146
186
|
}
|
|
147
187
|
function inflectionFiles(configDirectory) {
|
|
148
188
|
const directory = path2.join(configDirectory, "config", "initializers");
|
|
149
189
|
if (!fs2.existsSync(directory)) {
|
|
150
190
|
return [];
|
|
151
191
|
}
|
|
152
|
-
return fs2.readdirSync(directory, { withFileTypes: true, recursive: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".rb")).map((entry) => path2.join(entry.parentPath, entry.name)).filter(
|
|
153
|
-
|
|
154
|
-
|
|
192
|
+
return fs2.readdirSync(directory, { withFileTypes: true, recursive: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".rb")).map((entry) => path2.join(entry.parentPath, entry.name)).filter((file) => ANY_CALL.test(uncommented(fs2.readFileSync(file, "utf8")))).sort();
|
|
193
|
+
}
|
|
194
|
+
function uncommented(source) {
|
|
195
|
+
return source.split("\n").filter((line) => !line.trimStart().startsWith("#")).join("\n");
|
|
155
196
|
}
|
|
156
197
|
function readSimpleArgs(call, defaults = {}) {
|
|
157
198
|
const argumentList = field2(call, "arguments");
|
|
@@ -926,6 +967,19 @@ var RAILS_CONTROLLER_METHODS = [
|
|
|
926
967
|
"controller_name",
|
|
927
968
|
"controller_path"
|
|
928
969
|
];
|
|
970
|
+
var BODY_BLOCKS = [
|
|
971
|
+
{ name: "included", moduleOnly: true },
|
|
972
|
+
{ name: "prepended", moduleOnly: true },
|
|
973
|
+
{ name: "class_methods", moduleOnly: true, definesClassMethods: true },
|
|
974
|
+
{ name: "concerning" },
|
|
975
|
+
{ name: "with_options" }
|
|
976
|
+
];
|
|
977
|
+
var EMPTY_INFLECTIONS = {
|
|
978
|
+
acronyms: [],
|
|
979
|
+
irregular: [],
|
|
980
|
+
uncountable: [],
|
|
981
|
+
singular: []
|
|
982
|
+
};
|
|
929
983
|
function controllerKeyFromQualified(qualifiedName, acronyms) {
|
|
930
984
|
const withoutSuffix = qualifiedName.endsWith("Controller") ? qualifiedName.slice(0, -"Controller".length) : qualifiedName;
|
|
931
985
|
return underscoreConstantPath2(withoutSuffix, acronyms);
|
|
@@ -956,7 +1010,8 @@ function railsFramework(options = {}) {
|
|
|
956
1010
|
const displayPathOf = (file) => options.configDirectory === void 0 ? file : path4.relative(options.configDirectory, file);
|
|
957
1011
|
const engineRoots = () => expandPatterns(options.configDirectory, options.engineRoots);
|
|
958
1012
|
const extraRoutesFiles = () => expandPatterns(options.configDirectory, options.routesFiles);
|
|
959
|
-
const
|
|
1013
|
+
const inflections = options.configDirectory === void 0 ? EMPTY_INFLECTIONS : readInflections(options.configDirectory);
|
|
1014
|
+
const acronyms = inflections.acronyms;
|
|
960
1015
|
const routesInput = () => ({
|
|
961
1016
|
routesFile: {
|
|
962
1017
|
file: routesFile,
|
|
@@ -1006,6 +1061,8 @@ function railsFramework(options = {}) {
|
|
|
1006
1061
|
name: "rails",
|
|
1007
1062
|
protocol: "http",
|
|
1008
1063
|
discovery: [pattern],
|
|
1064
|
+
bodyBlocks: BODY_BLOCKS,
|
|
1065
|
+
inflections,
|
|
1009
1066
|
// Every file routing or naming is read from decides an action's
|
|
1010
1067
|
// binding without being walked, so the cache key has to read them
|
|
1011
1068
|
// here. This runs before the grammar loads, so nothing parses Ruby.
|
package/dist/sqlalchemy.js
CHANGED
package/dist/sqlmodel.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
sqlalchemyModels,
|
|
2
3
|
sqlalchemyRawSql,
|
|
3
4
|
sqlalchemyStorage
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-NYWZHQBJ.js";
|
|
5
6
|
|
|
6
7
|
// ../framework/sqlmodel/dist/index.js
|
|
7
8
|
import { z } from "zod";
|
|
@@ -69,6 +70,21 @@ function sqlmodelStorage(options) {
|
|
|
69
70
|
...sqlalchemyStorage(options)
|
|
70
71
|
];
|
|
71
72
|
}
|
|
73
|
+
function sqlmodelModels() {
|
|
74
|
+
return sqlalchemyModels().map((model) => ({
|
|
75
|
+
baseNames: [...model.baseNames, "SQLModel"],
|
|
76
|
+
givesBack: model.givesBack,
|
|
77
|
+
entryMethods: [...model.entryMethods, { method: "exec", argument: 0 }],
|
|
78
|
+
entryFunctions: [
|
|
79
|
+
...model.entryFunctions,
|
|
80
|
+
{ module: "sqlmodel", name: "select", argument: 0 }
|
|
81
|
+
],
|
|
82
|
+
relationships: [
|
|
83
|
+
...model.relationships ?? [],
|
|
84
|
+
{ module: "sqlmodel", name: "Relationship" }
|
|
85
|
+
]
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
72
88
|
function sqlmodelRawSql(options) {
|
|
73
89
|
return [
|
|
74
90
|
{
|
|
@@ -90,6 +106,7 @@ function sqlmodelFramework(options) {
|
|
|
90
106
|
protocol: options.storageSystem,
|
|
91
107
|
discovery: [],
|
|
92
108
|
storage: sqlmodelStorage(options),
|
|
109
|
+
models: sqlmodelModels(),
|
|
93
110
|
rawSql: sqlmodelRawSql(options)
|
|
94
111
|
};
|
|
95
112
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/packs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Every pack suss ships in one install, so it can read Express, Hono, NestJS, FastAPI, Rails, Prisma, OpenAPI and the rest.",
|
|
6
6
|
"keywords": [
|
|
@@ -218,15 +218,15 @@
|
|
|
218
218
|
"ts-morph": "^23.0.0"
|
|
219
219
|
},
|
|
220
220
|
"dependencies": {
|
|
221
|
-
"@suss/adapter-python": "0.
|
|
222
|
-
"@suss/adapter-ruby": "0.
|
|
223
|
-
"@suss/adapter-typescript": "0.
|
|
224
|
-
"@suss/behavioral-ir": "0.
|
|
225
|
-
"@suss/contract-graphql": "0.
|
|
226
|
-
"@suss/extractor": "0.
|
|
227
|
-
"@suss/manifest-aws": "0.
|
|
228
|
-
"@suss/recognize": "0.
|
|
229
|
-
"@suss/sql": "0.
|
|
221
|
+
"@suss/adapter-python": "0.30.0",
|
|
222
|
+
"@suss/adapter-ruby": "0.30.0",
|
|
223
|
+
"@suss/adapter-typescript": "0.30.0",
|
|
224
|
+
"@suss/behavioral-ir": "0.30.0",
|
|
225
|
+
"@suss/contract-graphql": "0.30.0",
|
|
226
|
+
"@suss/extractor": "0.30.0",
|
|
227
|
+
"@suss/manifest-aws": "0.30.0",
|
|
228
|
+
"@suss/recognize": "0.30.0",
|
|
229
|
+
"@suss/sql": "0.30.0",
|
|
230
230
|
"zod": "^4.0.0"
|
|
231
231
|
},
|
|
232
232
|
"devDependencies": {
|