@saasicat/cli 1.0.0-rc.0 → 1.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -1
- package/bin/saasicat.js +335 -34
- package/codemods/v1-rename.map.json +6 -0
- package/dist/.build-stamp +1 -1
- package/dist/index.cjs +564 -30
- package/dist/index.d.cts +246 -14
- package/dist/index.d.ts +246 -14
- package/dist/index.js +542 -24
- package/package.json +7 -6
- package/templates/init/config/saas.yaml.tpl +40 -2
- package/templates/init/src/auth/password.hasher.ts.tpl +1 -1
- package/templates/init/src/saas/admin-manifest.contribution.ts.tpl +2 -2
- package/templates/init/src/saas/feature-ui-registry.ts.tpl +1 -1
- package/templates/init/src/saas/persistence-without-hasher.ts.tpl +1 -1
- package/templates/init/src/saas/persistence.ts.tpl +1 -1
- package/templates/init/src/saas/quota.provider.ts.tpl +1 -1
package/dist/index.cjs
CHANGED
|
@@ -64,17 +64,23 @@ __export(index_exports, {
|
|
|
64
64
|
MfaSetupFlow: () => MfaSetupFlow,
|
|
65
65
|
PLATFORM_DOCTOR_CHECK_PROVIDERS: () => PLATFORM_DOCTOR_CHECK_PROVIDERS,
|
|
66
66
|
PlanCatalogDoctorCheck: () => PlanCatalogDoctorCheck,
|
|
67
|
+
SCANNED_FOR_DB_CATALOG: () => SCANNED_FOR_DB_CATALOG,
|
|
68
|
+
SCANNED_FOR_MOVED_SETTINGS: () => SCANNED_FOR_MOVED_SETTINGS,
|
|
69
|
+
SETTINGS_THAT_MOVED: () => SETTINGS_THAT_MOVED,
|
|
67
70
|
UI_VUE_SPECIFIER: () => UI_VUE_SPECIFIER,
|
|
68
71
|
USER_MANAGEMENT_PORT_TOKEN: () => USER_MANAGEMENT_PORT_TOKEN,
|
|
69
72
|
USER_PORT_TOKEN: () => USER_PORT_TOKEN,
|
|
70
73
|
UserCommands: () => UserCommands,
|
|
71
74
|
UserPortDoctorCheck: () => UserPortDoctorCheck,
|
|
75
|
+
WHERE_DB_CATALOG_GOES: () => WHERE_DB_CATALOG_GOES,
|
|
76
|
+
WHERE_IT_GOES: () => WHERE_IT_GOES,
|
|
72
77
|
WhoAmIFlow: () => WhoAmIFlow,
|
|
78
|
+
appKeyPattern: () => appKeyPattern,
|
|
73
79
|
appendConstraints: () => appendConstraints,
|
|
74
80
|
applyFragmentBlocks: () => applyFragmentBlocks,
|
|
75
81
|
applyTokens: () => applyTokens,
|
|
76
82
|
assertModelsExist: () => assertModelsExist,
|
|
77
|
-
|
|
83
|
+
assertValidAppKey: () => assertValidAppKey,
|
|
78
84
|
assertValidQuotaKey: () => assertValidQuotaKey,
|
|
79
85
|
blankStringLiterals: () => blankStringLiterals,
|
|
80
86
|
blockBodyLines: () => blockBodyLines,
|
|
@@ -82,17 +88,24 @@ __export(index_exports, {
|
|
|
82
88
|
buildImportMap: () => buildImportMap,
|
|
83
89
|
checkSchema: () => checkSchema,
|
|
84
90
|
constraintsFor: () => constraintsFor,
|
|
91
|
+
describeDbCatalogOccurrence: () => describeDbCatalogOccurrence,
|
|
85
92
|
enableFkPointers: () => enableFkPointers,
|
|
86
93
|
extractBlockNames: () => extractBlockNames,
|
|
87
94
|
extractBlocks: () => extractBlocks,
|
|
95
|
+
extractEnumBlocks: () => extractEnumBlocks,
|
|
96
|
+
extractEnumNames: () => extractEnumNames,
|
|
97
|
+
extractFragmentBlocks: () => extractFragmentBlocks,
|
|
88
98
|
extractModelBlocks: () => extractModelBlocks,
|
|
89
99
|
extractModelNames: () => extractModelNames,
|
|
100
|
+
findDbCatalogBlocks: () => findDbCatalogBlocks,
|
|
90
101
|
findFkPointers: () => findFkPointers,
|
|
102
|
+
findMovedSettings: () => findMovedSettings,
|
|
91
103
|
foreignKeyOf: () => foreignKeyOf,
|
|
92
104
|
hasBackRelation: () => hasBackRelation,
|
|
93
105
|
hasConstraints: () => hasConstraints,
|
|
94
106
|
isNoLongerPublic: () => isNoLongerPublic,
|
|
95
107
|
isOneToOne: () => isOneToOne,
|
|
108
|
+
judgeModuleResolution: () => judgeModuleResolution,
|
|
96
109
|
kebabCase: () => kebabCase,
|
|
97
110
|
migrationCreatedBy: () => migrationCreatedBy,
|
|
98
111
|
minimumQuotasPerPlan: () => minimumQuotasPerPlan,
|
|
@@ -106,13 +119,16 @@ __export(index_exports, {
|
|
|
106
119
|
patchAppModule: () => patchAppModule,
|
|
107
120
|
patchOptionsFor: () => patchOptionsFor,
|
|
108
121
|
planInit: () => planInit,
|
|
109
|
-
projectKeyPattern: () => projectKeyPattern,
|
|
110
122
|
quotaKeyPattern: () => quotaKeyPattern,
|
|
123
|
+
readEffectiveModuleResolution: () => readEffectiveModuleResolution,
|
|
111
124
|
relationNameOf: () => relationNameOf,
|
|
125
|
+
removeProjectKey: () => removeProjectKey,
|
|
112
126
|
reportConstraints: () => reportConstraints,
|
|
113
127
|
rewriteImports: () => rewriteImports,
|
|
128
|
+
rewriteManifest: () => rewriteManifest,
|
|
114
129
|
rewriteNames: () => rewriteNames,
|
|
115
130
|
rewriteSubpath: () => rewriteSubpath,
|
|
131
|
+
settingsWrittenTo: () => settingsWrittenTo,
|
|
116
132
|
stripLineComment: () => stripLineComment,
|
|
117
133
|
structuralOnly: () => structuralOnly,
|
|
118
134
|
tablesAddressedBy: () => tablesAddressedBy
|
|
@@ -1027,7 +1043,7 @@ var PlanCatalogDoctorCheck = class {
|
|
|
1027
1043
|
severity: "ok",
|
|
1028
1044
|
message: `${plans.length} plan(s), ${this.catalog.features?.length ?? 0} feature(s) loaded.`,
|
|
1029
1045
|
details: {
|
|
1030
|
-
|
|
1046
|
+
app: this.catalog.app.name,
|
|
1031
1047
|
planIds: plans.map((p) => p.id)
|
|
1032
1048
|
}
|
|
1033
1049
|
};
|
|
@@ -1247,13 +1263,43 @@ function extractModelBlocks(fragment) {
|
|
|
1247
1263
|
return extractBlocks(fragment, "model");
|
|
1248
1264
|
}
|
|
1249
1265
|
__name(extractModelBlocks, "extractModelBlocks");
|
|
1266
|
+
function extractEnumBlocks(fragment) {
|
|
1267
|
+
return extractBlocks(fragment, "enum");
|
|
1268
|
+
}
|
|
1269
|
+
__name(extractEnumBlocks, "extractEnumBlocks");
|
|
1270
|
+
function extractEnumNames(schema2) {
|
|
1271
|
+
return extractBlockNames(schema2, "enum");
|
|
1272
|
+
}
|
|
1273
|
+
__name(extractEnumNames, "extractEnumNames");
|
|
1274
|
+
function extractFragmentBlocks(fragment) {
|
|
1275
|
+
return {
|
|
1276
|
+
enums: extractEnumBlocks(fragment),
|
|
1277
|
+
models: extractModelBlocks(fragment)
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
__name(extractFragmentBlocks, "extractFragmentBlocks");
|
|
1250
1281
|
function applyFragmentBlocks(schema2, fragmentBlocks, options = {}) {
|
|
1251
|
-
const
|
|
1282
|
+
const blocks = fragmentBlocks instanceof Map ? {
|
|
1283
|
+
enums: /* @__PURE__ */ new Map(),
|
|
1284
|
+
models: fragmentBlocks
|
|
1285
|
+
} : fragmentBlocks;
|
|
1286
|
+
const existingModels = new Set(extractModelNames(schema2));
|
|
1287
|
+
const existingEnums = new Set(extractEnumNames(schema2));
|
|
1252
1288
|
const added = [];
|
|
1253
1289
|
const skipped = [];
|
|
1290
|
+
const addedEnums = [];
|
|
1291
|
+
const skippedEnums = [];
|
|
1254
1292
|
const additions = [];
|
|
1255
|
-
for (const [name, block] of
|
|
1256
|
-
if (
|
|
1293
|
+
for (const [name, block] of blocks.enums) {
|
|
1294
|
+
if (existingEnums.has(name)) {
|
|
1295
|
+
skippedEnums.push(name);
|
|
1296
|
+
} else {
|
|
1297
|
+
addedEnums.push(name);
|
|
1298
|
+
additions.push(block);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
for (const [name, block] of blocks.models) {
|
|
1302
|
+
if (existingModels.has(name)) {
|
|
1257
1303
|
skipped.push(name);
|
|
1258
1304
|
} else {
|
|
1259
1305
|
added.push(name);
|
|
@@ -1264,6 +1310,8 @@ function applyFragmentBlocks(schema2, fragmentBlocks, options = {}) {
|
|
|
1264
1310
|
return {
|
|
1265
1311
|
added,
|
|
1266
1312
|
skipped,
|
|
1313
|
+
addedEnums,
|
|
1314
|
+
skippedEnums,
|
|
1267
1315
|
schema: schema2
|
|
1268
1316
|
};
|
|
1269
1317
|
}
|
|
@@ -1280,6 +1328,8 @@ function applyFragmentBlocks(schema2, fragmentBlocks, options = {}) {
|
|
|
1280
1328
|
return {
|
|
1281
1329
|
added,
|
|
1282
1330
|
skipped,
|
|
1331
|
+
addedEnums,
|
|
1332
|
+
skippedEnums,
|
|
1283
1333
|
schema: trimmedSchema + header + additions.join("\n\n") + "\n"
|
|
1284
1334
|
};
|
|
1285
1335
|
}
|
|
@@ -1665,10 +1715,11 @@ function required(value, what) {
|
|
|
1665
1715
|
return value;
|
|
1666
1716
|
}
|
|
1667
1717
|
__name(required, "required");
|
|
1668
|
-
|
|
1669
|
-
|
|
1718
|
+
var APP_KEY_PATTERN = /^[a-z][a-z0-9-]{1,30}$/;
|
|
1719
|
+
function appKeyPattern() {
|
|
1720
|
+
return APP_KEY_PATTERN;
|
|
1670
1721
|
}
|
|
1671
|
-
__name(
|
|
1722
|
+
__name(appKeyPattern, "appKeyPattern");
|
|
1672
1723
|
function quotaKeyPattern() {
|
|
1673
1724
|
const patterns = Object.keys(required(schema.$defs?.PlanDef?.properties?.quotas?.patternProperties, "quota key pattern"));
|
|
1674
1725
|
if (patterns.length !== 1) {
|
|
@@ -1681,12 +1732,12 @@ function minimumQuotasPerPlan() {
|
|
|
1681
1732
|
return required(schema.$defs?.PlanDef?.properties?.quotas?.minProperties, "minProperties");
|
|
1682
1733
|
}
|
|
1683
1734
|
__name(minimumQuotasPerPlan, "minimumQuotasPerPlan");
|
|
1684
|
-
function
|
|
1685
|
-
const pattern =
|
|
1686
|
-
if (pattern.test(
|
|
1687
|
-
throw new Error(`--
|
|
1735
|
+
function assertValidAppKey(appKey) {
|
|
1736
|
+
const pattern = appKeyPattern();
|
|
1737
|
+
if (pattern.test(appKey)) return;
|
|
1738
|
+
throw new Error(`--app-key=${appKey} is not a valid app key. It has to match ${pattern.source} \u2014 lower case, starting with a letter, at least two characters. It becomes an npm package name and a browser storage prefix, so this would fail after every file was written.`);
|
|
1688
1739
|
}
|
|
1689
|
-
__name(
|
|
1740
|
+
__name(assertValidAppKey, "assertValidAppKey");
|
|
1690
1741
|
function assertValidQuotaKey(quotaKey) {
|
|
1691
1742
|
const pattern = quotaKeyPattern();
|
|
1692
1743
|
if (pattern.test(quotaKey)) return;
|
|
@@ -1718,24 +1769,24 @@ function pascalCase(value) {
|
|
|
1718
1769
|
__name(pascalCase, "pascalCase");
|
|
1719
1770
|
var quotaFileName = /* @__PURE__ */ __name((key) => `${key.replace(/[^A-Za-z0-9]+/g, "-").toLowerCase()}-quota.provider.ts`, "quotaFileName");
|
|
1720
1771
|
function planInit(options) {
|
|
1721
|
-
const
|
|
1722
|
-
if (!
|
|
1723
|
-
|
|
1724
|
-
const appLabel = options.appName ?? pascalCase(
|
|
1772
|
+
const appKey = options.appKey;
|
|
1773
|
+
if (!appKey) throw new Error("init needs an --app-key.");
|
|
1774
|
+
assertValidAppKey(appKey);
|
|
1775
|
+
const appLabel = options.appName ?? pascalCase(appKey);
|
|
1725
1776
|
const appName = pascalCase(appLabel);
|
|
1726
1777
|
const apiBase = options.apiBase ?? "/api/v1/admin";
|
|
1727
1778
|
const quotas = (options.quotas ?? []).map(parseQuota);
|
|
1728
1779
|
assertEnoughQuotas(quotas);
|
|
1729
1780
|
const hasherClass = options.skipHasher ? null : `${appName}PasswordHasher`;
|
|
1730
|
-
const featureKey = `${
|
|
1781
|
+
const featureKey = `${appKey.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase()}_CORE`;
|
|
1731
1782
|
const shared = {
|
|
1732
|
-
|
|
1783
|
+
APP_KEY: appKey,
|
|
1733
1784
|
APP_NAME: appName,
|
|
1734
1785
|
APP_LABEL: appLabel,
|
|
1735
1786
|
API_BASE: apiBase,
|
|
1736
1787
|
FEATURE_KEY: featureKey,
|
|
1737
|
-
REGISTRY_CONST: `${constantCase(
|
|
1738
|
-
MANIFEST_CONST: `${constantCase(
|
|
1788
|
+
REGISTRY_CONST: `${constantCase(appKey)}_FEATURE_UI_REGISTRY`,
|
|
1789
|
+
MANIFEST_CONST: `${constantCase(appKey)}_MANIFEST_CONTRIBUTION`,
|
|
1739
1790
|
ADMIN_MODULE_CLASS: `${appName}AdminModule`,
|
|
1740
1791
|
HASHER_CLASS: hasherClass ?? "",
|
|
1741
1792
|
HASHER_FILE: hasherClass ? `${kebabCase(appName)}-password.hasher` : "",
|
|
@@ -1834,6 +1885,63 @@ function patchOptionsFor(plan) {
|
|
|
1834
1885
|
}
|
|
1835
1886
|
__name(patchOptionsFor, "patchOptionsFor");
|
|
1836
1887
|
|
|
1888
|
+
// src/init/module-resolution.ts
|
|
1889
|
+
var import_node_path = require("path");
|
|
1890
|
+
var RESOLVES_SUBPATHS = /* @__PURE__ */ new Set([
|
|
1891
|
+
"node16",
|
|
1892
|
+
"nodenext",
|
|
1893
|
+
"bundler"
|
|
1894
|
+
]);
|
|
1895
|
+
function judgeModuleResolution(value) {
|
|
1896
|
+
if (value === null || RESOLVES_SUBPATHS.has(value)) {
|
|
1897
|
+
return {
|
|
1898
|
+
ok: true,
|
|
1899
|
+
value
|
|
1900
|
+
};
|
|
1901
|
+
}
|
|
1902
|
+
return {
|
|
1903
|
+
ok: false,
|
|
1904
|
+
value,
|
|
1905
|
+
reason: `tsconfig.json resolves modules with "moduleResolution": "${value}"` + (value === "node10" ? ' (what TypeScript calls the "node" setting)' : "") + '. The files init writes import subpath exports (`@saasicat/nest/platform`, `/billing`, `/discovery`), which TypeScript resolves only under "node16", "nodenext" or "bundler". Set one of those first \u2014 `nest new` has used "nodenext" since NestJS 10.'
|
|
1906
|
+
};
|
|
1907
|
+
}
|
|
1908
|
+
__name(judgeModuleResolution, "judgeModuleResolution");
|
|
1909
|
+
function readEffectiveModuleResolution(root, ts) {
|
|
1910
|
+
const configPath = (0, import_node_path.join)(root, "tsconfig.json");
|
|
1911
|
+
const read = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
1912
|
+
if (read.error || read.config === void 0) return null;
|
|
1913
|
+
const parsed = ts.parseJsonConfigFileContent(read.config, ts.sys, root);
|
|
1914
|
+
const kind = parsed.options.moduleResolution;
|
|
1915
|
+
if (kind === void 0) return null;
|
|
1916
|
+
return ts.ModuleResolutionKind[kind].toLowerCase();
|
|
1917
|
+
}
|
|
1918
|
+
__name(readEffectiveModuleResolution, "readEffectiveModuleResolution");
|
|
1919
|
+
|
|
1920
|
+
// src/init/settings-written.ts
|
|
1921
|
+
var import_billing = require("@saasicat/nest/billing");
|
|
1922
|
+
function flatten(prefix, value, into) {
|
|
1923
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
1924
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1925
|
+
flatten(`${prefix}.${key}`, child, into);
|
|
1926
|
+
}
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1929
|
+
into.push({
|
|
1930
|
+
key: prefix,
|
|
1931
|
+
value: JSON.stringify(value)
|
|
1932
|
+
});
|
|
1933
|
+
}
|
|
1934
|
+
__name(flatten, "flatten");
|
|
1935
|
+
function settingsWrittenTo(catalogYaml, source = "config/saas.yaml") {
|
|
1936
|
+
const catalog = (0, import_billing.loadPlanCatalogFromString)(catalogYaml, {
|
|
1937
|
+
source
|
|
1938
|
+
});
|
|
1939
|
+
const settings = [];
|
|
1940
|
+
flatten("tenantBilling", catalog.tenantBilling, settings);
|
|
1941
|
+
return settings;
|
|
1942
|
+
}
|
|
1943
|
+
__name(settingsWrittenTo, "settingsWrittenTo");
|
|
1944
|
+
|
|
1837
1945
|
// src/codemods/v1-imports.ts
|
|
1838
1946
|
var PUBLIC_PREFIXES = [
|
|
1839
1947
|
"ui/",
|
|
@@ -1999,6 +2107,13 @@ function rewriteNames(text, table) {
|
|
|
1999
2107
|
return `${quote}${to}`;
|
|
2000
2108
|
});
|
|
2001
2109
|
}
|
|
2110
|
+
for (const [from, to] of Object.entries(table.packages ?? {})) {
|
|
2111
|
+
if (from === "_") continue;
|
|
2112
|
+
next = next.replace(new RegExp(`${escape(from)}(?=['"\`/])`, "g"), () => {
|
|
2113
|
+
rewritten += 1;
|
|
2114
|
+
return to;
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2002
2117
|
for (const [from, to] of Object.entries(table.subpaths)) {
|
|
2003
2118
|
next = next.replace(new RegExp(escape(from), "g"), () => {
|
|
2004
2119
|
rewritten += 1;
|
|
@@ -2014,6 +2129,402 @@ function rewriteNames(text, table) {
|
|
|
2014
2129
|
};
|
|
2015
2130
|
}
|
|
2016
2131
|
__name(rewriteNames, "rewriteNames");
|
|
2132
|
+
var DEPENDENCY_FIELDS = [
|
|
2133
|
+
"dependencies",
|
|
2134
|
+
"devDependencies",
|
|
2135
|
+
"peerDependencies",
|
|
2136
|
+
"optionalDependencies"
|
|
2137
|
+
];
|
|
2138
|
+
var UNTRANSLATABLE_RANGE = /^(workspace:|file:|link:|npm:|git\+|https?:)/;
|
|
2139
|
+
function rewriteManifest(text, table, options) {
|
|
2140
|
+
const renames = Object.entries(table.packages ?? {}).filter(([from]) => from !== "_");
|
|
2141
|
+
const ambiguous = [];
|
|
2142
|
+
let manifest;
|
|
2143
|
+
try {
|
|
2144
|
+
manifest = JSON.parse(text);
|
|
2145
|
+
} catch {
|
|
2146
|
+
return {
|
|
2147
|
+
text,
|
|
2148
|
+
rewritten: 0,
|
|
2149
|
+
ambiguous
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
let rewritten = 0;
|
|
2153
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
2154
|
+
const deps = manifest[field];
|
|
2155
|
+
if (!deps || typeof deps !== "object") continue;
|
|
2156
|
+
const entries = Object.entries(deps);
|
|
2157
|
+
const renamed = entries.map(([name, range]) => {
|
|
2158
|
+
const to = renames.find(([from]) => from === name)?.[1];
|
|
2159
|
+
if (!to) return [
|
|
2160
|
+
name,
|
|
2161
|
+
range
|
|
2162
|
+
];
|
|
2163
|
+
if (UNTRANSLATABLE_RANGE.test(range)) {
|
|
2164
|
+
ambiguous.push(`${name} in ${field} (${range})`);
|
|
2165
|
+
return [
|
|
2166
|
+
name,
|
|
2167
|
+
range
|
|
2168
|
+
];
|
|
2169
|
+
}
|
|
2170
|
+
rewritten += 1;
|
|
2171
|
+
return [
|
|
2172
|
+
to,
|
|
2173
|
+
options.targetRange
|
|
2174
|
+
];
|
|
2175
|
+
});
|
|
2176
|
+
manifest[field] = Object.fromEntries(renamed);
|
|
2177
|
+
}
|
|
2178
|
+
const meta = manifest.peerDependenciesMeta;
|
|
2179
|
+
if (meta && typeof meta === "object") {
|
|
2180
|
+
manifest.peerDependenciesMeta = Object.fromEntries(Object.entries(meta).map(([name, flags]) => {
|
|
2181
|
+
const to = renames.find(([from]) => from === name)?.[1];
|
|
2182
|
+
if (!to) return [
|
|
2183
|
+
name,
|
|
2184
|
+
flags
|
|
2185
|
+
];
|
|
2186
|
+
rewritten += 1;
|
|
2187
|
+
return [
|
|
2188
|
+
to,
|
|
2189
|
+
flags
|
|
2190
|
+
];
|
|
2191
|
+
}));
|
|
2192
|
+
}
|
|
2193
|
+
if (rewritten === 0) return {
|
|
2194
|
+
text,
|
|
2195
|
+
rewritten: 0,
|
|
2196
|
+
ambiguous
|
|
2197
|
+
};
|
|
2198
|
+
const indent = /^[ \t]+/m.exec(text)?.[0] ?? " ";
|
|
2199
|
+
const trailing = text.endsWith("\n") ? "\n" : "";
|
|
2200
|
+
return {
|
|
2201
|
+
text: JSON.stringify(manifest, null, indent) + trailing,
|
|
2202
|
+
rewritten,
|
|
2203
|
+
ambiguous
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
__name(rewriteManifest, "rewriteManifest");
|
|
2207
|
+
|
|
2208
|
+
// src/codemods/v1-project-key.ts
|
|
2209
|
+
function stripQueryParameter(text) {
|
|
2210
|
+
const NEEDLE = "projectKey=";
|
|
2211
|
+
let out = "";
|
|
2212
|
+
let index = 0;
|
|
2213
|
+
const taken = /* @__PURE__ */ new Set();
|
|
2214
|
+
for (; ; ) {
|
|
2215
|
+
const at = text.indexOf(NEEDLE, index);
|
|
2216
|
+
if (at < 0) break;
|
|
2217
|
+
const separator = at === 0 ? "" : text[at - 1];
|
|
2218
|
+
const value = separator === "?" || separator === "&" ? simpleValueEnd(text, at + NEEDLE.length) : null;
|
|
2219
|
+
if (value === null || !servesTheCatalogue(text, at)) {
|
|
2220
|
+
out += text.slice(index, at + NEEDLE.length);
|
|
2221
|
+
index = at + NEEDLE.length;
|
|
2222
|
+
continue;
|
|
2223
|
+
}
|
|
2224
|
+
out += text.slice(index, at - 1);
|
|
2225
|
+
if (separator === "?" && text[value] === "&") {
|
|
2226
|
+
out += "?";
|
|
2227
|
+
index = value + 1;
|
|
2228
|
+
} else {
|
|
2229
|
+
index = value;
|
|
2230
|
+
}
|
|
2231
|
+
taken.add(at);
|
|
2232
|
+
}
|
|
2233
|
+
return {
|
|
2234
|
+
text: out + text.slice(index),
|
|
2235
|
+
taken
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2238
|
+
__name(stripQueryParameter, "stripQueryParameter");
|
|
2239
|
+
var HIDES_A_BRACE = /[`'"{}/\\]/;
|
|
2240
|
+
function simpleValueEnd(text, from) {
|
|
2241
|
+
let i = from;
|
|
2242
|
+
while (i < text.length) {
|
|
2243
|
+
const ch = text[i];
|
|
2244
|
+
if (ch === "&" || ch === "#" || ch === "'" || ch === '"' || ch === "`" || ch === "\n" || ch === " ") {
|
|
2245
|
+
return i;
|
|
2246
|
+
}
|
|
2247
|
+
if (ch !== "$" || text[i + 1] !== "{") {
|
|
2248
|
+
if (ch === "{" || ch === "}") return null;
|
|
2249
|
+
i += 1;
|
|
2250
|
+
continue;
|
|
2251
|
+
}
|
|
2252
|
+
const close = text.indexOf("}", i + 2);
|
|
2253
|
+
if (close < 0) return null;
|
|
2254
|
+
if (HIDES_A_BRACE.test(text.slice(i + 2, close))) return null;
|
|
2255
|
+
i = close + 1;
|
|
2256
|
+
}
|
|
2257
|
+
return null;
|
|
2258
|
+
}
|
|
2259
|
+
__name(simpleValueEnd, "simpleValueEnd");
|
|
2260
|
+
function servesTheCatalogue(text, at) {
|
|
2261
|
+
let start = at;
|
|
2262
|
+
while (start > 0) {
|
|
2263
|
+
const ch = text[start - 1];
|
|
2264
|
+
if (ch === "`" || ch === "'" || ch === '"' || ch === "\n") break;
|
|
2265
|
+
start -= 1;
|
|
2266
|
+
}
|
|
2267
|
+
const url = text.slice(start, at);
|
|
2268
|
+
const query = url.indexOf("?");
|
|
2269
|
+
return (query < 0 ? url : url.slice(0, query)).includes("/catalog/");
|
|
2270
|
+
}
|
|
2271
|
+
__name(servesTheCatalogue, "servesTheCatalogue");
|
|
2272
|
+
function isIdentifierChar(ch) {
|
|
2273
|
+
return ch !== void 0 && /[A-Za-z0-9_$]/.test(ch);
|
|
2274
|
+
}
|
|
2275
|
+
__name(isIdentifierChar, "isIdentifierChar");
|
|
2276
|
+
function lineAt(text, at) {
|
|
2277
|
+
let line = 1;
|
|
2278
|
+
for (let i = 0; i < at; i += 1) if (text[i] === "\n") line += 1;
|
|
2279
|
+
return line;
|
|
2280
|
+
}
|
|
2281
|
+
__name(lineAt, "lineAt");
|
|
2282
|
+
function removeProjectKey(text, kind = "source") {
|
|
2283
|
+
if (kind === "yaml") return removeFromYaml(text);
|
|
2284
|
+
const query = stripQueryParameter(text);
|
|
2285
|
+
const reported = /* @__PURE__ */ new Set();
|
|
2286
|
+
for (let at = text.indexOf("projectKey"); at >= 0; at = text.indexOf("projectKey", at + 1)) {
|
|
2287
|
+
if (query.taken.has(at)) continue;
|
|
2288
|
+
if (isIdentifierChar(text[at - 1])) continue;
|
|
2289
|
+
if (isIdentifierChar(text[at + "projectKey".length])) continue;
|
|
2290
|
+
reported.add(lineAt(text, at));
|
|
2291
|
+
}
|
|
2292
|
+
return {
|
|
2293
|
+
text: query.text,
|
|
2294
|
+
rewritten: query.taken.size,
|
|
2295
|
+
undecided: [
|
|
2296
|
+
...reported
|
|
2297
|
+
].sort((a, b) => a - b)
|
|
2298
|
+
};
|
|
2299
|
+
}
|
|
2300
|
+
__name(removeProjectKey, "removeProjectKey");
|
|
2301
|
+
function removeFromYaml(text) {
|
|
2302
|
+
const lines = text.split("\n");
|
|
2303
|
+
const kept = [];
|
|
2304
|
+
let rewritten = 0;
|
|
2305
|
+
for (const line of lines) {
|
|
2306
|
+
if (line.startsWith("projectKey:")) {
|
|
2307
|
+
rewritten += 1;
|
|
2308
|
+
continue;
|
|
2309
|
+
}
|
|
2310
|
+
kept.push(line);
|
|
2311
|
+
}
|
|
2312
|
+
return {
|
|
2313
|
+
text: kept.join("\n"),
|
|
2314
|
+
rewritten,
|
|
2315
|
+
undecided: []
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
__name(removeFromYaml, "removeFromYaml");
|
|
2319
|
+
|
|
2320
|
+
// src/codemods/v1-moved-settings.ts
|
|
2321
|
+
var import_spec2 = require("@saasicat/spec");
|
|
2322
|
+
var SETTINGS_THAT_MOVED = Object.keys(import_spec2.planCatalogSchema.properties?.tenantBilling?.properties ?? (() => {
|
|
2323
|
+
throw new Error("plan-catalog.schema.json declares no tenantBilling properties \u2014 @saasicat/spec and @saasicat/cli are out of step.");
|
|
2324
|
+
})());
|
|
2325
|
+
var isIdentifierChar2 = /* @__PURE__ */ __name((ch) => ch !== void 0 && /[A-Za-z0-9_$]/.test(ch), "isIdentifierChar");
|
|
2326
|
+
function lineAt2(text, index) {
|
|
2327
|
+
let line = 1;
|
|
2328
|
+
for (let at = 0; at < index; at += 1) {
|
|
2329
|
+
if (text[at] === "\n") line += 1;
|
|
2330
|
+
}
|
|
2331
|
+
return line;
|
|
2332
|
+
}
|
|
2333
|
+
__name(lineAt2, "lineAt");
|
|
2334
|
+
var SCANNED_FOR_MOVED_SETTINGS = /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|vue)$/;
|
|
2335
|
+
function findMovedSettings(text) {
|
|
2336
|
+
const occurrences = [];
|
|
2337
|
+
for (const setting of SETTINGS_THAT_MOVED) {
|
|
2338
|
+
for (let at = text.indexOf(setting); at >= 0; at = text.indexOf(setting, at + 1)) {
|
|
2339
|
+
if (isIdentifierChar2(text[at - 1])) continue;
|
|
2340
|
+
if (isIdentifierChar2(text[at + setting.length])) continue;
|
|
2341
|
+
occurrences.push({
|
|
2342
|
+
setting,
|
|
2343
|
+
line: lineAt2(text, at)
|
|
2344
|
+
});
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
return {
|
|
2348
|
+
occurrences: occurrences.sort((a, b) => a.line - b.line || a.setting.localeCompare(b.setting))
|
|
2349
|
+
};
|
|
2350
|
+
}
|
|
2351
|
+
__name(findMovedSettings, "findMovedSettings");
|
|
2352
|
+
var WHERE_IT_GOES = {
|
|
2353
|
+
cancellationNoticeDays: "config/saas.yaml \u2192 tenantBilling.cancellationNoticeDays \u2014 both `monthly` and `yearly` are required, so write the number you are running today rather than leaving one out.",
|
|
2354
|
+
selfServiceBlockedPlans: 'config/saas.yaml \u2192 tenantBilling.selfServiceBlockedPlans \u2014 both `asTarget` and `asSource` are required, and `[]` is the way to say "nothing is blocked".'
|
|
2355
|
+
};
|
|
2356
|
+
|
|
2357
|
+
// src/codemods/v1-db-catalog.ts
|
|
2358
|
+
var import_platform = require("@saasicat/nest/platform");
|
|
2359
|
+
var SCANNED_FOR_DB_CATALOG = SCANNED_FOR_MOVED_SETTINGS;
|
|
2360
|
+
var PROPERTY = "dbCatalog";
|
|
2361
|
+
var TAKEN = new Set(import_platform.DB_CATALOG_MEMBERS);
|
|
2362
|
+
var isIdentifierStart = /* @__PURE__ */ __name((ch) => ch !== void 0 && /[A-Za-z_$]/.test(ch), "isIdentifierStart");
|
|
2363
|
+
var isIdentifierChar3 = /* @__PURE__ */ __name((ch) => ch !== void 0 && /[A-Za-z0-9_$]/.test(ch), "isIdentifierChar");
|
|
2364
|
+
var isBlank = /* @__PURE__ */ __name((ch) => ch === " " || ch === " " || ch === "\n" || ch === "\r", "isBlank");
|
|
2365
|
+
function lineAt3(text, index) {
|
|
2366
|
+
let line = 1;
|
|
2367
|
+
for (let at = 0; at < index; at += 1) {
|
|
2368
|
+
if (text[at] === "\n") line += 1;
|
|
2369
|
+
}
|
|
2370
|
+
return line;
|
|
2371
|
+
}
|
|
2372
|
+
__name(lineAt3, "lineAt");
|
|
2373
|
+
function skipBlanks(text, from) {
|
|
2374
|
+
let at = from;
|
|
2375
|
+
while (isBlank(text[at])) at += 1;
|
|
2376
|
+
return at;
|
|
2377
|
+
}
|
|
2378
|
+
__name(skipBlanks, "skipBlanks");
|
|
2379
|
+
function closingQuote(text, open) {
|
|
2380
|
+
const quote = text[open];
|
|
2381
|
+
for (let at = open + 1; at < text.length; at += 1) {
|
|
2382
|
+
if (text[at] === "\\") {
|
|
2383
|
+
at += 1;
|
|
2384
|
+
continue;
|
|
2385
|
+
}
|
|
2386
|
+
if (text[at] === quote) return at;
|
|
2387
|
+
}
|
|
2388
|
+
return text.length;
|
|
2389
|
+
}
|
|
2390
|
+
__name(closingQuote, "closingQuote");
|
|
2391
|
+
function skipCommentOrString(text, at) {
|
|
2392
|
+
const ch = text[at];
|
|
2393
|
+
if (ch === "/" && text[at + 1] === "/") {
|
|
2394
|
+
const end = text.indexOf("\n", at);
|
|
2395
|
+
return end === -1 ? text.length : end;
|
|
2396
|
+
}
|
|
2397
|
+
if (ch === "/" && text[at + 1] === "*") {
|
|
2398
|
+
const end = text.indexOf("*/", at + 2);
|
|
2399
|
+
return end === -1 ? text.length : end + 2;
|
|
2400
|
+
}
|
|
2401
|
+
if (ch === "'" || ch === '"' || ch === "`") return closingQuote(text, at) + 1;
|
|
2402
|
+
return at;
|
|
2403
|
+
}
|
|
2404
|
+
__name(skipCommentOrString, "skipCommentOrString");
|
|
2405
|
+
function objectLiteralAt(text, open) {
|
|
2406
|
+
const members = [];
|
|
2407
|
+
let depth = 0;
|
|
2408
|
+
let expectingKey = true;
|
|
2409
|
+
let at = open + 1;
|
|
2410
|
+
while (at < text.length) {
|
|
2411
|
+
const ch = text[at];
|
|
2412
|
+
if (ch === "'" || ch === '"' || ch === "`") {
|
|
2413
|
+
const end = closingQuote(text, at);
|
|
2414
|
+
if (depth === 0 && expectingKey && text[skipBlanks(text, end + 1)] === ":") {
|
|
2415
|
+
members.push(text.slice(at + 1, end));
|
|
2416
|
+
}
|
|
2417
|
+
at = end + 1;
|
|
2418
|
+
continue;
|
|
2419
|
+
}
|
|
2420
|
+
const skipped = skipCommentOrString(text, at);
|
|
2421
|
+
if (skipped > at) {
|
|
2422
|
+
at = skipped;
|
|
2423
|
+
continue;
|
|
2424
|
+
}
|
|
2425
|
+
if (ch === "{" || ch === "[" || ch === "(") {
|
|
2426
|
+
depth += 1;
|
|
2427
|
+
at += 1;
|
|
2428
|
+
continue;
|
|
2429
|
+
}
|
|
2430
|
+
if (ch === "}" || ch === "]" || ch === ")") {
|
|
2431
|
+
if (depth === 0) return {
|
|
2432
|
+
members,
|
|
2433
|
+
end: at + 1
|
|
2434
|
+
};
|
|
2435
|
+
depth -= 1;
|
|
2436
|
+
at += 1;
|
|
2437
|
+
continue;
|
|
2438
|
+
}
|
|
2439
|
+
if (depth === 0 && ch === ":") expectingKey = false;
|
|
2440
|
+
if (depth === 0 && ch === ",") expectingKey = true;
|
|
2441
|
+
if (depth === 0 && expectingKey && ch === "." && text.startsWith("...", at)) {
|
|
2442
|
+
let end = at + 3;
|
|
2443
|
+
while (isIdentifierChar3(text[end])) end += 1;
|
|
2444
|
+
members.push(text.slice(at, end));
|
|
2445
|
+
at = end;
|
|
2446
|
+
continue;
|
|
2447
|
+
}
|
|
2448
|
+
if (depth === 0 && expectingKey && isIdentifierStart(ch)) {
|
|
2449
|
+
let end = at;
|
|
2450
|
+
while (isIdentifierChar3(text[end])) end += 1;
|
|
2451
|
+
const next = text[skipBlanks(text, end)];
|
|
2452
|
+
if (next === ":" || next === "," || next === "}") members.push(text.slice(at, end));
|
|
2453
|
+
at = end;
|
|
2454
|
+
continue;
|
|
2455
|
+
}
|
|
2456
|
+
at += 1;
|
|
2457
|
+
}
|
|
2458
|
+
return {
|
|
2459
|
+
members,
|
|
2460
|
+
end: text.length
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
__name(objectLiteralAt, "objectLiteralAt");
|
|
2464
|
+
function findDbCatalogBlocks(text) {
|
|
2465
|
+
const occurrences = [];
|
|
2466
|
+
let at = 0;
|
|
2467
|
+
while (at < text.length) {
|
|
2468
|
+
const skipped = skipCommentOrString(text, at);
|
|
2469
|
+
if (skipped > at) {
|
|
2470
|
+
at = skipped;
|
|
2471
|
+
continue;
|
|
2472
|
+
}
|
|
2473
|
+
if (!text.startsWith(PROPERTY, at) || isIdentifierChar3(text[at - 1])) {
|
|
2474
|
+
at += 1;
|
|
2475
|
+
continue;
|
|
2476
|
+
}
|
|
2477
|
+
const afterName = at + PROPERTY.length;
|
|
2478
|
+
if (isIdentifierChar3(text[afterName])) {
|
|
2479
|
+
at = afterName;
|
|
2480
|
+
continue;
|
|
2481
|
+
}
|
|
2482
|
+
const colon = skipBlanks(text, afterName);
|
|
2483
|
+
if (text[colon] !== ":") {
|
|
2484
|
+
at = afterName;
|
|
2485
|
+
continue;
|
|
2486
|
+
}
|
|
2487
|
+
const value = skipBlanks(text, colon + 1);
|
|
2488
|
+
const line = lineAt3(text, at);
|
|
2489
|
+
if (text[value] !== "{") {
|
|
2490
|
+
occurrences.push({
|
|
2491
|
+
line,
|
|
2492
|
+
shape: "reference",
|
|
2493
|
+
leftovers: []
|
|
2494
|
+
});
|
|
2495
|
+
at = value;
|
|
2496
|
+
continue;
|
|
2497
|
+
}
|
|
2498
|
+
const { members, end } = objectLiteralAt(text, value);
|
|
2499
|
+
const leftovers = members.filter((member) => !TAKEN.has(member));
|
|
2500
|
+
if (!members.includes("path")) {
|
|
2501
|
+
occurrences.push({
|
|
2502
|
+
line,
|
|
2503
|
+
shape: "values",
|
|
2504
|
+
leftovers
|
|
2505
|
+
});
|
|
2506
|
+
} else if (leftovers.length > 0) {
|
|
2507
|
+
occurrences.push({
|
|
2508
|
+
line,
|
|
2509
|
+
shape: "mixed",
|
|
2510
|
+
leftovers
|
|
2511
|
+
});
|
|
2512
|
+
}
|
|
2513
|
+
at = end;
|
|
2514
|
+
}
|
|
2515
|
+
return {
|
|
2516
|
+
occurrences
|
|
2517
|
+
};
|
|
2518
|
+
}
|
|
2519
|
+
__name(findDbCatalogBlocks, "findDbCatalogBlocks");
|
|
2520
|
+
function describeDbCatalogOccurrence(occurrence) {
|
|
2521
|
+
const { shape, leftovers } = occurrence;
|
|
2522
|
+
if (shape === "reference") return "carries something this cannot see into";
|
|
2523
|
+
if (shape === "mixed") return `names the path but still carries ${leftovers.join(", ")}`;
|
|
2524
|
+
return leftovers.length > 0 ? `carries the values: ${leftovers.join(", ")}` : "names no path";
|
|
2525
|
+
}
|
|
2526
|
+
__name(describeDbCatalogOccurrence, "describeDbCatalogOccurrence");
|
|
2527
|
+
var WHERE_DB_CATALOG_GOES = "dbCatalog: { path: 'config/saas.yaml' } \u2014 the file the values were forwarded from. Delete the values; the platform reads app, currency, vatRate, tenantBilling, marketing and notifications from the file it names, and the plans from the read sink as before.";
|
|
2017
2528
|
|
|
2018
2529
|
// src/init/patch-app-module.ts
|
|
2019
2530
|
var MARKER = "SaaSiCatModule.forRoot";
|
|
@@ -2035,7 +2546,7 @@ ${block}`;
|
|
|
2035
2546
|
return {
|
|
2036
2547
|
source,
|
|
2037
2548
|
status: "declined",
|
|
2038
|
-
reason: "no `@Module({
|
|
2549
|
+
reason: "this file has no `@Module({ ... })` with an `imports:` key on a line of its own \u2014 the shape `nest new` writes \u2014 so there is nowhere to add the platform without guessing at the structure",
|
|
2039
2550
|
manualBlock
|
|
2040
2551
|
};
|
|
2041
2552
|
}
|
|
@@ -2114,7 +2625,7 @@ function renderForRootBlock(options) {
|
|
|
2114
2625
|
"SaaSiCatModule.forRoot(",
|
|
2115
2626
|
" defineSaaSiCat({",
|
|
2116
2627
|
" // Plans straight from the YAML. Apps that manage plans in the",
|
|
2117
|
-
" // SuperAdmin UI pass `dbCatalog` instead.",
|
|
2628
|
+
" // SuperAdmin UI pass `dbCatalog: { path: 'config/saas.yaml' }` instead.",
|
|
2118
2629
|
" planCatalog: loadPlanCatalogFromFile({ path: 'config/saas.yaml' }),",
|
|
2119
2630
|
" // Your authentication guard. This does NOT compile until you",
|
|
2120
2631
|
" // name one, and that is deliberate: an empty array is how the",
|
|
@@ -2123,6 +2634,13 @@ function renderForRootBlock(options) {
|
|
|
2123
2634
|
" // your whole capability inventory \u2014 and the manifest routes to",
|
|
2124
2635
|
" // anyone who asks. Import your guard and put it in.",
|
|
2125
2636
|
" controller: { guards: [YourAuthGuard] },",
|
|
2637
|
+
" // The modules whose providers the platform injects: the one",
|
|
2638
|
+
" // exporting PrismaService, and the one your guard depends on.",
|
|
2639
|
+
" // `prismaPersistence({ client: PrismaService })` is resolved",
|
|
2640
|
+
" // inside the platform module, which sees only what is listed",
|
|
2641
|
+
" // here or declared @Global. Same rule as the guard: this does",
|
|
2642
|
+
" // not compile until you name them.",
|
|
2643
|
+
" imports: [YourPrismaModule, YourAuthModule],",
|
|
2126
2644
|
options.persistenceImport ? " persistence," : " // persistence: prismaPersistence({ client: PrismaService }),",
|
|
2127
2645
|
" catalog: { featureUiRegistry: " + options.registry.constName + " },",
|
|
2128
2646
|
" adminResources: true,",
|
|
@@ -2769,7 +3287,7 @@ DoctorCommands = _ts_decorate12([
|
|
|
2769
3287
|
|
|
2770
3288
|
// src/discovery.command.ts
|
|
2771
3289
|
var import_node_fs = require("fs");
|
|
2772
|
-
var
|
|
3290
|
+
var import_node_path2 = require("path");
|
|
2773
3291
|
var import_common13 = require("@nestjs/common");
|
|
2774
3292
|
var import_nest_commander5 = require("nest-commander");
|
|
2775
3293
|
var import_nest6 = require("@saasicat/nest");
|
|
@@ -2807,8 +3325,8 @@ var DiscoveryScanCommand = class extends import_nest_commander5.CommandRunner {
|
|
|
2807
3325
|
try {
|
|
2808
3326
|
const snapshot = this.scanner.rebuildSnapshot();
|
|
2809
3327
|
if (flags.out) {
|
|
2810
|
-
const outPath = (0,
|
|
2811
|
-
(0, import_node_fs.mkdirSync)((0,
|
|
3328
|
+
const outPath = (0, import_node_path2.resolve)(flags.out);
|
|
3329
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path2.dirname)(outPath), {
|
|
2812
3330
|
recursive: true
|
|
2813
3331
|
});
|
|
2814
3332
|
(0, import_node_fs.writeFileSync)(outPath, JSON.stringify(snapshot, null, 2) + "\n", "utf-8");
|
|
@@ -2817,7 +3335,7 @@ var DiscoveryScanCommand = class extends import_nest_commander5.CommandRunner {
|
|
|
2817
3335
|
process.stdout.write(`Discovery-Scan (${snapshot.app.key} v${snapshot.app.version}): ${snapshot.capabilities.length} Capabilities \xB7 ${snapshot.features.length} Features \xB7 ${snapshot.quotas.length} Quotas \xB7 hash ${snapshot.hash.slice(0, 19)}\u2026
|
|
2818
3336
|
`);
|
|
2819
3337
|
if (target) {
|
|
2820
|
-
process.stdout.write(`Snapshot persisted: ${(0,
|
|
3338
|
+
process.stdout.write(`Snapshot persisted: ${(0, import_node_path2.resolve)(target)}
|
|
2821
3339
|
`);
|
|
2822
3340
|
} else {
|
|
2823
3341
|
process.stderr.write("WARNING: the snapshot was not persisted \u2014 neither snapshotPath (DiscoveryModule.forRoot) configured nor --out given. The seed gate will find no snapshot this way.\n");
|
|
@@ -3225,17 +3743,23 @@ UserCommands = _ts_decorate14([
|
|
|
3225
3743
|
MfaSetupFlow,
|
|
3226
3744
|
PLATFORM_DOCTOR_CHECK_PROVIDERS,
|
|
3227
3745
|
PlanCatalogDoctorCheck,
|
|
3746
|
+
SCANNED_FOR_DB_CATALOG,
|
|
3747
|
+
SCANNED_FOR_MOVED_SETTINGS,
|
|
3748
|
+
SETTINGS_THAT_MOVED,
|
|
3228
3749
|
UI_VUE_SPECIFIER,
|
|
3229
3750
|
USER_MANAGEMENT_PORT_TOKEN,
|
|
3230
3751
|
USER_PORT_TOKEN,
|
|
3231
3752
|
UserCommands,
|
|
3232
3753
|
UserPortDoctorCheck,
|
|
3754
|
+
WHERE_DB_CATALOG_GOES,
|
|
3755
|
+
WHERE_IT_GOES,
|
|
3233
3756
|
WhoAmIFlow,
|
|
3757
|
+
appKeyPattern,
|
|
3234
3758
|
appendConstraints,
|
|
3235
3759
|
applyFragmentBlocks,
|
|
3236
3760
|
applyTokens,
|
|
3237
3761
|
assertModelsExist,
|
|
3238
|
-
|
|
3762
|
+
assertValidAppKey,
|
|
3239
3763
|
assertValidQuotaKey,
|
|
3240
3764
|
blankStringLiterals,
|
|
3241
3765
|
blockBodyLines,
|
|
@@ -3243,17 +3767,24 @@ UserCommands = _ts_decorate14([
|
|
|
3243
3767
|
buildImportMap,
|
|
3244
3768
|
checkSchema,
|
|
3245
3769
|
constraintsFor,
|
|
3770
|
+
describeDbCatalogOccurrence,
|
|
3246
3771
|
enableFkPointers,
|
|
3247
3772
|
extractBlockNames,
|
|
3248
3773
|
extractBlocks,
|
|
3774
|
+
extractEnumBlocks,
|
|
3775
|
+
extractEnumNames,
|
|
3776
|
+
extractFragmentBlocks,
|
|
3249
3777
|
extractModelBlocks,
|
|
3250
3778
|
extractModelNames,
|
|
3779
|
+
findDbCatalogBlocks,
|
|
3251
3780
|
findFkPointers,
|
|
3781
|
+
findMovedSettings,
|
|
3252
3782
|
foreignKeyOf,
|
|
3253
3783
|
hasBackRelation,
|
|
3254
3784
|
hasConstraints,
|
|
3255
3785
|
isNoLongerPublic,
|
|
3256
3786
|
isOneToOne,
|
|
3787
|
+
judgeModuleResolution,
|
|
3257
3788
|
kebabCase,
|
|
3258
3789
|
migrationCreatedBy,
|
|
3259
3790
|
minimumQuotasPerPlan,
|
|
@@ -3267,13 +3798,16 @@ UserCommands = _ts_decorate14([
|
|
|
3267
3798
|
patchAppModule,
|
|
3268
3799
|
patchOptionsFor,
|
|
3269
3800
|
planInit,
|
|
3270
|
-
projectKeyPattern,
|
|
3271
3801
|
quotaKeyPattern,
|
|
3802
|
+
readEffectiveModuleResolution,
|
|
3272
3803
|
relationNameOf,
|
|
3804
|
+
removeProjectKey,
|
|
3273
3805
|
reportConstraints,
|
|
3274
3806
|
rewriteImports,
|
|
3807
|
+
rewriteManifest,
|
|
3275
3808
|
rewriteNames,
|
|
3276
3809
|
rewriteSubpath,
|
|
3810
|
+
settingsWrittenTo,
|
|
3277
3811
|
stripLineComment,
|
|
3278
3812
|
structuralOnly,
|
|
3279
3813
|
tablesAddressedBy
|