@noormdev/sdk 1.0.0-alpha.33 → 1.0.0-alpha.35
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/{chunk-DE3NJ22A.js → chunk-WFVWNXTV.js} +3 -3
- package/dist/chunk-WFVWNXTV.js.map +1 -0
- package/dist/index.d.ts +43 -3
- package/dist/index.js +588 -302
- package/dist/index.js.map +1 -1
- package/dist/{storage-WQURRPCA.js → storage-HVPYW65B.js} +3 -3
- package/dist/{storage-WQURRPCA.js.map → storage-HVPYW65B.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-DE3NJ22A.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createRequire } from 'module';
|
|
2
2
|
import { processFile, DtReader, isTemplate, FORMAT_VERSION, DT_EXTENSIONS, encryptWithPassphrase, ENCODED_TYPES } from './chunk-5AYPF5D5.js';
|
|
3
3
|
import './chunk-UOFJT5PY.js';
|
|
4
|
-
import { initializeVault, getVaultStatus, setVaultSecret, getVaultSecret, getAllVaultSecrets, listVaultSecretKeys, deleteVaultSecret, vaultSecretExists, getVaultKey, noormDb, getNoormTables, encryptVaultKey } from './chunk-
|
|
5
|
-
import { makeNestedConfig, gigabytes, observer, attempt, retry, clone, merge,
|
|
4
|
+
import { initializeVault, getVaultStatus, setVaultSecret, getVaultSecret, getAllVaultSecrets, listVaultSecretKeys, deleteVaultSecret, vaultSecretExists, getVaultKey, noormDb, getNoormTables, encryptVaultKey } from './chunk-WFVWNXTV.js';
|
|
5
|
+
import { makeNestedConfig, gigabytes, observer, attempt, attemptSync, retry, clone, merge, ObserverRelay } from './chunk-DLQES4PM.js';
|
|
6
6
|
import { dist_default } from './chunk-TBRUDUBQ.js';
|
|
7
7
|
import { require_dist } from './chunk-YJBKNUTP.js';
|
|
8
8
|
import { __commonJS, __export, __toESM } from './chunk-D2XW2UOP.js';
|
|
@@ -10,7 +10,7 @@ import { constants, existsSync, readFileSync, mkdirSync, writeFileSync, createWr
|
|
|
10
10
|
import path, { join, dirname, resolve } from 'path';
|
|
11
11
|
import { homedir, availableParallelism, userInfo } from 'os';
|
|
12
12
|
import { stat, mkdir, writeFile, unlink, rename, rm, readdir, readFile, access } from 'fs/promises';
|
|
13
|
-
import { createHash, createDecipheriv, randomBytes, createCipheriv, randomUUID, hkdfSync } from 'crypto';
|
|
13
|
+
import { createHash, createPrivateKey, createPublicKey, createDecipheriv, randomBytes, createCipheriv, randomUUID, hkdfSync } from 'crypto';
|
|
14
14
|
import { execSync } from 'child_process';
|
|
15
15
|
import { sql } from 'kysely';
|
|
16
16
|
import { Worker, parentPort, workerData } from 'worker_threads';
|
|
@@ -304,6 +304,9 @@ var PRIVATE_KEY_PATH = join(NOORM_HOME, "identity.key");
|
|
|
304
304
|
var PUBLIC_KEY_PATH = join(NOORM_HOME, "identity.pub");
|
|
305
305
|
var IDENTITY_METADATA_PATH = join(NOORM_HOME, "identity.json");
|
|
306
306
|
async function loadIdentityMetadata() {
|
|
307
|
+
if (identityOverride) {
|
|
308
|
+
return identityOverride;
|
|
309
|
+
}
|
|
307
310
|
const [content, err] = await attempt(
|
|
308
311
|
() => readFile(IDENTITY_METADATA_PATH, { encoding: "utf8" })
|
|
309
312
|
);
|
|
@@ -320,6 +323,9 @@ async function loadIdentityMetadata() {
|
|
|
320
323
|
return parsed;
|
|
321
324
|
}
|
|
322
325
|
async function loadPrivateKey() {
|
|
326
|
+
if (keyOverride) {
|
|
327
|
+
return keyOverride;
|
|
328
|
+
}
|
|
323
329
|
const [content, err] = await attempt(() => readFile(PRIVATE_KEY_PATH, { encoding: "utf8" }));
|
|
324
330
|
if (err) {
|
|
325
331
|
if (err.code === "ENOENT") {
|
|
@@ -339,6 +345,34 @@ async function loadPublicKey() {
|
|
|
339
345
|
}
|
|
340
346
|
return content.trim();
|
|
341
347
|
}
|
|
348
|
+
function isValidKeyHex(hex4) {
|
|
349
|
+
if (!/^[0-9a-f]+$/i.test(hex4)) {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
const validLengths = [88, 96];
|
|
353
|
+
return validLengths.includes(hex4.length);
|
|
354
|
+
}
|
|
355
|
+
var keyOverride = null;
|
|
356
|
+
function setKeyOverride(key) {
|
|
357
|
+
keyOverride = key;
|
|
358
|
+
}
|
|
359
|
+
var identityOverride = null;
|
|
360
|
+
function setIdentityOverride(identity) {
|
|
361
|
+
identityOverride = identity;
|
|
362
|
+
}
|
|
363
|
+
function derivePublicKeyFromPrivate(privateKeyHex) {
|
|
364
|
+
const privateKeyObj = createPrivateKey({
|
|
365
|
+
key: Buffer.from(privateKeyHex, "hex"),
|
|
366
|
+
format: "der",
|
|
367
|
+
type: "pkcs8"
|
|
368
|
+
});
|
|
369
|
+
const publicKeyObj = createPublicKey(privateKeyObj);
|
|
370
|
+
const publicKeyDer = publicKeyObj.export({
|
|
371
|
+
type: "spki",
|
|
372
|
+
format: "der"
|
|
373
|
+
});
|
|
374
|
+
return publicKeyDer.toString("hex");
|
|
375
|
+
}
|
|
342
376
|
function deriveStateKey(privateKey) {
|
|
343
377
|
const privateKeyBuffer = Buffer.from(privateKey, "hex");
|
|
344
378
|
return Buffer.from(
|
|
@@ -14866,199 +14900,26 @@ function getEffectiveBuildPaths(buildInclude, buildExclude, rules, config2) {
|
|
|
14866
14900
|
|
|
14867
14901
|
// src/core/settings/manager.ts
|
|
14868
14902
|
var import_yaml = __toESM(require_dist(), 1);
|
|
14869
|
-
|
|
14870
|
-
// src/core/config/schema.ts
|
|
14871
|
-
var DialectSchema2 = external_exports.enum(["postgres", "mysql", "sqlite", "mssql"]);
|
|
14872
|
-
var ConfigNameSchema = external_exports.string().min(1, "Config name is required").regex(
|
|
14873
|
-
/^[a-z0-9_-]+$/i,
|
|
14874
|
-
"Config name must contain only letters, numbers, hyphens, and underscores"
|
|
14875
|
-
);
|
|
14876
|
-
var PortSchema2 = external_exports.number().int().min(1, "Port must be at least 1").max(65535, "Port must be at most 65535");
|
|
14877
|
-
var PoolSchema = external_exports.object({
|
|
14878
|
-
min: external_exports.number().int().min(0).optional(),
|
|
14879
|
-
max: external_exports.number().int().min(1).optional()
|
|
14880
|
-
});
|
|
14881
|
-
var SSLSchema = external_exports.union([
|
|
14882
|
-
external_exports.boolean(),
|
|
14883
|
-
external_exports.object({
|
|
14884
|
-
rejectUnauthorized: external_exports.boolean().optional(),
|
|
14885
|
-
ca: external_exports.string().optional(),
|
|
14886
|
-
cert: external_exports.string().optional(),
|
|
14887
|
-
key: external_exports.string().optional()
|
|
14888
|
-
})
|
|
14889
|
-
]);
|
|
14890
|
-
var ConnectionSchema = external_exports.object({
|
|
14891
|
-
dialect: DialectSchema2,
|
|
14892
|
-
host: external_exports.string().optional(),
|
|
14893
|
-
port: PortSchema2.optional(),
|
|
14894
|
-
database: external_exports.string().min(1, "Database name is required"),
|
|
14895
|
-
filename: external_exports.string().optional(),
|
|
14896
|
-
user: external_exports.string().optional(),
|
|
14897
|
-
password: external_exports.string().optional(),
|
|
14898
|
-
ssl: SSLSchema.optional(),
|
|
14899
|
-
pool: PoolSchema.optional()
|
|
14900
|
-
}).refine((conn) => conn.dialect === "sqlite" || conn.host, {
|
|
14901
|
-
message: "Host is required for non-SQLite databases",
|
|
14902
|
-
path: ["host"]
|
|
14903
|
-
});
|
|
14904
|
-
var ConfigSchema = external_exports.object({
|
|
14905
|
-
name: ConfigNameSchema,
|
|
14906
|
-
type: external_exports.enum(["local", "remote"]).default("local"),
|
|
14907
|
-
isTest: external_exports.boolean().default(false),
|
|
14908
|
-
protected: external_exports.boolean().default(false),
|
|
14909
|
-
connection: ConnectionSchema,
|
|
14910
|
-
identity: external_exports.string().optional()
|
|
14911
|
-
});
|
|
14912
|
-
var PartialConnectionSchema = external_exports.object({
|
|
14913
|
-
dialect: DialectSchema2.optional(),
|
|
14914
|
-
host: external_exports.string().optional(),
|
|
14915
|
-
port: PortSchema2.optional(),
|
|
14916
|
-
database: external_exports.string().optional(),
|
|
14917
|
-
filename: external_exports.string().optional(),
|
|
14918
|
-
user: external_exports.string().optional(),
|
|
14919
|
-
password: external_exports.string().optional(),
|
|
14920
|
-
ssl: SSLSchema.optional(),
|
|
14921
|
-
pool: PoolSchema.optional()
|
|
14922
|
-
});
|
|
14923
|
-
external_exports.object({
|
|
14924
|
-
name: external_exports.string().regex(/^[a-z0-9_-]+$/i).optional(),
|
|
14925
|
-
type: external_exports.enum(["local", "remote"]).optional(),
|
|
14926
|
-
isTest: external_exports.boolean().optional(),
|
|
14927
|
-
protected: external_exports.boolean().optional(),
|
|
14928
|
-
connection: PartialConnectionSchema.optional(),
|
|
14929
|
-
identity: external_exports.string().optional()
|
|
14930
|
-
});
|
|
14931
|
-
ConfigSchema.extend({
|
|
14932
|
-
name: ConfigNameSchema.optional()
|
|
14933
|
-
});
|
|
14934
|
-
var ConfigValidationError = class extends Error {
|
|
14935
|
-
constructor(message, field, issues) {
|
|
14936
|
-
super(message);
|
|
14937
|
-
this.field = field;
|
|
14938
|
-
this.issues = issues;
|
|
14939
|
-
this.name = "ConfigValidationError";
|
|
14940
|
-
}
|
|
14941
|
-
};
|
|
14942
|
-
function parseConfig(config2) {
|
|
14943
|
-
const result = ConfigSchema.safeParse(config2);
|
|
14944
|
-
if (!result.success) {
|
|
14945
|
-
const firstIssue = result.error.issues[0];
|
|
14946
|
-
throw new ConfigValidationError(
|
|
14947
|
-
firstIssue?.message ?? "Validation failed",
|
|
14948
|
-
firstIssue?.path.join(".") || "unknown",
|
|
14949
|
-
result.error.issues
|
|
14950
|
-
);
|
|
14951
|
-
}
|
|
14952
|
-
return result.data;
|
|
14953
|
-
}
|
|
14954
|
-
|
|
14955
|
-
// src/core/environment.ts
|
|
14956
|
-
function getEnvConfigName() {
|
|
14957
|
-
return process.env["NOORM_CONFIG"];
|
|
14958
|
-
}
|
|
14959
|
-
|
|
14960
|
-
// src/core/config/resolver.ts
|
|
14961
|
-
var SettingsProvider = class {
|
|
14962
|
-
#manager;
|
|
14963
|
-
constructor(manager) {
|
|
14964
|
-
this.#manager = manager;
|
|
14965
|
-
}
|
|
14966
|
-
/** Get a stage by name */
|
|
14967
|
-
getStage(name) {
|
|
14968
|
-
return this.#manager.getStage(name) ?? null;
|
|
14969
|
-
}
|
|
14970
|
-
/** Get stage that matches a config name (for auto-linking) */
|
|
14971
|
-
findStageForConfig(configName) {
|
|
14972
|
-
return this.#manager.getStage(configName) ?? null;
|
|
14973
|
-
}
|
|
14974
|
-
};
|
|
14975
|
-
var DEFAULTS = {
|
|
14976
|
-
type: "local",
|
|
14977
|
-
isTest: false,
|
|
14978
|
-
protected: false,
|
|
14979
|
-
connection: {
|
|
14980
|
-
host: "localhost",
|
|
14981
|
-
pool: { min: 0, max: 10 }
|
|
14982
|
-
},
|
|
14983
|
-
log: {
|
|
14984
|
-
level: "info"
|
|
14985
|
-
}
|
|
14986
|
-
};
|
|
14987
|
-
function resolveConfig(state, options = {}) {
|
|
14988
|
-
const configName = options.name ?? getEnvConfigName() ?? state.getActiveConfigName();
|
|
14989
|
-
if (!configName) {
|
|
14990
|
-
const envConfig2 = getEnvConfig();
|
|
14991
|
-
if (envConfig2.connection?.dialect && envConfig2.connection?.database) {
|
|
14992
|
-
return resolveFromEnvOnly(envConfig2, options.flags, options.stage, options.settings);
|
|
14993
|
-
}
|
|
14994
|
-
return null;
|
|
14995
|
-
}
|
|
14996
|
-
const stored = state.getConfig(configName);
|
|
14997
|
-
if (!stored) {
|
|
14998
|
-
throw new Error(`Config "${configName}" not found`);
|
|
14999
|
-
}
|
|
15000
|
-
const stageDefaults = getStageDefaults(configName, options.stage, options.settings);
|
|
15001
|
-
const envConfig = getEnvConfig();
|
|
15002
|
-
let merged = clone(DEFAULTS);
|
|
15003
|
-
if (stageDefaults) {
|
|
15004
|
-
merged = merge(merged, stageDefaults);
|
|
15005
|
-
}
|
|
15006
|
-
merged = merge(merged, stored);
|
|
15007
|
-
merged = merge(merged, envConfig);
|
|
15008
|
-
merged = merge(merged, options.flags ?? {});
|
|
15009
|
-
return parseConfig(merged);
|
|
15010
|
-
}
|
|
15011
|
-
function getStageDefaults(configName, stageName, settings) {
|
|
15012
|
-
if (!settings) return null;
|
|
15013
|
-
if (stageName) return settings.getStage(stageName)?.defaults ?? null;
|
|
15014
|
-
return settings.findStageForConfig(configName)?.defaults ?? null;
|
|
15015
|
-
}
|
|
15016
|
-
function resolveFromEnvOnly(envConfig, flags, stageName, settings) {
|
|
15017
|
-
const name = envConfig.name ?? flags?.name ?? "__env__";
|
|
15018
|
-
const stageDefaults = getStageDefaults(name, stageName, settings);
|
|
15019
|
-
let merged = clone(DEFAULTS);
|
|
15020
|
-
if (stageDefaults) {
|
|
15021
|
-
merged = merge(merged, stageDefaults);
|
|
15022
|
-
}
|
|
15023
|
-
merged = merge(merged, envConfig);
|
|
15024
|
-
merged = merge(merged, flags ?? {});
|
|
15025
|
-
if (!merged.name) {
|
|
15026
|
-
merged.name = "__env__";
|
|
15027
|
-
}
|
|
15028
|
-
return parseConfig(merged);
|
|
15029
|
-
}
|
|
15030
|
-
|
|
15031
|
-
// src/core/config/index.ts
|
|
15032
|
-
var META_ENV_VARS = /* @__PURE__ */ new Set([
|
|
14903
|
+
var META_SETTINGS_ENV_VARS = /* @__PURE__ */ new Set([
|
|
15033
14904
|
"NOORM_CONFIG",
|
|
15034
|
-
// Config selection
|
|
15035
14905
|
"NOORM_YES",
|
|
15036
|
-
|
|
15037
|
-
"
|
|
15038
|
-
|
|
14906
|
+
"NOORM_JSON",
|
|
14907
|
+
"NOORM_HEADLESS",
|
|
14908
|
+
"NOORM_DEBUG",
|
|
14909
|
+
"NOORM_DEV",
|
|
14910
|
+
"NOORM_CI_CONFIG_NAME",
|
|
14911
|
+
"NOORM_LOGGER_DEBUG",
|
|
14912
|
+
"NOORM_IDENTITY"
|
|
15039
14913
|
]);
|
|
15040
|
-
|
|
15041
|
-
|
|
15042
|
-
|
|
15043
|
-
|
|
15044
|
-
|
|
15045
|
-
|
|
15046
|
-
|
|
15047
|
-
|
|
15048
|
-
function getEnvConfig() {
|
|
15049
|
-
const config2 = allConfigs();
|
|
15050
|
-
if (config2.connection?.dialect) {
|
|
15051
|
-
const result = DialectSchema2.safeParse(config2.connection.dialect);
|
|
15052
|
-
if (!result.success) {
|
|
15053
|
-
throw new Error(
|
|
15054
|
-
`Invalid NOORM_CONNECTION_DIALECT: must be one of ${VALID_DIALECTS.join(", ")}`
|
|
15055
|
-
);
|
|
15056
|
-
}
|
|
15057
|
-
}
|
|
15058
|
-
return config2;
|
|
14914
|
+
function allSettingsEnv() {
|
|
14915
|
+
const { allConfigs: allConfigs2 } = makeNestedConfig(process.env, {
|
|
14916
|
+
filter: (key) => key.startsWith("NOORM_") && !META_SETTINGS_ENV_VARS.has(key) && !key.startsWith("NOORM_IDENTITY_") && !key.startsWith("NOORM_CONNECTION_"),
|
|
14917
|
+
stripPrefix: "NOORM_",
|
|
14918
|
+
forceAllCapToLower: true,
|
|
14919
|
+
memoizeOpts: false
|
|
14920
|
+
});
|
|
14921
|
+
return allConfigs2();
|
|
15059
14922
|
}
|
|
15060
|
-
|
|
15061
|
-
// src/core/settings/manager.ts
|
|
15062
14923
|
var SettingsManager = class {
|
|
15063
14924
|
#projectRoot;
|
|
15064
14925
|
#settingsDir;
|
|
@@ -15134,7 +14995,7 @@ var SettingsManager = class {
|
|
|
15134
14995
|
}
|
|
15135
14996
|
fromFile = true;
|
|
15136
14997
|
}
|
|
15137
|
-
this.#settings = merge(this.#settings,
|
|
14998
|
+
this.#settings = merge(this.#settings, allSettingsEnv());
|
|
15138
14999
|
this.#loaded = true;
|
|
15139
15000
|
observer.emit("settings:loaded", {
|
|
15140
15001
|
path: this.settingsFilePath,
|
|
@@ -15718,6 +15579,13 @@ function formatIdentity(identity) {
|
|
|
15718
15579
|
}
|
|
15719
15580
|
return identity.name;
|
|
15720
15581
|
}
|
|
15582
|
+
function computeIdentityHash(input) {
|
|
15583
|
+
if (!input.email || !input.name || !input.machine || false) {
|
|
15584
|
+
throw new Error("All fields required for identity hash: email, name, machine, os");
|
|
15585
|
+
}
|
|
15586
|
+
const hashInput = [input.email, input.name, input.machine, input.os].join("\0");
|
|
15587
|
+
return createHash("sha256").update(hashInput, "utf8").digest("hex");
|
|
15588
|
+
}
|
|
15721
15589
|
|
|
15722
15590
|
// src/core/identity/factory.ts
|
|
15723
15591
|
async function loadExistingIdentity() {
|
|
@@ -16638,6 +16506,11 @@ function createDialectQuoting(opts) {
|
|
|
16638
16506
|
return { quote: quote5, qualifiedName: qualifiedName4 };
|
|
16639
16507
|
}
|
|
16640
16508
|
|
|
16509
|
+
// src/core/environment.ts
|
|
16510
|
+
function getEnvConfigName() {
|
|
16511
|
+
return process.env["NOORM_CONFIG"];
|
|
16512
|
+
}
|
|
16513
|
+
|
|
16641
16514
|
// src/core/logger/logger.ts
|
|
16642
16515
|
__toESM(require_dayjs_min(), 1);
|
|
16643
16516
|
|
|
@@ -16765,6 +16638,50 @@ async function registerIdentity(db, identity, dialect) {
|
|
|
16765
16638
|
return { ok: true, registered: true };
|
|
16766
16639
|
}
|
|
16767
16640
|
|
|
16641
|
+
// src/core/identity/env.ts
|
|
16642
|
+
var CI_ENV_VARS = {
|
|
16643
|
+
privateKey: "NOORM_IDENTITY_PRIVATE_KEY",
|
|
16644
|
+
name: "NOORM_IDENTITY_NAME",
|
|
16645
|
+
email: "NOORM_IDENTITY_EMAIL"
|
|
16646
|
+
};
|
|
16647
|
+
function loadIdentityFromEnv() {
|
|
16648
|
+
const rawKey = process.env[CI_ENV_VARS.privateKey];
|
|
16649
|
+
const rawName = process.env[CI_ENV_VARS.name];
|
|
16650
|
+
const rawEmail = process.env[CI_ENV_VARS.email];
|
|
16651
|
+
if (!rawKey || !rawName || !rawEmail) {
|
|
16652
|
+
return null;
|
|
16653
|
+
}
|
|
16654
|
+
const privateKey = rawKey.trim();
|
|
16655
|
+
const name = rawName.trim();
|
|
16656
|
+
const email3 = rawEmail.trim();
|
|
16657
|
+
if (!name || !email3) {
|
|
16658
|
+
return null;
|
|
16659
|
+
}
|
|
16660
|
+
if (!isValidKeyHex(privateKey)) {
|
|
16661
|
+
return null;
|
|
16662
|
+
}
|
|
16663
|
+
const [publicKey, deriveErr] = attemptSync(() => derivePublicKeyFromPrivate(privateKey));
|
|
16664
|
+
if (deriveErr || !publicKey) {
|
|
16665
|
+
return null;
|
|
16666
|
+
}
|
|
16667
|
+
const identityHash = computeIdentityHash({
|
|
16668
|
+
email: email3,
|
|
16669
|
+
name,
|
|
16670
|
+
machine: publicKey,
|
|
16671
|
+
os: "env"
|
|
16672
|
+
});
|
|
16673
|
+
const identity = {
|
|
16674
|
+
identityHash,
|
|
16675
|
+
name,
|
|
16676
|
+
email: email3,
|
|
16677
|
+
publicKey,
|
|
16678
|
+
machine: "ci",
|
|
16679
|
+
os: "env",
|
|
16680
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16681
|
+
};
|
|
16682
|
+
return { identity, privateKey, source: "env" };
|
|
16683
|
+
}
|
|
16684
|
+
|
|
16768
16685
|
// src/core/identity/index.ts
|
|
16769
16686
|
var cachedIdentity = null;
|
|
16770
16687
|
function resolveIdentity2(options = {}) {
|
|
@@ -16791,6 +16708,205 @@ async function waitForIdentityToLoad(db, dialect) {
|
|
|
16791
16708
|
});
|
|
16792
16709
|
}
|
|
16793
16710
|
|
|
16711
|
+
// src/core/config/schema.ts
|
|
16712
|
+
var DialectSchema2 = external_exports.enum(["postgres", "mysql", "sqlite", "mssql"]);
|
|
16713
|
+
var ConfigNameSchema = external_exports.string().min(1, "Config name is required").regex(
|
|
16714
|
+
/^[a-z0-9_-]+$/i,
|
|
16715
|
+
"Config name must contain only letters, numbers, hyphens, and underscores"
|
|
16716
|
+
);
|
|
16717
|
+
var PortSchema2 = external_exports.number().int().min(1, "Port must be at least 1").max(65535, "Port must be at most 65535");
|
|
16718
|
+
var PoolSchema = external_exports.object({
|
|
16719
|
+
min: external_exports.number().int().min(0).optional(),
|
|
16720
|
+
max: external_exports.number().int().min(1).optional()
|
|
16721
|
+
});
|
|
16722
|
+
var SSLSchema = external_exports.union([
|
|
16723
|
+
external_exports.boolean(),
|
|
16724
|
+
external_exports.object({
|
|
16725
|
+
rejectUnauthorized: external_exports.boolean().optional(),
|
|
16726
|
+
ca: external_exports.string().optional(),
|
|
16727
|
+
cert: external_exports.string().optional(),
|
|
16728
|
+
key: external_exports.string().optional()
|
|
16729
|
+
})
|
|
16730
|
+
]);
|
|
16731
|
+
var ConnectionSchema = external_exports.object({
|
|
16732
|
+
dialect: DialectSchema2,
|
|
16733
|
+
host: external_exports.string().optional(),
|
|
16734
|
+
port: PortSchema2.optional(),
|
|
16735
|
+
database: external_exports.string().min(1, "Database name is required"),
|
|
16736
|
+
filename: external_exports.string().optional(),
|
|
16737
|
+
user: external_exports.string().optional(),
|
|
16738
|
+
password: external_exports.string().optional(),
|
|
16739
|
+
ssl: SSLSchema.optional(),
|
|
16740
|
+
pool: PoolSchema.optional()
|
|
16741
|
+
}).refine((conn) => conn.dialect === "sqlite" || conn.host, {
|
|
16742
|
+
message: "Host is required for non-SQLite databases",
|
|
16743
|
+
path: ["host"]
|
|
16744
|
+
});
|
|
16745
|
+
var ConfigSchema = external_exports.object({
|
|
16746
|
+
name: ConfigNameSchema,
|
|
16747
|
+
type: external_exports.enum(["local", "remote"]).default("local"),
|
|
16748
|
+
isTest: external_exports.boolean().default(false),
|
|
16749
|
+
protected: external_exports.boolean().default(false),
|
|
16750
|
+
connection: ConnectionSchema,
|
|
16751
|
+
identity: external_exports.string().optional()
|
|
16752
|
+
});
|
|
16753
|
+
var PartialConnectionSchema = external_exports.object({
|
|
16754
|
+
dialect: DialectSchema2.optional(),
|
|
16755
|
+
host: external_exports.string().optional(),
|
|
16756
|
+
port: PortSchema2.optional(),
|
|
16757
|
+
database: external_exports.string().optional(),
|
|
16758
|
+
filename: external_exports.string().optional(),
|
|
16759
|
+
user: external_exports.string().optional(),
|
|
16760
|
+
password: external_exports.string().optional(),
|
|
16761
|
+
ssl: SSLSchema.optional(),
|
|
16762
|
+
pool: PoolSchema.optional()
|
|
16763
|
+
});
|
|
16764
|
+
external_exports.object({
|
|
16765
|
+
name: external_exports.string().regex(/^[a-z0-9_-]+$/i).optional(),
|
|
16766
|
+
type: external_exports.enum(["local", "remote"]).optional(),
|
|
16767
|
+
isTest: external_exports.boolean().optional(),
|
|
16768
|
+
protected: external_exports.boolean().optional(),
|
|
16769
|
+
connection: PartialConnectionSchema.optional(),
|
|
16770
|
+
identity: external_exports.string().optional()
|
|
16771
|
+
});
|
|
16772
|
+
ConfigSchema.extend({
|
|
16773
|
+
name: ConfigNameSchema.optional()
|
|
16774
|
+
});
|
|
16775
|
+
var ConfigValidationError = class extends Error {
|
|
16776
|
+
constructor(message, field, issues) {
|
|
16777
|
+
super(message);
|
|
16778
|
+
this.field = field;
|
|
16779
|
+
this.issues = issues;
|
|
16780
|
+
this.name = "ConfigValidationError";
|
|
16781
|
+
}
|
|
16782
|
+
};
|
|
16783
|
+
function parseConfig(config2) {
|
|
16784
|
+
const result = ConfigSchema.safeParse(config2);
|
|
16785
|
+
if (!result.success) {
|
|
16786
|
+
const firstIssue = result.error.issues[0];
|
|
16787
|
+
throw new ConfigValidationError(
|
|
16788
|
+
firstIssue?.message ?? "Validation failed",
|
|
16789
|
+
firstIssue?.path.join(".") || "unknown",
|
|
16790
|
+
result.error.issues
|
|
16791
|
+
);
|
|
16792
|
+
}
|
|
16793
|
+
return result.data;
|
|
16794
|
+
}
|
|
16795
|
+
|
|
16796
|
+
// src/core/config/index.ts
|
|
16797
|
+
var META_ENV_VARS = /* @__PURE__ */ new Set([
|
|
16798
|
+
"NOORM_CONFIG",
|
|
16799
|
+
// Config selection
|
|
16800
|
+
"NOORM_YES",
|
|
16801
|
+
// Skip confirmations
|
|
16802
|
+
"NOORM_JSON",
|
|
16803
|
+
// JSON output mode
|
|
16804
|
+
"NOORM_HEADLESS",
|
|
16805
|
+
// Headless mode detection
|
|
16806
|
+
"NOORM_DEBUG",
|
|
16807
|
+
// Debug logging
|
|
16808
|
+
"NOORM_DEV",
|
|
16809
|
+
// Dev mode detection
|
|
16810
|
+
"NOORM_CI_CONFIG_NAME",
|
|
16811
|
+
// ci init config name override
|
|
16812
|
+
"NOORM_LOGGER_DEBUG"
|
|
16813
|
+
// Logger-internal debug
|
|
16814
|
+
]);
|
|
16815
|
+
var NON_CONFIG_ENV_PREFIXES = [
|
|
16816
|
+
"NOORM_IDENTITY_"
|
|
16817
|
+
];
|
|
16818
|
+
var VALID_DIALECTS = ["postgres", "mysql", "sqlite", "mssql"];
|
|
16819
|
+
var { allConfigs, getConfig } = makeNestedConfig(process.env, {
|
|
16820
|
+
filter: (key) => key.startsWith("NOORM_") && !META_ENV_VARS.has(key) && !NON_CONFIG_ENV_PREFIXES.some((prefix) => key.startsWith(prefix)),
|
|
16821
|
+
stripPrefix: "NOORM_",
|
|
16822
|
+
forceAllCapToLower: true,
|
|
16823
|
+
memoizeOpts: false,
|
|
16824
|
+
skipConversion: (key) => key.toLowerCase().includes("password")
|
|
16825
|
+
});
|
|
16826
|
+
function getEnvConfig() {
|
|
16827
|
+
const config2 = allConfigs();
|
|
16828
|
+
if (config2.connection?.dialect) {
|
|
16829
|
+
const result = DialectSchema2.safeParse(config2.connection.dialect);
|
|
16830
|
+
if (!result.success) {
|
|
16831
|
+
throw new Error(
|
|
16832
|
+
`Invalid NOORM_CONNECTION_DIALECT: must be one of ${VALID_DIALECTS.join(", ")}`
|
|
16833
|
+
);
|
|
16834
|
+
}
|
|
16835
|
+
}
|
|
16836
|
+
return config2;
|
|
16837
|
+
}
|
|
16838
|
+
|
|
16839
|
+
// src/core/config/resolver.ts
|
|
16840
|
+
var SettingsProvider = class {
|
|
16841
|
+
#manager;
|
|
16842
|
+
constructor(manager) {
|
|
16843
|
+
this.#manager = manager;
|
|
16844
|
+
}
|
|
16845
|
+
/** Get a stage by name */
|
|
16846
|
+
getStage(name) {
|
|
16847
|
+
return this.#manager.getStage(name) ?? null;
|
|
16848
|
+
}
|
|
16849
|
+
/** Get stage that matches a config name (for auto-linking) */
|
|
16850
|
+
findStageForConfig(configName) {
|
|
16851
|
+
return this.#manager.getStage(configName) ?? null;
|
|
16852
|
+
}
|
|
16853
|
+
};
|
|
16854
|
+
var DEFAULTS = {
|
|
16855
|
+
type: "local",
|
|
16856
|
+
isTest: false,
|
|
16857
|
+
protected: false,
|
|
16858
|
+
connection: {
|
|
16859
|
+
host: "localhost",
|
|
16860
|
+
pool: { min: 0, max: 10 }
|
|
16861
|
+
},
|
|
16862
|
+
log: {
|
|
16863
|
+
level: "info"
|
|
16864
|
+
}
|
|
16865
|
+
};
|
|
16866
|
+
function resolveConfig(state, options = {}) {
|
|
16867
|
+
const configName = options.name ?? getEnvConfigName() ?? state.getActiveConfigName();
|
|
16868
|
+
if (!configName) {
|
|
16869
|
+
const envConfig2 = getEnvConfig();
|
|
16870
|
+
if (envConfig2.connection?.dialect && envConfig2.connection?.database) {
|
|
16871
|
+
return resolveFromEnvOnly(envConfig2, options.flags, options.stage, options.settings);
|
|
16872
|
+
}
|
|
16873
|
+
return null;
|
|
16874
|
+
}
|
|
16875
|
+
const stored = state.getConfig(configName);
|
|
16876
|
+
if (!stored) {
|
|
16877
|
+
throw new Error(`Config "${configName}" not found`);
|
|
16878
|
+
}
|
|
16879
|
+
const stageDefaults = getStageDefaults(configName, options.stage, options.settings);
|
|
16880
|
+
const envConfig = getEnvConfig();
|
|
16881
|
+
let merged = clone(DEFAULTS);
|
|
16882
|
+
if (stageDefaults) {
|
|
16883
|
+
merged = merge(merged, stageDefaults);
|
|
16884
|
+
}
|
|
16885
|
+
merged = merge(merged, stored);
|
|
16886
|
+
merged = merge(merged, envConfig);
|
|
16887
|
+
merged = merge(merged, options.flags ?? {});
|
|
16888
|
+
return parseConfig(merged);
|
|
16889
|
+
}
|
|
16890
|
+
function getStageDefaults(configName, stageName, settings) {
|
|
16891
|
+
if (!settings) return null;
|
|
16892
|
+
if (stageName) return settings.getStage(stageName)?.defaults ?? null;
|
|
16893
|
+
return settings.findStageForConfig(configName)?.defaults ?? null;
|
|
16894
|
+
}
|
|
16895
|
+
function resolveFromEnvOnly(envConfig, flags, stageName, settings) {
|
|
16896
|
+
const name = envConfig.name ?? flags?.name ?? "__env__";
|
|
16897
|
+
const stageDefaults = getStageDefaults(name, stageName, settings);
|
|
16898
|
+
let merged = clone(DEFAULTS);
|
|
16899
|
+
if (stageDefaults) {
|
|
16900
|
+
merged = merge(merged, stageDefaults);
|
|
16901
|
+
}
|
|
16902
|
+
merged = merge(merged, envConfig);
|
|
16903
|
+
merged = merge(merged, flags ?? {});
|
|
16904
|
+
if (!merged.name) {
|
|
16905
|
+
merged.name = "__env__";
|
|
16906
|
+
}
|
|
16907
|
+
return parseConfig(merged);
|
|
16908
|
+
}
|
|
16909
|
+
|
|
16794
16910
|
// src/sdk/tvp.ts
|
|
16795
16911
|
var MSSQL_PARAM_LIMIT = 2100;
|
|
16796
16912
|
function tvp(typeName, rows) {
|
|
@@ -16822,6 +16938,16 @@ function isTvp(value) {
|
|
|
16822
16938
|
}
|
|
16823
16939
|
|
|
16824
16940
|
// src/sdk/sql.ts
|
|
16941
|
+
function quoteIdent(dialect, name) {
|
|
16942
|
+
const parts = name.includes(".") ? name.split(".", 2) : [name];
|
|
16943
|
+
if (dialect === "postgres") {
|
|
16944
|
+
return parts.map((p) => '"' + p.replace(/"/g, '""') + '"').join(".");
|
|
16945
|
+
}
|
|
16946
|
+
if (dialect === "mssql") {
|
|
16947
|
+
return parts.map((p) => "[" + p.replace(/]/g, "]]") + "]").join(".");
|
|
16948
|
+
}
|
|
16949
|
+
return parts.map((p) => "`" + p.replace(/`/g, "``") + "`").join(".");
|
|
16950
|
+
}
|
|
16825
16951
|
function buildProcCall(dialect, name, params) {
|
|
16826
16952
|
if (dialect === "sqlite") {
|
|
16827
16953
|
throw new Error("SQLite does not support stored procedures.");
|
|
@@ -16829,7 +16955,7 @@ function buildProcCall(dialect, name, params) {
|
|
|
16829
16955
|
if (params && hasTvpParam(params) && dialect !== "mssql") {
|
|
16830
16956
|
throw new Error("Table-valued parameters (TVP) are only supported on MSSQL.");
|
|
16831
16957
|
}
|
|
16832
|
-
const rawName = sql.raw(name);
|
|
16958
|
+
const rawName = sql.raw(quoteIdent(dialect, name));
|
|
16833
16959
|
if (!params || Array.isArray(params) && params.length === 0 || !Array.isArray(params) && Object.keys(params).length === 0) {
|
|
16834
16960
|
if (dialect === "mssql") {
|
|
16835
16961
|
return sql`EXEC ${rawName}`;
|
|
@@ -16851,8 +16977,8 @@ function buildFuncCall(dialect, name, column, params) {
|
|
|
16851
16977
|
if (params && hasTvpParam(params) && dialect !== "mssql") {
|
|
16852
16978
|
throw new Error("Table-valued parameters (TVP) are only supported on MSSQL.");
|
|
16853
16979
|
}
|
|
16854
|
-
const rawName = sql.raw(name);
|
|
16855
|
-
const rawCol = sql.raw(column);
|
|
16980
|
+
const rawName = sql.raw(quoteIdent(dialect, name));
|
|
16981
|
+
const rawCol = sql.raw(quoteIdent(dialect, column));
|
|
16856
16982
|
if (!params || Array.isArray(params) && params.length === 0 || !Array.isArray(params) && Object.keys(params).length === 0) {
|
|
16857
16983
|
return sql`SELECT ${rawName}() AS ${rawCol}`;
|
|
16858
16984
|
}
|
|
@@ -16861,7 +16987,7 @@ function buildFuncCall(dialect, name, column, params) {
|
|
|
16861
16987
|
}
|
|
16862
16988
|
if (dialect === "postgres" && !Array.isArray(params)) {
|
|
16863
16989
|
const parts = Object.entries(params).map(
|
|
16864
|
-
([key, val]) => sql`${sql.raw(key)} => ${val}`
|
|
16990
|
+
([key, val]) => sql`${sql.raw(quoteIdent("postgres", key))} => ${val}`
|
|
16865
16991
|
);
|
|
16866
16992
|
return sql`SELECT ${rawName}(${sql.join(parts)}) AS ${rawCol}`;
|
|
16867
16993
|
}
|
|
@@ -16888,7 +17014,7 @@ function buildTvfCall(dialect, name, params) {
|
|
|
16888
17014
|
if (params && hasTvpParam(params) && dialect !== "mssql") {
|
|
16889
17015
|
throw new Error("Table-valued parameters (TVP) are only supported on MSSQL.");
|
|
16890
17016
|
}
|
|
16891
|
-
const rawName = sql.raw(name);
|
|
17017
|
+
const rawName = sql.raw(quoteIdent(dialect, name));
|
|
16892
17018
|
if (!params || Array.isArray(params) && params.length === 0 || !Array.isArray(params) && Object.keys(params).length === 0) {
|
|
16893
17019
|
return sql`SELECT * FROM ${rawName}()`;
|
|
16894
17020
|
}
|
|
@@ -16897,7 +17023,7 @@ function buildTvfCall(dialect, name, params) {
|
|
|
16897
17023
|
}
|
|
16898
17024
|
if (dialect === "postgres" && !Array.isArray(params)) {
|
|
16899
17025
|
const parts = Object.entries(params).map(
|
|
16900
|
-
([key, val]) => sql`${sql.raw(key)} => ${val}`
|
|
17026
|
+
([key, val]) => sql`${sql.raw(quoteIdent("postgres", key))} => ${val}`
|
|
16901
17027
|
);
|
|
16902
17028
|
return sql`SELECT * FROM ${rawName}(${sql.join(parts)})`;
|
|
16903
17029
|
}
|
|
@@ -16924,7 +17050,7 @@ function buildPostgresProc(rawName, params) {
|
|
|
16924
17050
|
return sql`CALL ${rawName}(${joined})`;
|
|
16925
17051
|
}
|
|
16926
17052
|
const parts = Object.entries(params).map(
|
|
16927
|
-
([key, val]) => sql`${sql.raw(key)} => ${val}`
|
|
17053
|
+
([key, val]) => sql`${sql.raw(quoteIdent("postgres", key))} => ${val}`
|
|
16928
17054
|
);
|
|
16929
17055
|
return sql`CALL ${rawName}(${sql.join(parts)})`;
|
|
16930
17056
|
}
|
|
@@ -19119,7 +19245,7 @@ async function executeChange(context, change, options = {}) {
|
|
|
19119
19245
|
const [, lockErr] = await attempt(
|
|
19120
19246
|
() => lockManager.acquire(context.db, context.configName, identity, {
|
|
19121
19247
|
reason: `Change: ${change.name}`,
|
|
19122
|
-
dialect: context.dialect
|
|
19248
|
+
dialect: context.dialect ?? "postgres"
|
|
19123
19249
|
})
|
|
19124
19250
|
);
|
|
19125
19251
|
if (lockErr) {
|
|
@@ -19191,7 +19317,7 @@ async function revertChange(context, change, options = {}) {
|
|
|
19191
19317
|
const [, lockErr] = await attempt(
|
|
19192
19318
|
() => lockManager.acquire(context.db, context.configName, identity, {
|
|
19193
19319
|
reason: `Revert: ${change.name}`,
|
|
19194
|
-
dialect: context.dialect
|
|
19320
|
+
dialect: context.dialect ?? "postgres"
|
|
19195
19321
|
})
|
|
19196
19322
|
);
|
|
19197
19323
|
if (lockErr) {
|
|
@@ -20117,9 +20243,16 @@ var ChangesNamespace = class {
|
|
|
20117
20243
|
/**
|
|
20118
20244
|
* Apply a specific change.
|
|
20119
20245
|
*
|
|
20246
|
+
* Pass `dryRun: true` to render the change to `tmp/` without
|
|
20247
|
+
* touching the database, or `preview: true` to emit rendered SQL.
|
|
20248
|
+
*
|
|
20120
20249
|
* @example
|
|
20121
20250
|
* ```typescript
|
|
20122
20251
|
* const result = await ctx.noorm.changes.apply('2024-01-15-add-users')
|
|
20252
|
+
* const dry = await ctx.noorm.changes.apply(
|
|
20253
|
+
* '2024-01-15-add-users',
|
|
20254
|
+
* { dryRun: true },
|
|
20255
|
+
* )
|
|
20123
20256
|
* ```
|
|
20124
20257
|
*/
|
|
20125
20258
|
async apply(name, options) {
|
|
@@ -20128,9 +20261,16 @@ var ChangesNamespace = class {
|
|
|
20128
20261
|
/**
|
|
20129
20262
|
* Revert a specific change.
|
|
20130
20263
|
*
|
|
20264
|
+
* Pass `dryRun: true` to render the revert files to `tmp/` without
|
|
20265
|
+
* touching the database, or `preview: true` to emit rendered SQL.
|
|
20266
|
+
*
|
|
20131
20267
|
* @example
|
|
20132
20268
|
* ```typescript
|
|
20133
20269
|
* const result = await ctx.noorm.changes.revert('2024-01-15-add-users')
|
|
20270
|
+
* const dry = await ctx.noorm.changes.revert(
|
|
20271
|
+
* '2024-01-15-add-users',
|
|
20272
|
+
* { dryRun: true },
|
|
20273
|
+
* )
|
|
20134
20274
|
* ```
|
|
20135
20275
|
*/
|
|
20136
20276
|
async revert(name, options) {
|
|
@@ -20140,25 +20280,37 @@ var ChangesNamespace = class {
|
|
|
20140
20280
|
/**
|
|
20141
20281
|
* Apply all pending changes.
|
|
20142
20282
|
*
|
|
20283
|
+
* Pass `dryRun: true` to render each change to `tmp/` without
|
|
20284
|
+
* touching the database. Pass `preview: true` to emit rendered
|
|
20285
|
+
* SQL (also without DB writes). When omitted, both default to
|
|
20286
|
+
* `false` and changes are applied normally.
|
|
20287
|
+
*
|
|
20143
20288
|
* @example
|
|
20144
20289
|
* ```typescript
|
|
20145
20290
|
* const result = await ctx.noorm.changes.ff()
|
|
20291
|
+
* const dry = await ctx.noorm.changes.ff({ dryRun: true })
|
|
20292
|
+
* const forced = await ctx.noorm.changes.ff({ force: true })
|
|
20146
20293
|
* ```
|
|
20147
20294
|
*/
|
|
20148
|
-
async ff() {
|
|
20149
|
-
return this.#getManager().ff();
|
|
20295
|
+
async ff(options) {
|
|
20296
|
+
return this.#getManager().ff(options);
|
|
20150
20297
|
}
|
|
20151
20298
|
/**
|
|
20152
20299
|
* Apply the next N pending changes (default 1).
|
|
20153
20300
|
*
|
|
20301
|
+
* Pass `dryRun: true` to render the changes to `tmp/` without
|
|
20302
|
+
* touching the database. Pass `preview: true` to emit rendered
|
|
20303
|
+
* SQL (also without DB writes).
|
|
20304
|
+
*
|
|
20154
20305
|
* @example
|
|
20155
20306
|
* ```typescript
|
|
20156
20307
|
* const result = await ctx.noorm.changes.next()
|
|
20157
20308
|
* const result = await ctx.noorm.changes.next(3)
|
|
20309
|
+
* const dry = await ctx.noorm.changes.next(2, { dryRun: true })
|
|
20158
20310
|
* ```
|
|
20159
20311
|
*/
|
|
20160
|
-
async next(count = 1) {
|
|
20161
|
-
return this.#getManager().next(count);
|
|
20312
|
+
async next(count = 1, options) {
|
|
20313
|
+
return this.#getManager().next(count, options);
|
|
20162
20314
|
}
|
|
20163
20315
|
// ─────────────────────────────────────────────────────
|
|
20164
20316
|
// Status & history (connected)
|
|
@@ -20266,8 +20418,12 @@ var ChangesNamespace = class {
|
|
|
20266
20418
|
}
|
|
20267
20419
|
#createChangeContext() {
|
|
20268
20420
|
const state = getStateManager(this.#state.projectRoot);
|
|
20421
|
+
if (!this.#state.connection) {
|
|
20422
|
+
throw new Error("Not connected. Call connect() first.");
|
|
20423
|
+
}
|
|
20269
20424
|
return {
|
|
20270
20425
|
db: this.#kysely,
|
|
20426
|
+
dialect: this.#state.connection.dialect,
|
|
20271
20427
|
configName: this.#state.config.name,
|
|
20272
20428
|
identity: this.#state.identity,
|
|
20273
20429
|
projectRoot: this.#state.projectRoot,
|
|
@@ -20285,6 +20441,48 @@ var ChangesNamespace = class {
|
|
|
20285
20441
|
return this.#manager;
|
|
20286
20442
|
}
|
|
20287
20443
|
};
|
|
20444
|
+
function splitMssqlBatches(content) {
|
|
20445
|
+
const batches = content.split(/^\s*GO\s*$/mi);
|
|
20446
|
+
return batches.map((b) => b.trim()).filter((b) => b.length > 0 && !isCommentOnly(b));
|
|
20447
|
+
}
|
|
20448
|
+
function isCommentOnly(batch) {
|
|
20449
|
+
for (const line of batch.split("\n")) {
|
|
20450
|
+
const trimmed = line.trim();
|
|
20451
|
+
if (trimmed.length > 0 && !trimmed.startsWith("--")) {
|
|
20452
|
+
return false;
|
|
20453
|
+
}
|
|
20454
|
+
}
|
|
20455
|
+
return true;
|
|
20456
|
+
}
|
|
20457
|
+
async function executeSqlBody(context, sqlContent) {
|
|
20458
|
+
if (context.dialect !== "mssql") {
|
|
20459
|
+
const [, execErr] = await attempt(() => sql.raw(sqlContent).execute(context.db));
|
|
20460
|
+
if (execErr) {
|
|
20461
|
+
return getSqlErrorMessage(execErr);
|
|
20462
|
+
}
|
|
20463
|
+
return null;
|
|
20464
|
+
}
|
|
20465
|
+
const batches = splitMssqlBatches(sqlContent);
|
|
20466
|
+
if (batches.length === 0) {
|
|
20467
|
+
return null;
|
|
20468
|
+
}
|
|
20469
|
+
if (batches.length === 1) {
|
|
20470
|
+
const [, execErr] = await attempt(() => sql.raw(batches[0]).execute(context.db));
|
|
20471
|
+
if (execErr) {
|
|
20472
|
+
return getSqlErrorMessage(execErr);
|
|
20473
|
+
}
|
|
20474
|
+
return null;
|
|
20475
|
+
}
|
|
20476
|
+
for (let i = 0; i < batches.length; i++) {
|
|
20477
|
+
const [, execErr] = await attempt(() => sql.raw(batches[i]).execute(context.db));
|
|
20478
|
+
if (execErr) {
|
|
20479
|
+
return `[batch ${i + 1} of ${batches.length}] ${getSqlErrorMessage(execErr)}`;
|
|
20480
|
+
}
|
|
20481
|
+
}
|
|
20482
|
+
return null;
|
|
20483
|
+
}
|
|
20484
|
+
|
|
20485
|
+
// src/core/runner/runner.ts
|
|
20288
20486
|
var SQL_EXTENSIONS2 = [".sql", ".sql.tmpl"];
|
|
20289
20487
|
var FILE_HEADER_TEMPLATE = `-- ============================================================
|
|
20290
20488
|
-- File: %FILE%
|
|
@@ -20650,14 +20848,14 @@ async function executeSingleFileWithUpdate(context, filepath, checksum, options,
|
|
|
20650
20848
|
return result2;
|
|
20651
20849
|
}
|
|
20652
20850
|
}
|
|
20653
|
-
const
|
|
20851
|
+
const execErrMsg = await executeSqlBody(context, sqlContent);
|
|
20654
20852
|
const durationMs = performance.now() - start;
|
|
20655
|
-
if (
|
|
20853
|
+
if (execErrMsg) {
|
|
20656
20854
|
const result2 = {
|
|
20657
20855
|
filepath,
|
|
20658
20856
|
checksum: finalChecksum,
|
|
20659
20857
|
status: "failed",
|
|
20660
|
-
error:
|
|
20858
|
+
error: execErrMsg,
|
|
20661
20859
|
durationMs
|
|
20662
20860
|
};
|
|
20663
20861
|
await tracker.updateFileExecution(
|
|
@@ -20665,13 +20863,13 @@ async function executeSingleFileWithUpdate(context, filepath, checksum, options,
|
|
|
20665
20863
|
relFilepath,
|
|
20666
20864
|
"failed",
|
|
20667
20865
|
Math.round(durationMs),
|
|
20668
|
-
|
|
20866
|
+
execErrMsg
|
|
20669
20867
|
);
|
|
20670
20868
|
observer.emit("file:after", {
|
|
20671
20869
|
filepath,
|
|
20672
20870
|
status: "failed",
|
|
20673
20871
|
durationMs,
|
|
20674
|
-
error:
|
|
20872
|
+
error: execErrMsg
|
|
20675
20873
|
});
|
|
20676
20874
|
return result2;
|
|
20677
20875
|
}
|
|
@@ -20774,14 +20972,14 @@ async function executeSingleFile(context, filepath, options, tracker, operationI
|
|
|
20774
20972
|
});
|
|
20775
20973
|
return result2;
|
|
20776
20974
|
}
|
|
20777
|
-
const
|
|
20975
|
+
const execErrMsg = await executeSqlBody(context, sqlContent);
|
|
20778
20976
|
const durationMs = performance.now() - start;
|
|
20779
|
-
if (
|
|
20977
|
+
if (execErrMsg) {
|
|
20780
20978
|
const result2 = {
|
|
20781
20979
|
filepath,
|
|
20782
20980
|
checksum,
|
|
20783
20981
|
status: "failed",
|
|
20784
|
-
error:
|
|
20982
|
+
error: execErrMsg,
|
|
20785
20983
|
durationMs
|
|
20786
20984
|
};
|
|
20787
20985
|
await tracker.recordExecution({
|
|
@@ -20789,14 +20987,14 @@ async function executeSingleFile(context, filepath, options, tracker, operationI
|
|
|
20789
20987
|
filepath: relFilepath,
|
|
20790
20988
|
checksum,
|
|
20791
20989
|
status: "failed",
|
|
20792
|
-
errorMessage:
|
|
20990
|
+
errorMessage: execErrMsg,
|
|
20793
20991
|
durationMs: Math.round(durationMs)
|
|
20794
20992
|
});
|
|
20795
20993
|
observer.emit("file:after", {
|
|
20796
20994
|
filepath,
|
|
20797
20995
|
status: "failed",
|
|
20798
20996
|
durationMs,
|
|
20799
|
-
error:
|
|
20997
|
+
error: execErrMsg
|
|
20800
20998
|
});
|
|
20801
20999
|
return result2;
|
|
20802
21000
|
}
|
|
@@ -23322,10 +23520,10 @@ var { quote, qualifiedName } = createDialectQuoting({
|
|
|
23322
23520
|
escape: '""'
|
|
23323
23521
|
});
|
|
23324
23522
|
var postgresTeardownOperations = {
|
|
23325
|
-
disableForeignKeyChecks() {
|
|
23523
|
+
disableForeignKeyChecks(_tables) {
|
|
23326
23524
|
return "SET session_replication_role = 'replica'";
|
|
23327
23525
|
},
|
|
23328
|
-
enableForeignKeyChecks() {
|
|
23526
|
+
enableForeignKeyChecks(_tables) {
|
|
23329
23527
|
return "SET session_replication_role = 'origin'";
|
|
23330
23528
|
},
|
|
23331
23529
|
truncateTable(tableName, schema, restartIdentity = true) {
|
|
@@ -23360,10 +23558,10 @@ var { quote: quote2, qualifiedName: qualifiedName2 } = createDialectQuoting({
|
|
|
23360
23558
|
escape: "``"
|
|
23361
23559
|
});
|
|
23362
23560
|
var mysqlTeardownOperations = {
|
|
23363
|
-
disableForeignKeyChecks() {
|
|
23561
|
+
disableForeignKeyChecks(_tables) {
|
|
23364
23562
|
return "SET FOREIGN_KEY_CHECKS = 0";
|
|
23365
23563
|
},
|
|
23366
|
-
enableForeignKeyChecks() {
|
|
23564
|
+
enableForeignKeyChecks(_tables) {
|
|
23367
23565
|
return "SET FOREIGN_KEY_CHECKS = 1";
|
|
23368
23566
|
},
|
|
23369
23567
|
truncateTable(tableName, schema, _restartIdentity = true) {
|
|
@@ -23396,10 +23594,16 @@ var { quote: quote3, qualifiedName: qualifiedName3 } = createDialectQuoting({
|
|
|
23396
23594
|
escape: "]]"
|
|
23397
23595
|
});
|
|
23398
23596
|
var mssqlTeardownOperations = {
|
|
23399
|
-
disableForeignKeyChecks() {
|
|
23597
|
+
disableForeignKeyChecks(tables) {
|
|
23598
|
+
if (tables && tables.length > 0) {
|
|
23599
|
+
return tables.map((t) => `ALTER TABLE ${qualifiedName3(t)} NOCHECK CONSTRAINT ALL`);
|
|
23600
|
+
}
|
|
23400
23601
|
return "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'";
|
|
23401
23602
|
},
|
|
23402
|
-
enableForeignKeyChecks() {
|
|
23603
|
+
enableForeignKeyChecks(tables) {
|
|
23604
|
+
if (tables && tables.length > 0) {
|
|
23605
|
+
return tables.map((t) => `ALTER TABLE ${qualifiedName3(t)} CHECK CONSTRAINT ALL`);
|
|
23606
|
+
}
|
|
23403
23607
|
return "EXEC sp_MSforeachtable 'ALTER TABLE ? CHECK CONSTRAINT ALL'";
|
|
23404
23608
|
},
|
|
23405
23609
|
truncateTable(tableName, schema, restartIdentity = true) {
|
|
@@ -23438,10 +23642,10 @@ var { quote: quote4 } = createDialectQuoting({
|
|
|
23438
23642
|
escape: '""'
|
|
23439
23643
|
});
|
|
23440
23644
|
var sqliteTeardownOperations = {
|
|
23441
|
-
disableForeignKeyChecks() {
|
|
23645
|
+
disableForeignKeyChecks(_tables) {
|
|
23442
23646
|
return "PRAGMA foreign_keys = OFF";
|
|
23443
23647
|
},
|
|
23444
|
-
enableForeignKeyChecks() {
|
|
23648
|
+
enableForeignKeyChecks(_tables) {
|
|
23445
23649
|
return "PRAGMA foreign_keys = ON";
|
|
23446
23650
|
},
|
|
23447
23651
|
truncateTable(tableName, _schema, _restartIdentity = false) {
|
|
@@ -23491,6 +23695,13 @@ function isNoormTable2(name) {
|
|
|
23491
23695
|
if (name.startsWith("__noorm_")) return true;
|
|
23492
23696
|
return false;
|
|
23493
23697
|
}
|
|
23698
|
+
function pushFlat(out, value) {
|
|
23699
|
+
if (Array.isArray(value)) {
|
|
23700
|
+
out.push(...value);
|
|
23701
|
+
return;
|
|
23702
|
+
}
|
|
23703
|
+
out.push(value);
|
|
23704
|
+
}
|
|
23494
23705
|
async function truncateData(db, dialect, options = {}) {
|
|
23495
23706
|
const startTime = performance.now();
|
|
23496
23707
|
const ops = getTeardownOperations(dialect);
|
|
@@ -23520,11 +23731,13 @@ async function truncateData(db, dialect, options = {}) {
|
|
|
23520
23731
|
}
|
|
23521
23732
|
truncated.push(tableName);
|
|
23522
23733
|
}
|
|
23523
|
-
|
|
23524
|
-
|
|
23525
|
-
|
|
23734
|
+
if (truncated.length > 0) {
|
|
23735
|
+
pushFlat(statements, ops.disableForeignKeyChecks(truncated));
|
|
23736
|
+
for (const tableName of truncated) {
|
|
23737
|
+
statements.push(ops.truncateTable(tableName, void 0, options.restartIdentity ?? true));
|
|
23738
|
+
}
|
|
23739
|
+
pushFlat(statements, ops.enableForeignKeyChecks(truncated));
|
|
23526
23740
|
}
|
|
23527
|
-
statements.push(ops.enableForeignKeyChecks());
|
|
23528
23741
|
if (!options.dryRun) {
|
|
23529
23742
|
for (const stmt of statements) {
|
|
23530
23743
|
if (stmt.startsWith("--")) continue;
|
|
@@ -23596,6 +23809,18 @@ async function teardownSchema(db, dialect, options = {}) {
|
|
|
23596
23809
|
dropped.foreignKeys.push(fk.name);
|
|
23597
23810
|
statements.push(ops.dropForeignKey(fk.name, tableName, fk.schema));
|
|
23598
23811
|
}
|
|
23812
|
+
if (!options.keepProcedures) {
|
|
23813
|
+
for (const proc of procedures) {
|
|
23814
|
+
dropped.procedures.push(proc.name);
|
|
23815
|
+
statements.push(ops.dropProcedure(proc.name, proc.schema));
|
|
23816
|
+
}
|
|
23817
|
+
}
|
|
23818
|
+
if (!options.keepFunctions) {
|
|
23819
|
+
for (const fn of functions) {
|
|
23820
|
+
dropped.functions.push(fn.name);
|
|
23821
|
+
statements.push(ops.dropFunction(fn.name, fn.schema));
|
|
23822
|
+
}
|
|
23823
|
+
}
|
|
23599
23824
|
if (!options.keepViews) {
|
|
23600
23825
|
for (const view of views) {
|
|
23601
23826
|
dropped.views.push(view.name);
|
|
@@ -23615,18 +23840,6 @@ async function teardownSchema(db, dialect, options = {}) {
|
|
|
23615
23840
|
dropped.tables.push(tableName);
|
|
23616
23841
|
statements.push(ops.dropTable(tableName, table.schema));
|
|
23617
23842
|
}
|
|
23618
|
-
if (!options.keepFunctions) {
|
|
23619
|
-
for (const fn of functions) {
|
|
23620
|
-
dropped.functions.push(fn.name);
|
|
23621
|
-
statements.push(ops.dropFunction(fn.name, fn.schema));
|
|
23622
|
-
}
|
|
23623
|
-
}
|
|
23624
|
-
if (!options.keepProcedures) {
|
|
23625
|
-
for (const proc of procedures) {
|
|
23626
|
-
dropped.procedures.push(proc.name);
|
|
23627
|
-
statements.push(ops.dropProcedure(proc.name, proc.schema));
|
|
23628
|
-
}
|
|
23629
|
-
}
|
|
23630
23843
|
const skipTypesDueToKeptRoutines = dialect === "mssql" && (options.keepFunctions || options.keepProcedures);
|
|
23631
23844
|
if (!options.keepTypes && !skipTypesDueToKeptRoutines) {
|
|
23632
23845
|
const sortedTypes = [...types].sort((a, b) => {
|
|
@@ -24286,25 +24499,60 @@ async function copyVaultSecrets(sourceConfig, destConfig, keys, identityHash, pr
|
|
|
24286
24499
|
// src/sdk/namespaces/vault.ts
|
|
24287
24500
|
var VaultNamespace = class {
|
|
24288
24501
|
#state;
|
|
24502
|
+
#cryptoIdentity = null;
|
|
24289
24503
|
constructor(state) {
|
|
24290
24504
|
this.#state = state;
|
|
24291
24505
|
}
|
|
24506
|
+
/**
|
|
24507
|
+
* Load the cryptographic identity (with publicKey + identityHash) on
|
|
24508
|
+
* demand. The Context's state.identity is the audit identity (name +
|
|
24509
|
+
* email only); the crypto fields needed for vault encryption live in
|
|
24510
|
+
* `~/.noorm/identity.json` (or a NOORM_IDENTITY_* env override).
|
|
24511
|
+
*
|
|
24512
|
+
* Cached after the first call so repeated vault ops don't re-read disk.
|
|
24513
|
+
*/
|
|
24514
|
+
async #getCryptoIdentity() {
|
|
24515
|
+
if (this.#cryptoIdentity) return this.#cryptoIdentity;
|
|
24516
|
+
const loaded = await loadIdentityMetadata();
|
|
24517
|
+
if (!loaded) {
|
|
24518
|
+
throw new Error(
|
|
24519
|
+
"Vault operations require a cryptographic identity at ~/.noorm/identity.json. Run `noorm identity init` or set NOORM_IDENTITY_* env vars in CI."
|
|
24520
|
+
);
|
|
24521
|
+
}
|
|
24522
|
+
this.#cryptoIdentity = loaded;
|
|
24523
|
+
return loaded;
|
|
24524
|
+
}
|
|
24292
24525
|
// ─────────────────────────────────────────────────────
|
|
24293
24526
|
// Lifecycle
|
|
24294
24527
|
// ─────────────────────────────────────────────────────
|
|
24295
24528
|
/**
|
|
24296
24529
|
* Initialize the vault for this database.
|
|
24297
24530
|
*
|
|
24531
|
+
* Idempotent. Returns the vault key on first init; returns [null, null]
|
|
24532
|
+
* on subsequent calls when the vault already exists. Returns [null, Error]
|
|
24533
|
+
* only on real failures (DB errors, encryption errors).
|
|
24534
|
+
*
|
|
24535
|
+
* The `vault:initialized` observer event fires only on first init, never
|
|
24536
|
+
* on repeat calls.
|
|
24537
|
+
*
|
|
24298
24538
|
* @example
|
|
24299
24539
|
* ```typescript
|
|
24300
|
-
* const [vaultKey, err] = await ctx.noorm.vault.init()
|
|
24540
|
+
* const [vaultKey, err] = await ctx.noorm.vault.init();
|
|
24541
|
+
* if (err) throw err;
|
|
24542
|
+
* if (vaultKey) {
|
|
24543
|
+
* // first-time init — set initial secrets, etc.
|
|
24544
|
+
* }
|
|
24545
|
+
* else {
|
|
24546
|
+
* // already initialized — vault.get / vault.set with private key
|
|
24547
|
+
* }
|
|
24301
24548
|
* ```
|
|
24302
24549
|
*/
|
|
24303
24550
|
async init() {
|
|
24551
|
+
const crypto = await this.#getCryptoIdentity();
|
|
24304
24552
|
return initializeVault(
|
|
24305
24553
|
this.#kysely,
|
|
24306
|
-
|
|
24307
|
-
|
|
24554
|
+
crypto.identityHash,
|
|
24555
|
+
crypto.publicKey,
|
|
24308
24556
|
this.#dialect
|
|
24309
24557
|
);
|
|
24310
24558
|
}
|
|
@@ -24317,9 +24565,10 @@ var VaultNamespace = class {
|
|
|
24317
24565
|
* ```
|
|
24318
24566
|
*/
|
|
24319
24567
|
async status() {
|
|
24568
|
+
const crypto = await this.#getCryptoIdentity();
|
|
24320
24569
|
return getVaultStatus(
|
|
24321
24570
|
this.#kysely,
|
|
24322
|
-
|
|
24571
|
+
crypto.identityHash,
|
|
24323
24572
|
this.#dialect
|
|
24324
24573
|
);
|
|
24325
24574
|
}
|
|
@@ -24456,13 +24705,14 @@ var VaultNamespace = class {
|
|
|
24456
24705
|
* ```
|
|
24457
24706
|
*/
|
|
24458
24707
|
async copy(destConfig, keys, privateKey, options) {
|
|
24708
|
+
const crypto = await this.#getCryptoIdentity();
|
|
24459
24709
|
return copyVaultSecrets(
|
|
24460
24710
|
this.#state.config,
|
|
24461
24711
|
destConfig,
|
|
24462
24712
|
keys,
|
|
24463
|
-
|
|
24713
|
+
crypto.identityHash,
|
|
24464
24714
|
privateKey,
|
|
24465
|
-
|
|
24715
|
+
crypto.publicKey,
|
|
24466
24716
|
options
|
|
24467
24717
|
);
|
|
24468
24718
|
}
|
|
@@ -24475,21 +24725,14 @@ var VaultNamespace = class {
|
|
|
24475
24725
|
}
|
|
24476
24726
|
return this.#state.connection.db;
|
|
24477
24727
|
}
|
|
24478
|
-
get #identityHash() {
|
|
24479
|
-
const identity = this.#state.identity;
|
|
24480
|
-
return identity["identityHash"] ?? "";
|
|
24481
|
-
}
|
|
24482
|
-
get #publicKey() {
|
|
24483
|
-
const identity = this.#state.identity;
|
|
24484
|
-
return identity["publicKey"] ?? "";
|
|
24485
|
-
}
|
|
24486
24728
|
get #dialect() {
|
|
24487
24729
|
return this.#state.config.connection.dialect;
|
|
24488
24730
|
}
|
|
24489
24731
|
async #getVaultKey(privateKey) {
|
|
24732
|
+
const crypto = await this.#getCryptoIdentity();
|
|
24490
24733
|
return getVaultKey(
|
|
24491
24734
|
this.#kysely,
|
|
24492
|
-
|
|
24735
|
+
crypto.identityHash,
|
|
24493
24736
|
privateKey,
|
|
24494
24737
|
this.#dialect
|
|
24495
24738
|
);
|
|
@@ -24575,7 +24818,7 @@ function isSameServer(source, dest) {
|
|
|
24575
24818
|
}
|
|
24576
24819
|
return true;
|
|
24577
24820
|
}
|
|
24578
|
-
function
|
|
24821
|
+
function quoteIdent2(name) {
|
|
24579
24822
|
return `"${name.replace(/"/g, '""')}"`;
|
|
24580
24823
|
}
|
|
24581
24824
|
var postgresTransferOperations = {
|
|
@@ -24592,20 +24835,20 @@ var postgresTransferOperations = {
|
|
|
24592
24835
|
return null;
|
|
24593
24836
|
},
|
|
24594
24837
|
getResetSequenceSql(table, column, schema) {
|
|
24595
|
-
const fullTable = schema ? `${
|
|
24838
|
+
const fullTable = schema ? `${quoteIdent2(schema)}.${quoteIdent2(table)}` : quoteIdent2(table);
|
|
24596
24839
|
return `
|
|
24597
24840
|
SELECT setval(
|
|
24598
24841
|
pg_get_serial_sequence('${schema ? schema + "." : ""}${table}', '${column}'),
|
|
24599
|
-
COALESCE((SELECT MAX(${
|
|
24842
|
+
COALESCE((SELECT MAX(${quoteIdent2(column)}) FROM ${fullTable}), 0) + 1,
|
|
24600
24843
|
false
|
|
24601
24844
|
)
|
|
24602
24845
|
`.trim();
|
|
24603
24846
|
},
|
|
24604
24847
|
buildConflictInsert(table, columns, primaryKey, strategy) {
|
|
24605
|
-
const quotedCols = columns.map(
|
|
24848
|
+
const quotedCols = columns.map(quoteIdent2).join(", ");
|
|
24606
24849
|
const placeholders = columns.map((_, i) => `$${i + 1}`).join(", ");
|
|
24607
|
-
const pkCols = primaryKey.map(
|
|
24608
|
-
let insertSql = `INSERT INTO ${
|
|
24850
|
+
const pkCols = primaryKey.map(quoteIdent2).join(", ");
|
|
24851
|
+
let insertSql = `INSERT INTO ${quoteIdent2(table)} (${quotedCols}) OVERRIDING SYSTEM VALUE VALUES (${placeholders})`;
|
|
24609
24852
|
switch (strategy) {
|
|
24610
24853
|
case "fail":
|
|
24611
24854
|
break;
|
|
@@ -24615,7 +24858,7 @@ var postgresTransferOperations = {
|
|
|
24615
24858
|
case "update": {
|
|
24616
24859
|
const updateCols = columns.filter((c) => !primaryKey.includes(c));
|
|
24617
24860
|
if (updateCols.length > 0) {
|
|
24618
|
-
const setClauses = updateCols.map((c) => `${
|
|
24861
|
+
const setClauses = updateCols.map((c) => `${quoteIdent2(c)} = EXCLUDED.${quoteIdent2(c)}`).join(", ");
|
|
24619
24862
|
insertSql += ` ON CONFLICT (${pkCols}) DO UPDATE SET ${setClauses}`;
|
|
24620
24863
|
} else {
|
|
24621
24864
|
insertSql += ` ON CONFLICT (${pkCols}) DO NOTHING`;
|
|
@@ -24624,7 +24867,7 @@ var postgresTransferOperations = {
|
|
|
24624
24867
|
}
|
|
24625
24868
|
case "replace":
|
|
24626
24869
|
{
|
|
24627
|
-
const setClauses = columns.map((c) => `${
|
|
24870
|
+
const setClauses = columns.map((c) => `${quoteIdent2(c)} = EXCLUDED.${quoteIdent2(c)}`).join(", ");
|
|
24628
24871
|
insertSql += ` ON CONFLICT (${pkCols}) DO UPDATE SET ${setClauses}`;
|
|
24629
24872
|
}
|
|
24630
24873
|
break;
|
|
@@ -24632,9 +24875,9 @@ var postgresTransferOperations = {
|
|
|
24632
24875
|
return insertSql;
|
|
24633
24876
|
},
|
|
24634
24877
|
buildDirectTransfer(srcDb, srcTable, dstTable, columns, srcSchema = "public", dstSchema = "public") {
|
|
24635
|
-
const quotedCols = columns.map(
|
|
24636
|
-
const srcFull = `${
|
|
24637
|
-
const dstFull = `${
|
|
24878
|
+
const quotedCols = columns.map(quoteIdent2).join(", ");
|
|
24879
|
+
const srcFull = `${quoteIdent2(srcSchema)}.${quoteIdent2(srcTable)}`;
|
|
24880
|
+
const dstFull = `${quoteIdent2(dstSchema)}.${quoteIdent2(dstTable)}`;
|
|
24638
24881
|
return `INSERT INTO ${dstFull} (${quotedCols}) OVERRIDING SYSTEM VALUE SELECT ${quotedCols} FROM ${srcFull}`;
|
|
24639
24882
|
},
|
|
24640
24883
|
async executeDisableFK(db) {
|
|
@@ -24644,7 +24887,7 @@ var postgresTransferOperations = {
|
|
|
24644
24887
|
await sql.raw(this.getEnableFKSql()).execute(db);
|
|
24645
24888
|
}
|
|
24646
24889
|
};
|
|
24647
|
-
function
|
|
24890
|
+
function quoteIdent3(name) {
|
|
24648
24891
|
return `\`${name.replace(/`/g, "``")}\``;
|
|
24649
24892
|
}
|
|
24650
24893
|
var mysqlTransferOperations = {
|
|
@@ -24661,32 +24904,32 @@ var mysqlTransferOperations = {
|
|
|
24661
24904
|
return null;
|
|
24662
24905
|
},
|
|
24663
24906
|
getResetSequenceSql(table, _column, _schema) {
|
|
24664
|
-
return `ALTER TABLE ${
|
|
24907
|
+
return `ALTER TABLE ${quoteIdent3(table)} AUTO_INCREMENT = 1`;
|
|
24665
24908
|
},
|
|
24666
24909
|
buildConflictInsert(table, columns, primaryKey, strategy) {
|
|
24667
|
-
const quotedCols = columns.map(
|
|
24910
|
+
const quotedCols = columns.map(quoteIdent3).join(", ");
|
|
24668
24911
|
const placeholders = columns.map(() => "?").join(", ");
|
|
24669
24912
|
switch (strategy) {
|
|
24670
24913
|
case "fail":
|
|
24671
|
-
return `INSERT INTO ${
|
|
24914
|
+
return `INSERT INTO ${quoteIdent3(table)} (${quotedCols}) VALUES (${placeholders})`;
|
|
24672
24915
|
case "skip":
|
|
24673
|
-
return `INSERT IGNORE INTO ${
|
|
24916
|
+
return `INSERT IGNORE INTO ${quoteIdent3(table)} (${quotedCols}) VALUES (${placeholders})`;
|
|
24674
24917
|
case "update": {
|
|
24675
24918
|
const updateCols = columns.filter((c) => !primaryKey.includes(c));
|
|
24676
24919
|
if (updateCols.length > 0) {
|
|
24677
|
-
const setClauses = updateCols.map((c) => `${
|
|
24678
|
-
return `INSERT INTO ${
|
|
24920
|
+
const setClauses = updateCols.map((c) => `${quoteIdent3(c)} = VALUES(${quoteIdent3(c)})`).join(", ");
|
|
24921
|
+
return `INSERT INTO ${quoteIdent3(table)} (${quotedCols}) VALUES (${placeholders}) ON DUPLICATE KEY UPDATE ${setClauses}`;
|
|
24679
24922
|
}
|
|
24680
|
-
return `INSERT IGNORE INTO ${
|
|
24923
|
+
return `INSERT IGNORE INTO ${quoteIdent3(table)} (${quotedCols}) VALUES (${placeholders})`;
|
|
24681
24924
|
}
|
|
24682
24925
|
case "replace":
|
|
24683
|
-
return `REPLACE INTO ${
|
|
24926
|
+
return `REPLACE INTO ${quoteIdent3(table)} (${quotedCols}) VALUES (${placeholders})`;
|
|
24684
24927
|
}
|
|
24685
24928
|
},
|
|
24686
24929
|
buildDirectTransfer(srcDb, srcTable, dstTable, columns, _srcSchema, _dstSchema) {
|
|
24687
|
-
const quotedCols = columns.map(
|
|
24688
|
-
const srcFull = `${
|
|
24689
|
-
const dstFull =
|
|
24930
|
+
const quotedCols = columns.map(quoteIdent3).join(", ");
|
|
24931
|
+
const srcFull = `${quoteIdent3(srcDb)}.${quoteIdent3(srcTable)}`;
|
|
24932
|
+
const dstFull = quoteIdent3(dstTable);
|
|
24690
24933
|
return `INSERT INTO ${dstFull} (${quotedCols}) SELECT ${quotedCols} FROM ${srcFull}`;
|
|
24691
24934
|
},
|
|
24692
24935
|
async executeDisableFK(db) {
|
|
@@ -24696,7 +24939,7 @@ var mysqlTransferOperations = {
|
|
|
24696
24939
|
await sql.raw(this.getEnableFKSql()).execute(db);
|
|
24697
24940
|
}
|
|
24698
24941
|
};
|
|
24699
|
-
function
|
|
24942
|
+
function quoteIdent4(name) {
|
|
24700
24943
|
return `[${name.replace(/\]/g, "]]")}]`;
|
|
24701
24944
|
}
|
|
24702
24945
|
var mssqlTransferOperations = {
|
|
@@ -24707,27 +24950,27 @@ var mssqlTransferOperations = {
|
|
|
24707
24950
|
return "-- FK enable per-table";
|
|
24708
24951
|
},
|
|
24709
24952
|
getEnableIdentityInsertSql(table) {
|
|
24710
|
-
return `SET IDENTITY_INSERT ${
|
|
24953
|
+
return `SET IDENTITY_INSERT ${quoteIdent4(table)} ON`;
|
|
24711
24954
|
},
|
|
24712
24955
|
getDisableIdentityInsertSql(table) {
|
|
24713
|
-
return `SET IDENTITY_INSERT ${
|
|
24956
|
+
return `SET IDENTITY_INSERT ${quoteIdent4(table)} OFF`;
|
|
24714
24957
|
},
|
|
24715
24958
|
getResetSequenceSql(table, _column, _schema) {
|
|
24716
24959
|
return `DBCC CHECKIDENT ('${table}', RESEED)`;
|
|
24717
24960
|
},
|
|
24718
24961
|
buildConflictInsert(table, columns, primaryKey, strategy) {
|
|
24719
|
-
const quotedCols = columns.map(
|
|
24962
|
+
const quotedCols = columns.map(quoteIdent4).join(", ");
|
|
24720
24963
|
const placeholders = columns.map((_, i) => `@p${i}`).join(", ");
|
|
24721
24964
|
switch (strategy) {
|
|
24722
24965
|
case "fail":
|
|
24723
|
-
return `INSERT INTO ${
|
|
24966
|
+
return `INSERT INTO ${quoteIdent4(table)} (${quotedCols}) VALUES (${placeholders})`;
|
|
24724
24967
|
case "skip":
|
|
24725
24968
|
case "update":
|
|
24726
24969
|
case "replace": {
|
|
24727
|
-
const pkConditions = primaryKey.map((pk) => `target.${
|
|
24728
|
-
const sourceValues = columns.map((c, i) => `@p${i} AS ${
|
|
24970
|
+
const pkConditions = primaryKey.map((pk) => `target.${quoteIdent4(pk)} = source.${quoteIdent4(pk)}`).join(" AND ");
|
|
24971
|
+
const sourceValues = columns.map((c, i) => `@p${i} AS ${quoteIdent4(c)}`).join(", ");
|
|
24729
24972
|
let mergeSql = `
|
|
24730
|
-
MERGE INTO ${
|
|
24973
|
+
MERGE INTO ${quoteIdent4(table)} AS target
|
|
24731
24974
|
USING (SELECT ${sourceValues}) AS source
|
|
24732
24975
|
ON (${pkConditions})
|
|
24733
24976
|
`.trim();
|
|
@@ -24735,12 +24978,12 @@ var mssqlTransferOperations = {
|
|
|
24735
24978
|
mergeSql += `
|
|
24736
24979
|
WHEN NOT MATCHED THEN
|
|
24737
24980
|
INSERT (${quotedCols})
|
|
24738
|
-
VALUES (${columns.map((c) => `source.${
|
|
24981
|
+
VALUES (${columns.map((c) => `source.${quoteIdent4(c)}`).join(", ")})
|
|
24739
24982
|
`;
|
|
24740
24983
|
} else if (strategy === "update") {
|
|
24741
24984
|
const updateCols = columns.filter((c) => !primaryKey.includes(c));
|
|
24742
24985
|
if (updateCols.length > 0) {
|
|
24743
|
-
const setClauses = updateCols.map((c) => `target.${
|
|
24986
|
+
const setClauses = updateCols.map((c) => `target.${quoteIdent4(c)} = source.${quoteIdent4(c)}`).join(", ");
|
|
24744
24987
|
mergeSql += `
|
|
24745
24988
|
WHEN MATCHED THEN
|
|
24746
24989
|
UPDATE SET ${setClauses}
|
|
@@ -24749,16 +24992,16 @@ var mssqlTransferOperations = {
|
|
|
24749
24992
|
mergeSql += `
|
|
24750
24993
|
WHEN NOT MATCHED THEN
|
|
24751
24994
|
INSERT (${quotedCols})
|
|
24752
|
-
VALUES (${columns.map((c) => `source.${
|
|
24995
|
+
VALUES (${columns.map((c) => `source.${quoteIdent4(c)}`).join(", ")})
|
|
24753
24996
|
`;
|
|
24754
24997
|
} else {
|
|
24755
|
-
const setClauses = columns.map((c) => `target.${
|
|
24998
|
+
const setClauses = columns.map((c) => `target.${quoteIdent4(c)} = source.${quoteIdent4(c)}`).join(", ");
|
|
24756
24999
|
mergeSql += `
|
|
24757
25000
|
WHEN MATCHED THEN
|
|
24758
25001
|
UPDATE SET ${setClauses}
|
|
24759
25002
|
WHEN NOT MATCHED THEN
|
|
24760
25003
|
INSERT (${quotedCols})
|
|
24761
|
-
VALUES (${columns.map((c) => `source.${
|
|
25004
|
+
VALUES (${columns.map((c) => `source.${quoteIdent4(c)}`).join(", ")})
|
|
24762
25005
|
`;
|
|
24763
25006
|
}
|
|
24764
25007
|
mergeSql += ";";
|
|
@@ -24767,19 +25010,19 @@ var mssqlTransferOperations = {
|
|
|
24767
25010
|
}
|
|
24768
25011
|
},
|
|
24769
25012
|
buildDirectTransfer(srcDb, srcTable, dstTable, columns, srcSchema = "dbo", dstSchema = "dbo") {
|
|
24770
|
-
const quotedCols = columns.map(
|
|
24771
|
-
const srcFull = `${
|
|
24772
|
-
const dstFull = `${
|
|
25013
|
+
const quotedCols = columns.map(quoteIdent4).join(", ");
|
|
25014
|
+
const srcFull = `${quoteIdent4(srcDb)}.${quoteIdent4(srcSchema)}.${quoteIdent4(srcTable)}`;
|
|
25015
|
+
const dstFull = `${quoteIdent4(dstSchema)}.${quoteIdent4(dstTable)}`;
|
|
24773
25016
|
return `INSERT INTO ${dstFull} (${quotedCols}) SELECT ${quotedCols} FROM ${srcFull}`;
|
|
24774
25017
|
},
|
|
24775
25018
|
async executeDisableFK(db, tables) {
|
|
24776
25019
|
for (const table of tables) {
|
|
24777
|
-
await sql.raw(`ALTER TABLE ${
|
|
25020
|
+
await sql.raw(`ALTER TABLE ${quoteIdent4(table)} NOCHECK CONSTRAINT ALL`).execute(db);
|
|
24778
25021
|
}
|
|
24779
25022
|
},
|
|
24780
25023
|
async executeEnableFK(db, tables) {
|
|
24781
25024
|
for (const table of tables) {
|
|
24782
|
-
await sql.raw(`ALTER TABLE ${
|
|
25025
|
+
await sql.raw(`ALTER TABLE ${quoteIdent4(table)} WITH CHECK CHECK CONSTRAINT ALL`).execute(db);
|
|
24783
25026
|
}
|
|
24784
25027
|
}
|
|
24785
25028
|
};
|
|
@@ -26319,10 +26562,10 @@ async function transferTableCrossDialect(ctx, plan, transferPlan, options) {
|
|
|
26319
26562
|
}, null];
|
|
26320
26563
|
}
|
|
26321
26564
|
async function fetchBatch(db, dialect, table, columns, limit, offset, _schema) {
|
|
26322
|
-
const
|
|
26323
|
-
const columnList = columns.map(
|
|
26565
|
+
const quoteIdent5 = dialect === "mssql" ? (c) => `[${c}]` : dialect === "mysql" ? (c) => `\`${c}\`` : (c) => `"${c}"`;
|
|
26566
|
+
const columnList = columns.map(quoteIdent5).join(", ");
|
|
26324
26567
|
if (dialect === "mssql") {
|
|
26325
|
-
const orderCol =
|
|
26568
|
+
const orderCol = quoteIdent5(columns[0]);
|
|
26326
26569
|
const result2 = await sql`
|
|
26327
26570
|
SELECT ${sql.raw(columnList)}
|
|
26328
26571
|
FROM ${sql.table(table)}
|
|
@@ -26988,9 +27231,9 @@ async function exportTableWithWorkers(ctx) {
|
|
|
26988
27231
|
totalRows = Number(firstRow?.["cnt"] ?? 0);
|
|
26989
27232
|
}
|
|
26990
27233
|
const columns = dtSchema.columns.map((c) => c.name);
|
|
26991
|
-
const
|
|
26992
|
-
const columnList = columns.map(
|
|
26993
|
-
const orderCol =
|
|
27234
|
+
const quoteIdent5 = getQuoteIdent(dialect);
|
|
27235
|
+
const columnList = columns.map(quoteIdent5).join(", ");
|
|
27236
|
+
const orderCol = quoteIdent5(columns[0]);
|
|
26994
27237
|
let globalIndex = 0;
|
|
26995
27238
|
let loaded = 0;
|
|
26996
27239
|
let processed = 0;
|
|
@@ -27748,10 +27991,40 @@ var Context = class {
|
|
|
27748
27991
|
throw new Error("SQLite does not support stored procedures.");
|
|
27749
27992
|
}
|
|
27750
27993
|
const params = args[0];
|
|
27994
|
+
if (this.dialect === "postgres") {
|
|
27995
|
+
return this.#executeProcPostgres(name, params);
|
|
27996
|
+
}
|
|
27751
27997
|
const query = buildProcCall(this.dialect, name, params);
|
|
27752
27998
|
const result = await query.execute(this.kysely);
|
|
27753
27999
|
return result.rows ?? [];
|
|
27754
28000
|
}
|
|
28001
|
+
/**
|
|
28002
|
+
* Execute a stored procedure on PostgreSQL with FUNCTION fallback.
|
|
28003
|
+
*
|
|
28004
|
+
* PostgreSQL distinguishes between PROCEDURE and FUNCTION objects:
|
|
28005
|
+
* `CALL` only works against true procedures and raises SQLSTATE 42809
|
|
28006
|
+
* (`<name>(...) is not a procedure`) when the target is a function.
|
|
28007
|
+
*
|
|
28008
|
+
* The noorm playbook tells schema authors to use `CREATE PROCEDURE`
|
|
28009
|
+
* for void-returning ops and `CREATE FUNCTION` for row-returning ops,
|
|
28010
|
+
* and `ctx.proc()` is expected to handle both. So if `CALL` fails
|
|
28011
|
+
* with the wrong-object-type error, we retry with
|
|
28012
|
+
* `SELECT * FROM <name>(...)`, which works for both scalar and
|
|
28013
|
+
* set-returning PG functions.
|
|
28014
|
+
*/
|
|
28015
|
+
async #executeProcPostgres(name, params) {
|
|
28016
|
+
const callQuery = buildProcCall("postgres", name, params);
|
|
28017
|
+
const [callResult, callErr] = await attempt(() => callQuery.execute(this.kysely));
|
|
28018
|
+
if (!callErr) {
|
|
28019
|
+
return callResult.rows ?? [];
|
|
28020
|
+
}
|
|
28021
|
+
if (!isFunctionNotProcedureError(callErr)) {
|
|
28022
|
+
throw callErr;
|
|
28023
|
+
}
|
|
28024
|
+
const tvfQuery = buildTvfCall("postgres", name, params);
|
|
28025
|
+
const tvfResult = await tvfQuery.execute(this.kysely);
|
|
28026
|
+
return tvfResult.rows ?? [];
|
|
28027
|
+
}
|
|
27755
28028
|
/**
|
|
27756
28029
|
* Call a database function and return the scalar result.
|
|
27757
28030
|
*
|
|
@@ -27867,6 +28140,14 @@ var Context = class {
|
|
|
27867
28140
|
return ready;
|
|
27868
28141
|
}
|
|
27869
28142
|
};
|
|
28143
|
+
function isFunctionNotProcedureError(err) {
|
|
28144
|
+
if (!err || typeof err !== "object") return false;
|
|
28145
|
+
const obj = err;
|
|
28146
|
+
if (obj.code !== "42809" && obj.code !== "42883") return false;
|
|
28147
|
+
const msg = String(obj.message ?? "");
|
|
28148
|
+
const hint = String(obj.hint ?? "");
|
|
28149
|
+
return msg.includes("is not a procedure") || msg.includes("procedure") || hint.includes("use SELECT");
|
|
28150
|
+
}
|
|
27870
28151
|
|
|
27871
28152
|
// src/sdk/index.ts
|
|
27872
28153
|
function toSettingsProvider(manager) {
|
|
@@ -27874,6 +28155,11 @@ function toSettingsProvider(manager) {
|
|
|
27874
28155
|
}
|
|
27875
28156
|
async function createContext(options = {}) {
|
|
27876
28157
|
const projectRoot = options.projectRoot ?? process.cwd();
|
|
28158
|
+
const envIdentity = loadIdentityFromEnv();
|
|
28159
|
+
if (envIdentity) {
|
|
28160
|
+
setKeyOverride(envIdentity.privateKey);
|
|
28161
|
+
setIdentityOverride(envIdentity.identity);
|
|
28162
|
+
}
|
|
27877
28163
|
await initState(projectRoot);
|
|
27878
28164
|
const state = getStateManager(projectRoot);
|
|
27879
28165
|
const settingsManager = getSettingsManager(projectRoot);
|