@effect/sql-mssql 4.0.0-beta.9 → 4.0.0-beta.90

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.
@@ -1,7 +1,19 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Microsoft SQL Server client implementation for Effect SQL, backed by the
3
+ * `tedious` driver.
4
+ *
5
+ * This module provides the `MssqlClient` service, constructors, layers, and SQL
6
+ * Server statement compiler. `make` creates a pooled Tedious client, checks the
7
+ * connection with `SELECT 1`, maps SQL Server failures to `SqlError`, and
8
+ * supports transactions with savepoints. The SQL Server-specific service adds
9
+ * typed Tedious parameters with `param`, stored procedure calls with `call`,
10
+ * direct or config-backed layers, and default parameter type mappings.
11
+ * Streaming queries are not implemented by this driver.
12
+ *
13
+ * @since 4.0.0
3
14
  */
4
15
  import * as Config from "effect/Config"
16
+ import * as Context from "effect/Context"
5
17
  import * as Duration from "effect/Duration"
6
18
  import * as Effect from "effect/Effect"
7
19
  import { identity } from "effect/Function"
@@ -9,12 +21,23 @@ import * as Layer from "effect/Layer"
9
21
  import * as Pool from "effect/Pool"
10
22
  import * as Redacted from "effect/Redacted"
11
23
  import * as Scope from "effect/Scope"
12
- import * as ServiceMap from "effect/ServiceMap"
13
24
  import * as Stream from "effect/Stream"
14
25
  import * as Reactivity from "effect/unstable/reactivity/Reactivity"
15
26
  import * as Client from "effect/unstable/sql/SqlClient"
16
27
  import type { Connection } from "effect/unstable/sql/SqlConnection"
17
- import { SqlError } from "effect/unstable/sql/SqlError"
28
+ import {
29
+ AuthenticationError,
30
+ AuthorizationError,
31
+ ConnectionError,
32
+ ConstraintError,
33
+ DeadlockError,
34
+ LockTimeoutError,
35
+ SerializationError,
36
+ SqlError,
37
+ SqlSyntaxError,
38
+ UniqueViolation,
39
+ UnknownError
40
+ } from "effect/unstable/sql/SqlError"
18
41
  import * as Statement from "effect/unstable/sql/Statement"
19
42
  import * as Tedious from "tedious"
20
43
  import type { ConnectionOptions } from "tedious/lib/connection.ts"
@@ -28,21 +51,116 @@ const ATTR_DB_NAMESPACE = "db.namespace"
28
51
  const ATTR_SERVER_ADDRESS = "server.address"
29
52
  const ATTR_SERVER_PORT = "server.port"
30
53
 
