@noormdev/sdk 1.0.0-alpha.25 → 1.0.0-alpha.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3920,6 +3920,57 @@ export declare class Context<DB = unknown, Procs = object, Funcs = object, Tvfs
3920
3920
  impersonate<T>(username: string, fn: (scope: ImpersonatedScope<DB, Procs, Funcs, Tvfs>) => Promise<T>): Promise<T>;
3921
3921
  impersonate(username: string): Promise<ImpersonatedScope<DB, Procs, Funcs, Tvfs>>;
3922
3922
  }
3923
+ /**
3924
+ * A TVP marker that wraps row data with the required table type name.
3925
+ *
3926
+ * Use the `tvp()` helper to create instances. Column names are
3927
+ * inferred from the object keys of the first row.
3928
+ *
3929
+ * @example
3930
+ * ```typescript
3931
+ * interface CheckoutItem { Type: string; ReferenceNo: number; Qty: number }
3932
+ *
3933
+ * interface MyProcs {
3934
+ * 'Checkout_trx': [{ Party: number; Items: TvpValue<CheckoutItem> }, void];
3935
+ * }
3936
+ * ```
3937
+ */
3938
+ export interface TvpValue<T extends Record<string, unknown> = Record<string, unknown>> {
3939
+ readonly __noorm_tvp: true;
3940
+ readonly typeName: string;
3941
+ readonly rows: ReadonlyArray<T>;
3942
+ }
3943
+ /**
3944
+ * Create a Table-Valued Parameter for MSSQL stored procedure calls.
3945
+ *
3946
+ * Wraps an array of row objects as a TVP with the specified SQL Server
3947
+ * table type name. Column names are inferred from the first row's keys.
3948
+ *
3949
+ * @param typeName - SQL Server table type (e.g., 'CheckoutItems' or 'dbo.CheckoutItems')
3950
+ * @param rows - Array of row objects with consistent keys
3951
+ *
3952
+ * @example
3953
+ * ```typescript
3954
+ * interface CheckoutItem { Type: number; ReferenceNo: number; Qty: number }
3955
+ *
3956
+ * // Row type is inferred — returns TvpValue<CheckoutItem>
3957
+ * const items: CheckoutItem[] = [
3958
+ * { Type: 1, ReferenceNo: 100, Qty: 5 },
3959
+ * { Type: 2, ReferenceNo: 200, Qty: 3 },
3960
+ * ];
3961
+ *
3962
+ * // Named params — type-checked against TvpValue<CheckoutItem>
3963
+ * await ctx.proc('Checkout_trx', {
3964
+ * Party: 1,
3965
+ * PaymentMethod: 2,
3966
+ * Items: tvp('CheckoutItems', items),
3967
+ * });
3968
+ *
3969
+ * // Positional params
3970
+ * await ctx.proc('Checkout_trx', [1, 2, tvp('CheckoutItems', items)]);
3971
+ * ```
3972
+ */
3973
+ export declare function tvp<T extends Record<string, unknown>>(typeName: string, rows: T[]): TvpValue<T>;
3923
3974
  /**
3924
3975
  * Error thrown when requireTest is enabled but config.isTest is false.
3925
3976
  *
package/dist/index.js CHANGED
@@ -16781,10 +16781,45 @@ async function waitForIdentityToLoad(db, dialect) {
16781
16781
  context: { identity }
16782
16782
  });
16783
16783
  }
16784
+
16785
+ // src/sdk/tvp.ts
16786
+ var MSSQL_PARAM_LIMIT = 2100;
16787
+ function tvp(typeName, rows) {
16788
+ if (!typeName) {
16789
+ throw new Error("TVP type name is required.");
16790
+ }
16791
+ if (!Array.isArray(rows)) {
16792
+ throw new Error("TVP rows must be an array.");
16793
+ }
16794
+ if (rows.length > 1) {
16795
+ const expectedKeys = Object.keys(rows[0]).sort().join(",");
16796
+ for (let i = 1; i < rows.length; i++) {
16797
+ const rowKeys = Object.keys(rows[i]).sort().join(",");
16798
+ if (rowKeys !== expectedKeys) {
16799
+ throw new Error(
16800
+ `TVP row ${i} has mismatched keys. Expected [${Object.keys(rows[0]).join(", ")}] but got [${Object.keys(rows[i]).join(", ")}].`
16801
+ );
16802
+ }
16803
+ }
16804
+ }
16805
+ return {
16806
+ __noorm_tvp: true,
16807
+ typeName,
16808
+ rows
16809
+ };
16810
+ }
16811
+ function isTvp(value) {
16812
+ return typeof value === "object" && value !== null && "__noorm_tvp" in value && value["__noorm_tvp"] === true;
16813
+ }
16814
+
16815
+ // src/sdk/sql.ts
16784
16816
  function buildProcCall(dialect, name, params) {
16785
16817
  if (dialect === "sqlite") {
16786
16818
  throw new Error("SQLite does not support stored procedures.");
16787
16819
  }
16820
+ if (params && hasTvpParam(params) && dialect !== "mssql") {
16821
+ throw new Error("Table-valued parameters (TVP) are only supported on MSSQL.");
16822
+ }
16788
16823
  const rawName = sql.raw(name);
16789
16824
  if (!params || Array.isArray(params) && params.length === 0 || !Array.isArray(params) && Object.keys(params).length === 0) {
16790
16825
  if (dialect === "mssql") {
@@ -16804,11 +16839,17 @@ function buildFuncCall(dialect, name, column, params) {
16804
16839
  if (dialect === "sqlite") {
16805
16840
  throw new Error("SQLite does not support database function calls.");
16806
16841
  }
16842
+ if (params && hasTvpParam(params) && dialect !== "mssql") {
16843
+ throw new Error("Table-valued parameters (TVP) are only supported on MSSQL.");
16844
+ }
16807
16845
  const rawName = sql.raw(name);
16808
16846
  const rawCol = sql.raw(column);
16809
16847
  if (!params || Array.isArray(params) && params.length === 0 || !Array.isArray(params) && Object.keys(params).length === 0) {
16810
16848
  return sql`SELECT ${rawName}() AS ${rawCol}`;
16811
16849
  }
16850
+ if (dialect === "mssql" && hasTvpParam(params)) {
16851
+ return buildMssqlFuncWithTvp(rawName, rawCol, params);
16852
+ }
16812
16853
  if (dialect === "postgres" && !Array.isArray(params)) {
16813
16854
  const parts = Object.entries(params).map(
16814
16855
  ([key, val]) => sql`${sql.raw(key)} => ${val}`
@@ -16835,10 +16876,16 @@ function buildTvfCall(dialect, name, params) {
16835
16876
  if (dialect === "mysql") {
16836
16877
  throw new Error("MySQL does not support table-valued functions.");
16837
16878
  }
16879
+ if (params && hasTvpParam(params) && dialect !== "mssql") {
16880
+ throw new Error("Table-valued parameters (TVP) are only supported on MSSQL.");
16881
+ }
16838
16882
  const rawName = sql.raw(name);
16839
16883
  if (!params || Array.isArray(params) && params.length === 0 || !Array.isArray(params) && Object.keys(params).length === 0) {
16840
16884
  return sql`SELECT * FROM ${rawName}()`;
16841
16885
  }
16886
+ if (dialect === "mssql" && hasTvpParam(params)) {
16887
+ return buildMssqlTvfWithTvp(rawName, params);
16888
+ }
16842
16889
  if (dialect === "postgres" && !Array.isArray(params)) {
16843
16890
  const parts = Object.entries(params).map(
16844
16891
  ([key, val]) => sql`${sql.raw(key)} => ${val}`
@@ -16850,6 +16897,9 @@ function buildTvfCall(dialect, name, params) {
16850
16897
  return sql`SELECT * FROM ${rawName}(${joined})`;
16851
16898
  }
16852
16899
  function buildMssqlProc(rawName, params) {
16900
+ if (hasTvpParam(params)) {
16901
+ return buildMssqlProcWithTvp(rawName, params);
16902
+ }
16853
16903
  if (Array.isArray(params)) {
16854
16904
  const joined = sql.join(params.map((v) => sql`${v}`));
16855
16905
  return sql`EXEC ${rawName} ${joined}`;
@@ -16874,6 +16924,137 @@ function buildMysqlProc(rawName, params) {
16874
16924
  const joined = sql.join(values.map((v) => sql`${v}`));
16875
16925
  return sql`CALL ${rawName}(${joined})`;
16876
16926
  }
16927
+ function hasTvpParam(params) {
16928
+ const values = Array.isArray(params) ? params : Object.values(params);
16929
+ return values.some(isTvp);
16930
+ }
16931
+ function validateTvpParamCount(params) {
16932
+ const values = Array.isArray(params) ? params : Object.values(params);
16933
+ let count = 0;
16934
+ for (const val of values) {
16935
+ if (isTvp(val)) {
16936
+ if (val.rows.length > 0) {
16937
+ count += val.rows.length * Object.keys(val.rows[0]).length;
16938
+ }
16939
+ } else {
16940
+ count += 1;
16941
+ }
16942
+ }
16943
+ if (count > MSSQL_PARAM_LIMIT) {
16944
+ throw new Error(
16945
+ "TVP parameter count (" + count + ") exceeds MSSQL limit of " + MSSQL_PARAM_LIMIT + ". Split your TVP rows into smaller batches and call the procedure multiple times."
16946
+ );
16947
+ }
16948
+ }
16949
+ function buildMssqlProcWithTvp(rawName, params) {
16950
+ validateTvpParamCount(params);
16951
+ const preamble = [];
16952
+ const execParts = [];
16953
+ if (Array.isArray(params)) {
16954
+ for (let i = 0; i < params.length; i++) {
16955
+ const val = params[i];
16956
+ if (isTvp(val)) {
16957
+ const varName = `@__tvp_${i}`;
16958
+ appendTvpStatements(preamble, varName, val);
16959
+ execParts.push(sql.raw(varName));
16960
+ } else {
16961
+ execParts.push(sql`${val}`);
16962
+ }
16963
+ }
16964
+ } else {
16965
+ for (const [key, val] of Object.entries(params)) {
16966
+ if (isTvp(val)) {
16967
+ const varName = `@__tvp_${key}`;
16968
+ appendTvpStatements(preamble, varName, val);
16969
+ execParts.push(sql`${sql.raw(`@${key}`)} = ${sql.raw(varName)}`);
16970
+ } else {
16971
+ execParts.push(sql`${sql.raw(`@${key}`)} = ${val}`);
16972
+ }
16973
+ }
16974
+ }
16975
+ const execStmt = sql`EXEC ${rawName} ${sql.join(execParts)}`;
16976
+ if (preamble.length === 0) {
16977
+ return execStmt;
16978
+ }
16979
+ return sql.join([...preamble, execStmt], sql.raw("; "));
16980
+ }
16981
+ function appendTvpStatements(statements, varName, tvpVal) {
16982
+ statements.push(sql.raw(`DECLARE ${varName} ${tvpVal.typeName}`));
16983
+ if (tvpVal.rows.length === 0) return;
16984
+ const cols = Object.keys(tvpVal.rows[0]);
16985
+ const colList = cols.map((c) => `[${c}]`).join(", ");
16986
+ const rowFragments = tvpVal.rows.map((row) => {
16987
+ const values = cols.map((c) => sql`${row[c]}`);
16988
+ return sql`(${sql.join(values)})`;
16989
+ });
16990
+ statements.push(
16991
+ sql`INSERT INTO ${sql.raw(varName)} (${sql.raw(colList)}) VALUES ${sql.join(rowFragments)}`
16992
+ );
16993
+ }
16994
+ function buildMssqlFuncWithTvp(rawName, rawCol, params) {
16995
+ validateTvpParamCount(params);
16996
+ const preamble = [];
16997
+ const execParts = [];
16998
+ if (Array.isArray(params)) {
16999
+ for (let i = 0; i < params.length; i++) {
17000
+ const val = params[i];
17001
+ if (isTvp(val)) {
17002
+ const varName = `@__tvp_${i}`;
17003
+ appendTvpStatements(preamble, varName, val);
17004
+ execParts.push(sql.raw(varName));
17005
+ } else {
17006
+ execParts.push(sql`${val}`);
17007
+ }
17008
+ }
17009
+ } else {
17010
+ for (const [key, val] of Object.entries(params)) {
17011
+ if (isTvp(val)) {
17012
+ const varName = `@__tvp_${key}`;
17013
+ appendTvpStatements(preamble, varName, val);
17014
+ execParts.push(sql`${sql.raw(`@${key}`)} = ${sql.raw(varName)}`);
17015
+ } else {
17016
+ execParts.push(sql`${sql.raw(`@${key}`)} = ${val}`);
17017
+ }
17018
+ }
17019
+ }
17020
+ const funcStmt = sql`DECLARE @__result sql_variant; EXEC @__result = ${rawName} ${sql.join(execParts)}; SELECT @__result AS ${rawCol}`;
17021
+ if (preamble.length === 0) {
17022
+ return funcStmt;
17023
+ }
17024
+ return sql.join([...preamble, funcStmt], sql.raw("; "));
17025
+ }
17026
+ function buildMssqlTvfWithTvp(rawName, params) {
17027
+ validateTvpParamCount(params);
17028
+ const preamble = [];
17029
+ const callParts = [];
17030
+ if (Array.isArray(params)) {
17031
+ for (let i = 0; i < params.length; i++) {
17032
+ const val = params[i];
17033
+ if (isTvp(val)) {
17034
+ const varName = `@__tvp_${i}`;
17035
+ appendTvpStatements(preamble, varName, val);
17036
+ callParts.push(sql.raw(varName));
17037
+ } else {
17038
+ callParts.push(sql`${val}`);
17039
+ }
17040
+ }
17041
+ } else {
17042
+ for (const [key, val] of Object.entries(params)) {
17043
+ if (isTvp(val)) {
17044
+ const varName = `@__tvp_${key}`;
17045
+ appendTvpStatements(preamble, varName, val);
17046
+ callParts.push(sql.raw(varName));
17047
+ } else {
17048
+ callParts.push(sql`${val}`);
17049
+ }
17050
+ }
17051
+ }
17052
+ const selectStmt = sql`SELECT * FROM ${rawName}(${sql.join(callParts)})`;
17053
+ if (preamble.length === 0) {
17054
+ return selectStmt;
17055
+ }
17056
+ return sql.join([...preamble, selectStmt], sql.raw("; "));
17057
+ }
16877
17058
  var ChangeValidationError = class extends Error {
16878
17059
  constructor(changeName, issue2) {
16879
17060
  super(`Invalid change '${changeName}': ${issue2}`);
@@ -23351,8 +23532,14 @@ async function teardownSchema(db, dialect, options = {}) {
23351
23532
  statements.push(ops.dropProcedure(proc.name, proc.schema));
23352
23533
  }
23353
23534
  }
23354
- if (!options.keepTypes) {
23355
- for (const type of types) {
23535
+ const skipTypesDueToKeptRoutines = dialect === "mssql" && (options.keepFunctions || options.keepProcedures);
23536
+ if (!options.keepTypes && !skipTypesDueToKeptRoutines) {
23537
+ const sortedTypes = [...types].sort((a, b) => {
23538
+ if (a.kind === "composite" && b.kind !== "composite") return -1;
23539
+ if (a.kind !== "composite" && b.kind === "composite") return 1;
23540
+ return 0;
23541
+ });
23542
+ for (const type of sortedTypes) {
23356
23543
  dropped.types.push(type.name);
23357
23544
  statements.push(ops.dropType(type.name, type.schema));
23358
23545
  }
@@ -27528,6 +27715,6 @@ async function createContext(options = {}) {
27528
27715
  return new Context(config2, settings, identity, options, projectRoot);
27529
27716
  }
27530
27717
 
27531
- export { ChangeAlreadyAppliedError, ChangeNotAppliedError, ChangeNotFoundError, ChangeOrphanedError, ChangeValidationError, ChangesNamespace, Context, DbNamespace, DtNamespace, ImpersonationError, LockAcquireError, LockExpiredError, LockNamespace, ManifestReferenceError, NoormOps, ProtectedConfigError, RequireTestError, RunNamespace, SecretsNamespace, TemplatesNamespace, TransferNamespace, UtilsNamespace, VaultNamespace, createContext };
27718
+ export { ChangeAlreadyAppliedError, ChangeNotAppliedError, ChangeNotFoundError, ChangeOrphanedError, ChangeValidationError, ChangesNamespace, Context, DbNamespace, DtNamespace, ImpersonationError, LockAcquireError, LockExpiredError, LockNamespace, ManifestReferenceError, NoormOps, ProtectedConfigError, RequireTestError, RunNamespace, SecretsNamespace, TemplatesNamespace, TransferNamespace, UtilsNamespace, VaultNamespace, createContext, tvp };
27532
27719
  //# sourceMappingURL=index.js.map
27533
27720
  //# sourceMappingURL=index.js.map