54
+ const mssqlNumberFromCause = (cause: unknown): number | undefined => {
55
+ if (typeof cause !== "object" || cause === null || !("number" in cause)) {
56
+ return undefined
57
+ }
58
+ const number = cause.number
59
+ return typeof number === "number" ? number : undefined
60
+ }
61
+
62
+ const mssqlConnectionErrorCodes = new Set([233, 10054])
63
+ const mssqlAuthenticationErrorCodes = new Set([4060, 18452, 18456])
64
+ const mssqlAuthorizationErrorCodes = new Set([229, 230, 262, 297, 300])
65
+ const mssqlSyntaxErrorCodes = new Set([102, 207, 208, 2714])
66
+ const mssqlConstraintErrorCodes = new Set([515, 547])
67
+
68
+ const UNKNOWN_CONSTRAINT = "unknown"
69
+
70
+ const normalizeConstraintIdentifier = (identifier: unknown): string => {
71
+ if (typeof identifier !== "string") {
72
+ return UNKNOWN_CONSTRAINT
73
+ }
74
+ const trimmed = identifier.trim()
75
+ return trimmed.length === 0 ? UNKNOWN_CONSTRAINT : trimmed
76
+ }
77
+
78
+ const mssqlCauseProperty = (cause: unknown, property: "constraint" | "message"): unknown => {
79
+ if (typeof cause !== "object" || cause === null || !(property in cause)) {
80
+ return undefined
81
+ }
82
+ return (cause as Record<string, unknown>)[property]
83
+ }
84
+
85
+ const mssqlUniqueViolationConstraintFromMessage = (number: 2601 | 2627, message: unknown): string => {
86
+ if (typeof message !== "string") {
87
+ return UNKNOWN_CONSTRAINT
88
+ }
89
+ const match = number === 2627 ?
90
+ /\bconstraint\s+'([^']*)'/i.exec(message) :
91
+ /\bunique index\s+'([^']*)'/i.exec(message)
92
+ return match === null ? UNKNOWN_CONSTRAINT : normalizeConstraintIdentifier(match[1])
93
+ }
94
+
95
+ const mssqlUniqueViolationConstraintFromCause = (number: 2601 | 2627, cause: unknown): string => {
96
+ const constraint = normalizeConstraintIdentifier(mssqlCauseProperty(cause, "constraint"))
97
+ if (constraint !== UNKNOWN_CONSTRAINT) {
98
+ return constraint
99
+ }
100
+ return mssqlUniqueViolationConstraintFromMessage(number, mssqlCauseProperty(cause, "message"))
101
+ }
102
+
103
+ const classifyError = (
104
+ cause: unknown,
105
+ message: string,
106
+ operation: string,
107
+ fallback: "connection" | "unknown" = "unknown"
108
+ ) => {
109
+ const props = { cause, message, operation }
110
+ const number = mssqlNumberFromCause(cause)
111
+ if (number !== undefined) {
112
+ if (mssqlConnectionErrorCodes.has(number)) {
113
+ return new ConnectionError(props)
114
+ }
115
+ if (mssqlAuthenticationErrorCodes.has(number)) {
116
+ return new AuthenticationError(props)
117
+ }
118
+ if (mssqlAuthorizationErrorCodes.has(number)) {
119
+ return new AuthorizationError(props)
120
+ }
121
+ if (mssqlSyntaxErrorCodes.has(number)) {
122
+ return new SqlSyntaxError(props)
123
+ }
124
+ if (number === 2601 || number === 2627) {
125
+ return new UniqueViolation({ ...props, constraint: mssqlUniqueViolationConstraintFromCause(number, cause) })
126
+ }
127
+ if (mssqlConstraintErrorCodes.has(number)) {
128
+ return new ConstraintError(props)
129
+ }
130
+ if (number === 1205) {
131
+ return new DeadlockError(props)
132
+ }
133
+ if (number === 3960) {
134
+ return new SerializationError(props)
135
+ }
136
+ if (number === 1222) {
137
+ return new LockTimeoutError(props)
138
+ }
139
+ }
140
+ return fallback === "connection" ? new ConnectionError(props) : new UnknownError(props)
141
+ }
142
+
31
143
  /**
32
- * @category type ids
33
- * @since 1.0.0
144
+ * Runtime type identifier used to mark `MssqlClient` values.
145
+ *
146
+ * @category type IDs
147
+ * @since 4.0.0
34
148
  */
35
149
  export const TypeId: unique symbol = Symbol.for("@effect/sql-mssql/MssqlClient")
36
150
 
37
151
  /**
38
- * @category type ids
39
- * @since 1.0.0
152
+ * Type-level identifier used to mark `MssqlClient` values.
153
+ *
154
+ * @category type IDs
155
+ * @since 4.0.0
40
156
  */
41
157
  export type TypeId = typeof TypeId
42
158
 
43
159
  /**
160
+ * Microsoft SQL Server client service, extending `SqlClient` with typed parameter fragments and stored procedure calls.
161
+ *
44
162
  * @category models
45
- * @since 1.0.0
163
+ * @since 4.0.0
46
164
  */
47
165
  export interface MssqlClient extends Client.SqlClient {
48
166
  readonly [TypeId]: TypeId
@@ -65,14 +183,23 @@ export interface MssqlClient extends Client.SqlClient {
65
183
  }
66
184
 
67
185
  /**
68
- * @category tags
69
- * @since 1.0.0
186
+ * Service tag for the Microsoft SQL Server client service.
187
+ *
188
+ * **When to use**
189
+ *
190
+ * Use to access or provide a Microsoft SQL Server client through the Effect
191
+ * context.
192
+ *
193
+ * @category services
194
+ * @since 4.0.0
70
195
  */
71
- export const MssqlClient = ServiceMap.Service<MssqlClient>("@effect/sql-mssql/MssqlClient")
196
+ export const MssqlClient = Context.Service<MssqlClient>("@effect/sql-mssql/MssqlClient")
72
197
 
73
198
  /**
199
+ * Configuration for a Microsoft SQL Server client, including connection, authentication, pool, parameter type, span attribute, and query/result name transform options.
200
+ *
74
201
  * @category models
75
- * @since 1.0.0
202
+ * @since 4.0.0
76
203
  */
77
204
  export interface MssqlClientConfig {
78
205
  readonly domain?: string | undefined
@@ -86,6 +213,10 @@ export interface MssqlClientConfig {
86
213
  readonly username?: string | undefined
87
214
  readonly password?: Redacted.Redacted | undefined
88
215
  readonly connectTimeout?: Duration.Input | undefined
216
+ readonly cancelTimeout?: Duration.Input | undefined
217
+ readonly connectionRetryInterval?: Duration.Input | undefined
218
+ readonly multiSubnetFailover?: boolean | undefined
219
+ readonly maxRetriesOnTransientErrors?: number | undefined
89
220
 
90
221
  readonly minConnections?: number | undefined
91
222
  readonly maxConnections?: number | undefined
@@ -111,14 +242,18 @@ interface MssqlConnection extends Connection {
111
242
  readonly rollback: (name?: string) => Effect.Effect<void, SqlError>
112
243
  }
113
244
 
114
- const TransactionConnection = Client.TransactionConnection as unknown as ServiceMap.Service<
245
+ const TransactionConnection = Client.TransactionConnection as unknown as (clientId: number) => Context.Service<
115
246
  readonly [conn: MssqlConnection, counter: number],
116
247
  readonly [conn: MssqlConnection, counter: number]
117
248
  >
118
249
 
250
+ let clientIdCounter = 0
251
+
119
252
  /**
253
+ * Creates a scoped Microsoft SQL Server client backed by a connection pool, with transaction and stored procedure support. Streaming queries are not implemented.
254
+ *
120
255
  * @category constructors
121
- * @since 1.0.0
256
+ * @since 4.0.0
122
257
  */
123
258
  export const make = (
124
259
  options: MssqlClientConfig
@@ -149,13 +284,21 @@ export const make = (
149
284
  port: options.port,
150
285
  database: options.database,
151
286
  trustServerCertificate: options.trustServer ?? true,
287
+ multiSubnetFailover: options.multiSubnetFailover,
152
288
  connectTimeout: options.connectTimeout
153
289
  ? Duration.toMillis(Duration.fromInputUnsafe(options.connectTimeout))
154
290
  : undefined,
155
291
  rowCollectionOnRequestCompletion: true,
156
292
  useColumnNames: false,
157
293
  instanceName: options.instanceName,
158
- encrypt: options.encrypt ?? false
294
+ encrypt: options.encrypt ?? false,
295
+ cancelTimeout: options.cancelTimeout
296
+ ? Duration.toMillis(Duration.fromInputUnsafe(options.cancelTimeout))
297
+ : undefined,
298
+ connectionRetryInterval: options.connectionRetryInterval
299
+ ? Duration.toMillis(Duration.fromInputUnsafe(options.connectionRetryInterval))
300
+ : undefined,
301
+ maxRetriesOnTransientErrors: options.maxRetriesOnTransientErrors
159
302
  } as ConnectionOptions,
160
303
  server: options.server,
161
304
  authentication: {
@@ -174,7 +317,9 @@ export const make = (
174
317
  yield* Effect.callback<void, SqlError>((resume) => {
175
318
  conn.connect((cause) => {
176
319
  if (cause) {
177
- resume(Effect.fail(new SqlError({ cause, message: "Failed to connect" })))
320
+ resume(
321
+ Effect.fail(new SqlError({ reason: classifyError(cause, "Failed to connect", "connect", "connection") }))
322
+ )
178
323
  } else {
179
324
  resume(Effect.void)
180
325
  }
@@ -189,7 +334,9 @@ export const make = (
189
334
  Effect.callback<any, SqlError>((resume) => {
190
335
  const req = new Tedious.Request(sql, (cause, _rowCount, result) => {
191
336
  if (cause) {
192
- resume(Effect.fail(new SqlError({ cause, message: "Failed to execute statement" })))
337
+ resume(
338
+ Effect.fail(new SqlError({ reason: classifyError(cause, "Failed to execute statement", "execute") }))
339
+ )
193
340
  return
194
341
  }
195
342
 
@@ -232,7 +379,9 @@ export const make = (
232
379
  escape(procedure.name),
233
380
  (cause, _, rows) => {
234
381
  if (cause) {
235
- resume(Effect.fail(new SqlError({ cause, message: "Failed to execute statement" })))
382
+ resume(
383
+ Effect.fail(new SqlError({ reason: classifyError(cause, "Failed to execute statement", "execute") }))
384
+ )
236
385
  } else {
237
386
  rows = rowsToObjects(rows)
238
387
  if (transformRows) {
@@ -279,6 +428,9 @@ export const make = (
279
428
  executeValues(sql, params) {
280
429
  return run(sql, params, true)
281
430
  },
431
+ executeValuesUnprepared(sql, params) {
432
+ return run(sql, params, true)
433
+ },
282
434
  executeUnprepared(sql, params, transformRows) {
283
435
  return this.execute(sql, params, transformRows)
284
436
  },
@@ -291,7 +443,13 @@ export const make = (
291
443
  begin: Effect.callback<void, SqlError>((resume) => {
292
444
  conn.beginTransaction((cause) => {
293
445
  if (cause) {
294
- resume(Effect.fail(new SqlError({ cause, message: "Failed to begin transaction" })))
446
+ resume(
447
+ Effect.fail(
448
+ new SqlError({
449
+ reason: classifyError(cause, "Failed to begin transaction", "beginTransaction")
450
+ })
451
+ )
452
+ )
295
453
  } else {
296
454
  resume(Effect.void)
297
455
  }
@@ -300,7 +458,13 @@ export const make = (
300
458
  commit: Effect.callback<void, SqlError>((resume) => {
301
459
  conn.commitTransaction((cause) => {
302
460
  if (cause) {
303
- resume(Effect.fail(new SqlError({ cause, message: "Failed to commit transaction" })))
461
+ resume(
462
+ Effect.fail(
463
+ new SqlError({
464
+ reason: classifyError(cause, "Failed to commit transaction", "commitTransaction")
465
+ })
466
+ )
467
+ )
304
468
  } else {
305
469
  resume(Effect.void)
306
470
  }
@@ -310,7 +474,11 @@ export const make = (
310
474
  Effect.callback<void, SqlError>((resume) => {
311
475
  conn.saveTransaction((cause) => {
312
476
  if (cause) {
313
- resume(Effect.fail(new SqlError({ cause, message: "Failed to create savepoint" })))
477
+ resume(
478
+ Effect.fail(
479
+ new SqlError({ reason: classifyError(cause, "Failed to create savepoint", "createSavepoint") })
480
+ )
481
+ )
314
482
  } else {
315
483
  resume(Effect.void)
316
484
  }
@@ -320,7 +488,13 @@ export const make = (
320
488
  Effect.callback<void, SqlError>((resume) => {
321
489
  conn.rollbackTransaction((cause) => {
322
490
  if (cause) {
323
- resume(Effect.fail(new SqlError({ cause, message: "Failed to rollback transaction" })))
491
+ resume(
492
+ Effect.fail(
493
+ new SqlError({
494
+ reason: classifyError(cause, "Failed to rollback transaction", "rollbackTransaction")
495
+ })
496
+ )
497
+ )
324
498
  } else {
325
499
  resume(Effect.void)
326
500
  }
@@ -349,22 +523,29 @@ export const make = (
349
523
 
350
524
  yield* Pool.get(pool).pipe(
351
525
  Effect.tap((connection) => connection.executeUnprepared("SELECT 1", [], undefined)),
352
- Effect.mapError(({ cause }) => new SqlError({ cause, message: "MssqlClient: Failed to connect" })),
526
+ Effect.mapError((cause) =>
527
+ new SqlError({ reason: classifyError(cause, "MssqlClient: Failed to connect", "connect", "connection") })
528
+ ),
353
529
  Effect.scoped,
354
530
  Effect.timeoutOrElse({
355
531
  duration: options.connectTimeout ?? Duration.seconds(5),
356
- onTimeout: () =>
532
+ orElse: () =>
357
533
  Effect.fail(
358
534
  new SqlError({
359
- message: "MssqlClient: Connection timeout",
360
- cause: new Error("connection timeout")
535
+ reason: new ConnectionError({
536
+ message: "MssqlClient: Connection timeout",
537
+ cause: new Error("connection timeout"),
538
+ operation: "connect"
539
+ })
361
540
  })
362
541
  )
363
542
  })
364
543
  )
365
544
 
545
+ const transactionService = TransactionConnection(clientIdCounter++)
546
+
366
547
  const withTransaction = Client.makeWithTransaction({
367
- transactionService: TransactionConnection,
548
+ transactionService,
368
549
  spanAttributes,
369
550
  acquireConnection: Effect.gen(function*() {
370
551
  const scope = Scope.makeUnsafe()
@@ -382,6 +563,7 @@ export const make = (
382
563
  yield* Client.make({
383
564
  acquirer: Pool.get(pool),
384
565
  compiler,
566
+ transactionService: transactionService as any,
385
567
  spanAttributes,
386
568
  transformRows
387
569
  }),
@@ -426,42 +608,48 @@ export const make = (
426
608
  })
427
609
 
428
610
  /**
611
+ * Creates a layer from a `Config`-wrapped SQL Server client configuration, providing both `MssqlClient` and `SqlClient`.
612
+ *
429
613
  * @category layers
430
- * @since 1.0.0
614
+ * @since 4.0.0
431
615
  */
432
616
  export const layerConfig: (
433
617
  config: Config.Wrap<MssqlClientConfig>
434
618
  ) => Layer.Layer<Client.SqlClient | MssqlClient, Config.ConfigError | SqlError> = (
435
619
  config: Config.Wrap<MssqlClientConfig>
436
620
  ): Layer.Layer<Client.SqlClient | MssqlClient, Config.ConfigError | SqlError> =>
437
- Layer.effectServices(
438
- Config.unwrap(config).asEffect().pipe(
621
+ Layer.effectContext(
622
+ Config.unwrap(config).pipe(
439
623
  Effect.flatMap(make),
440
624
  Effect.map((client) =>
441
- ServiceMap.make(MssqlClient, client).pipe(
442
- ServiceMap.add(Client.SqlClient, client)
625
+ Context.make(MssqlClient, client).pipe(
626
+ Context.add(Client.SqlClient, client)
443
627
  )
444
628
  )
445
629
  )
446
630
  ).pipe(Layer.provide(Reactivity.layer))
447
631
 
448
632
  /**
633
+ * Creates a layer from a concrete SQL Server client configuration, providing both `MssqlClient` and `SqlClient`.
634
+ *
449
635
  * @category layers
450
- * @since 1.0.0
636
+ * @since 4.0.0
451
637
  */
452
638
  export const layer = (
453
639
  config: MssqlClientConfig
454
640
  ): Layer.Layer<Client.SqlClient | MssqlClient, never | SqlError> =>
455
- Layer.effectServices(
641
+ Layer.effectContext(
456
642
  Effect.map(make(config), (client) =>
457
- ServiceMap.make(MssqlClient, client).pipe(
458
- ServiceMap.add(Client.SqlClient, client)
643
+ Context.make(MssqlClient, client).pipe(
644
+ Context.add(Client.SqlClient, client)
459
645
  ))
460
646
  ).pipe(Layer.provide(Reactivity.layer))
461
647
 
462
648
  /**
649
+ * Creates the SQL Server statement compiler, using `@1`-style placeholders, bracket-escaped identifiers, and SQL Server `OUTPUT INSERTED` returning clauses.
650
+ *
463
651
  * @category compiler
464
- * @since 1.0.0
652
+ * @since 4.0.0
465
653
  */
466
654
  export const makeCompiler = (transform?: (_: string) => string) =>
467
655
  Statement.makeCompiler<MssqlCustom>({
@@ -510,7 +698,10 @@ function numberToParamName(n: number) {
510
698
  }
511
699
 
512
700
  /**
513
- * @since 1.0.0
701
+ * Default mapping from Effect SQL primitive value kinds to Tedious SQL Server parameter data types.
702
+ *
703
+ * @category configuration
704
+ * @since 4.0.0
514
705
  */
515
706
  export const defaultParameterTypes: Record<Statement.PrimitiveKind, DataType> = {
516
707
  string: Tedious.TYPES.VarChar,
@@ -1,5 +1,13 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Utilities for applying Effect SQL migrations to Microsoft SQL Server.
3
+ *
4
+ * This module re-exports the shared `Migrator` loaders and error types, then
5
+ * provides `run` and `layer` helpers for applying ordered migrations through
6
+ * the current SQL Server `SqlClient`. `run` returns the applied migration IDs
7
+ * and names, while `layer` runs migrations during layer construction and
8
+ * provides no services.
9
+ *
10
+ * @since 4.0.0
3
11
  */
4
12
  import type * as Effect from "effect/Effect"
5
13
  import * as Layer from "effect/Layer"
@@ -8,13 +16,15 @@ import type * as Client from "effect/unstable/sql/SqlClient"
8
16
  import type { SqlError } from "effect/unstable/sql/SqlError"
9
17
 
10
18
  /**
11
- * @since 1.0.0
19
+ * @since 4.0.0
12
20
  */
13
21
  export * from "effect/unstable/sql/Migrator"
14
22
 
15
23
  /**
16
- * @category constructor
17
- * @since 1.0.0
24
+ * Runs SQL migrations using the configured `SqlClient`, returning the migrations that were applied.
25
+ *
26
+ * @category constructors
27
+ * @since 4.0.0
18
28
  */
19
29
  export const run: <R>(
20
30
  options: Migrator.MigratorOptions<R>
@@ -25,8 +35,10 @@ export const run: <R>(
25
35
  > = Migrator.make({})
26
36
 
27
37
  /**
38
+ * Creates a layer that runs the configured SQL migrations during layer construction.
39
+ *
28
40
  * @category layers
29
- * @since 1.0.0
41
+ * @since 4.0.0
30
42
  */
31
43
  export const layer = <R>(
32
44
  options: Migrator.MigratorOptions<R>
package/src/Parameter.ts CHANGED
@@ -1,25 +1,41 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * Typed SQL Server stored procedure parameter metadata.
3
+ *
4
+ * This module builds {@link Parameter} values that pair a stored procedure
5
+ * parameter name with a Tedious `DataType`, Tedious `ParameterOptions`, and a
6
+ * phantom TypeScript value type. `Procedure.param` and
7
+ * `Procedure.outputParam` use this metadata, and `MssqlClient.call` forwards it
8
+ * to Tedious when registering input and output parameters.
9
+ *
10
+ * @see {@link make} for constructing parameter metadata directly.
11
+ *
12
+ * @since 4.0.0
3
13
  */
4
14
  import { identity } from "effect/Function"
5
15
  import type { DataType } from "tedious/lib/data-type.ts"
6
16
  import type { ParameterOptions } from "tedious/lib/request.ts"
7
17
 
8
18
  /**
9
- * @category type id
10
- * @since 1.0.0
19
+ * Runtime type identifier used to mark SQL Server stored procedure parameter metadata.
20
+ *
21
+ * @category type IDs
22
+ * @since 4.0.0
11
23
  */
12
24
  export const TypeId: TypeId = "~@effect/sql-mssql/Parameter"
13
25
 
14
26
  /**
15
- * @category type id
16
- * @since 1.0.0
27
+ * Type-level identifier used to mark SQL Server stored procedure parameter metadata.
28
+ *
29
+ * @category type IDs
30
+ * @since 4.0.0
17
31
  */
18
32
  export type TypeId = "~@effect/sql-mssql/Parameter"
19
33
 
20
34
  /**
21
- * @category model
22
- * @since 1.0.0
35
+ * Metadata for a SQL Server stored procedure parameter, including its name, Tedious data type, options, and phantom value type.
36
+ *
37
+ * @category models
38
+ * @since 4.0.0
23
39
  */
24
40
  export interface Parameter<out A> {
25
41
  readonly [TypeId]: (_: never) => A
@@ -30,8 +46,10 @@ export interface Parameter<out A> {
30
46
  }
31
47
 
32
48
  /**
33
- * @category constructor
34
- * @since 1.0.0
49
+ * Creates typed metadata for a SQL Server stored procedure parameter.
50
+ *
51
+ * @category constructors
52
+ * @since 4.0.0
35
53
  */
36
54
  export const make = <A>(
37
55
  name: string,