@noormdev/sdk 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2083 @@
1
+ // Generated by dts-bundle-generator v9.5.1
2
+
3
+ import { Kysely } from 'kysely';
4
+
5
+ /**
6
+ * Supported database dialects.
7
+ */
8
+ export type Dialect = "postgres" | "mysql" | "sqlite" | "mssql";
9
+ /**
10
+ * Database connection configuration.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const postgresConfig: ConnectionConfig = {
15
+ * dialect: 'postgres',
16
+ * host: 'localhost',
17
+ * port: 5432,
18
+ * database: 'myapp',
19
+ * user: 'postgres',
20
+ * password: 'secret',
21
+ * }
22
+ *
23
+ * const sqliteConfig: ConnectionConfig = {
24
+ * dialect: 'sqlite',
25
+ * database: './data.db',
26
+ * }
27
+ * ```
28
+ */
29
+ export interface ConnectionConfig {
30
+ dialect: Dialect;
31
+ host?: string;
32
+ port?: number;
33
+ user?: string;
34
+ password?: string;
35
+ database: string;
36
+ filename?: string;
37
+ pool?: {
38
+ min?: number;
39
+ max?: number;
40
+ };
41
+ ssl?: boolean | {
42
+ rejectUnauthorized?: boolean;
43
+ ca?: string;
44
+ cert?: string;
45
+ key?: string;
46
+ };
47
+ }
48
+ /**
49
+ * Full configuration object.
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const config: Config = {
54
+ * name: 'dev',
55
+ * type: 'local',
56
+ * isTest: false,
57
+ * protected: false,
58
+ * connection: {
59
+ * dialect: 'postgres',
60
+ * host: 'localhost',
61
+ * port: 5432,
62
+ * database: 'myapp_dev',
63
+ * user: 'postgres',
64
+ * password: 'postgres',
65
+ * },
66
+ * paths: {
67
+ * sql: './sql',
68
+ * changes: './changes',
69
+ * },
70
+ * }
71
+ * ```
72
+ */
73
+ export interface Config {
74
+ name: string;
75
+ type: "local" | "remote";
76
+ isTest: boolean;
77
+ protected: boolean;
78
+ connection: ConnectionConfig;
79
+ paths: {
80
+ sql: string;
81
+ changes: string;
82
+ };
83
+ identity?: string;
84
+ }
85
+ /**
86
+ * Settings Types
87
+ *
88
+ * Settings define project-wide build behavior and stage configuration.
89
+ * Unlike encrypted configs (credentials), settings are version controlled
90
+ * and shared across the team via .noorm/settings.yml
91
+ */
92
+ /**
93
+ * Secret type hints for CLI input handling.
94
+ *
95
+ * - string: Plain text input
96
+ * - password: Masked input, no echo
97
+ * - api_key: Masked input, validated format
98
+ * - connection_string: Validated as URI
99
+ */
100
+ export type SecretType = "string" | "password" | "api_key" | "connection_string";
101
+ /**
102
+ * Required secret definition for a stage.
103
+ *
104
+ * @example
105
+ * ```yaml
106
+ * secrets:
107
+ * - key: DB_PASSWORD
108
+ * type: password
109
+ * description: Database password
110
+ * required: true
111
+ * ```
112
+ */
113
+ export interface StageSecret {
114
+ /** Secret key name (e.g., DB_PASSWORD) */
115
+ key: string;
116
+ /** Type hint for CLI input handling */
117
+ type: SecretType;
118
+ /** Human-readable description shown in CLI prompts */
119
+ description?: string;
120
+ /** Whether the secret is required (default: true) */
121
+ required?: boolean;
122
+ }
123
+ /**
124
+ * Stage defaults that can be overridden when creating a config.
125
+ *
126
+ * These provide initial values. Some are enforceable constraints:
127
+ * - protected: true - Cannot be overridden to false
128
+ * - isTest: true - Cannot be overridden to false
129
+ * - dialect - Cannot be changed after creation
130
+ */
131
+ export interface StageDefaults {
132
+ dialect?: "postgres" | "mysql" | "sqlite" | "mssql";
133
+ host?: string;
134
+ port?: number;
135
+ database?: string;
136
+ user?: string;
137
+ password?: string;
138
+ ssl?: boolean;
139
+ isTest?: boolean;
140
+ protected?: boolean;
141
+ }
142
+ /**
143
+ * Stage definition - a preconfigured config template.
144
+ *
145
+ * Stages are team-defined config templates with defaults, constraints,
146
+ * and required secrets. They're managed via version control.
147
+ *
148
+ * @example
149
+ * ```yaml
150
+ * stages:
151
+ * prod:
152
+ * description: Production database
153
+ * locked: true
154
+ * defaults:
155
+ * dialect: postgres
156
+ * protected: true
157
+ * secrets:
158
+ * - key: DB_PASSWORD
159
+ * type: password
160
+ * ```
161
+ */
162
+ export interface Stage {
163
+ /** Human-readable description shown in CLI */
164
+ description?: string;
165
+ /** If true, configs linked to this stage cannot be deleted */
166
+ locked?: boolean;
167
+ /** Default values when creating config from this stage */
168
+ defaults?: StageDefaults;
169
+ /** Required secrets that must be set before config is usable */
170
+ secrets?: StageSecret[];
171
+ }
172
+ /**
173
+ * Conditions for rule matching.
174
+ *
175
+ * All conditions in a rule are AND'd together.
176
+ * A rule matches when ALL specified conditions are true.
177
+ */
178
+ export interface RuleMatch {
179
+ /** Config name (exact match) */
180
+ name?: string;
181
+ /** Protected config flag */
182
+ protected?: boolean;
183
+ /** Test database flag */
184
+ isTest?: boolean;
185
+ /** Connection type */
186
+ type?: "local" | "remote";
187
+ }
188
+ /**
189
+ * Stage-based rule for conditional include/exclude.
190
+ *
191
+ * Rules control which files/folders are included or excluded
192
+ * based on the active config's properties.
193
+ *
194
+ * @example
195
+ * ```yaml
196
+ * rules:
197
+ * - match:
198
+ * isTest: true
199
+ * include:
200
+ * - sql/seeds
201
+ * - match:
202
+ * protected: true
203
+ * exclude:
204
+ * - sql/dangerous
205
+ * ```
206
+ */
207
+ export interface Rule {
208
+ /** User-friendly description (e.g., "test seeds", "prod config") */
209
+ description?: string;
210
+ /** Conditions that must all match */
211
+ match: RuleMatch;
212
+ /** Folders to include when rule matches */
213
+ include?: string[];
214
+ /** Folders to exclude when rule matches */
215
+ exclude?: string[];
216
+ }
217
+ /**
218
+ * Build configuration - controls file inclusion and execution order.
219
+ */
220
+ export interface BuildConfig {
221
+ /**
222
+ * Folders to include, executed in this order.
223
+ * First listed = first executed.
224
+ */
225
+ include?: string[];
226
+ /** Folders to exclude from all builds */
227
+ exclude?: string[];
228
+ }
229
+ /**
230
+ * Path configuration - override default locations.
231
+ */
232
+ export interface PathConfig {
233
+ /** Path to SQL files (relative to project root) */
234
+ sql?: string;
235
+ /** Path to change files (relative to project root) */
236
+ changes?: string;
237
+ }
238
+ /**
239
+ * Strict mode configuration.
240
+ *
241
+ * When enabled, requires certain stages to exist before operations can run.
242
+ */
243
+ export interface StrictConfig {
244
+ /** Enable strict mode */
245
+ enabled?: boolean;
246
+ /** Required stage names that must have configs */
247
+ stages?: string[];
248
+ }
249
+ /**
250
+ * Logging configuration.
251
+ */
252
+ export interface LoggingConfig {
253
+ /** Enable file logging */
254
+ enabled?: boolean;
255
+ /** Minimum level to capture: silent, error, warn, info, verbose */
256
+ level?: "silent" | "error" | "warn" | "info" | "verbose";
257
+ /** Log file path (relative to project root) */
258
+ file?: string;
259
+ /** Rotate log when size exceeded (e.g., '10mb') */
260
+ maxSize?: string;
261
+ /** Number of rotated files to keep */
262
+ maxFiles?: number;
263
+ }
264
+ /**
265
+ * Teardown configuration.
266
+ *
267
+ * Controls database reset and teardown behavior.
268
+ *
269
+ * @example
270
+ * ```yaml
271
+ * teardown:
272
+ * preserveTables:
273
+ * - AppSettings
274
+ * - UserRoles
275
+ * postScript: "sql/teardown/cleanup.sql"
276
+ * ```
277
+ */
278
+ export interface TeardownConfig {
279
+ /** Tables to always preserve during truncate operations */
280
+ preserveTables?: string[];
281
+ /** SQL script to run after schema teardown (relative to project root) */
282
+ postScript?: string;
283
+ }
284
+ /**
285
+ * Complete settings configuration.
286
+ *
287
+ * Stored in .noorm/settings.yml and version controlled.
288
+ */
289
+ export interface Settings {
290
+ /** Build configuration */
291
+ build?: BuildConfig;
292
+ /** Path overrides */
293
+ paths?: PathConfig;
294
+ /** Stage-based rules for conditional include/exclude */
295
+ rules?: Rule[];
296
+ /** Preconfigured config templates */
297
+ stages?: Record<string, Stage>;
298
+ /** Strict mode requiring specific stages */
299
+ strict?: StrictConfig;
300
+ /** Logging configuration */
301
+ logging?: LoggingConfig;
302
+ /** Universal secrets required by ALL stages */
303
+ secrets?: StageSecret[];
304
+ /** Database teardown/reset configuration */
305
+ teardown?: TeardownConfig;
306
+ }
307
+ /**
308
+ * Settings events emitted by the settings module.
309
+ */
310
+ export interface SettingsEvents {
311
+ /** Settings loaded from file or defaults */
312
+ "settings:loaded": {
313
+ path: string;
314
+ settings: Settings;
315
+ fromFile: boolean;
316
+ };
317
+ /** Settings saved to disk */
318
+ "settings:saved": {
319
+ path: string;
320
+ };
321
+ /** Settings file initialized */
322
+ "settings:initialized": {
323
+ path: string;
324
+ force: boolean;
325
+ };
326
+ /** Stage added or updated */
327
+ "settings:stage-set": {
328
+ name: string;
329
+ stage: Stage;
330
+ };
331
+ /** Stage removed */
332
+ "settings:stage-removed": {
333
+ name: string;
334
+ };
335
+ /** Rule added */
336
+ "settings:rule-added": {
337
+ rule: Rule;
338
+ };
339
+ /** Rule removed */
340
+ "settings:rule-removed": {
341
+ index: number;
342
+ rule: Rule;
343
+ };
344
+ /** Build config updated */
345
+ "settings:build-updated": {
346
+ build: BuildConfig;
347
+ };
348
+ /** Paths config updated */
349
+ "settings:paths-updated": {
350
+ paths: PathConfig;
351
+ };
352
+ /** Strict mode config updated */
353
+ "settings:strict-updated": {
354
+ strict: StrictConfig;
355
+ };
356
+ /** Logging config updated */
357
+ "settings:logging-updated": {
358
+ logging: LoggingConfig;
359
+ };
360
+ /** Secret added (universal or stage-scoped) */
361
+ "settings:secret-added": {
362
+ secret: StageSecret;
363
+ scope: "universal" | "stage";
364
+ stageName?: string;
365
+ };
366
+ /** Secret updated (universal or stage-scoped) */
367
+ "settings:secret-updated": {
368
+ key: string;
369
+ secret: StageSecret;
370
+ scope: "universal" | "stage";
371
+ stageName?: string;
372
+ };
373
+ /** Secret removed (universal or stage-scoped) */
374
+ "settings:secret-removed": {
375
+ key: string;
376
+ scope: "universal" | "stage";
377
+ stageName?: string;
378
+ };
379
+ }
380
+ /**
381
+ * Identity types.
382
+ *
383
+ * Two types of identity in noorm:
384
+ * 1. Audit Identity - Simple name/email for tracking "who executed this"
385
+ * 2. Cryptographic Identity - Full keypair system for config sharing and team discovery
386
+ */
387
+ /**
388
+ * Source of audit identity resolution.
389
+ */
390
+ export type IdentitySource = "state" | "config" | "env" | "git" | "system";
391
+ /**
392
+ * Resolved audit identity.
393
+ *
394
+ * Used for tracking "who" executed a change or SQL file.
395
+ *
396
+ * @example
397
+ * ```typescript
398
+ * const identity: Identity = {
399
+ * name: 'John Doe',
400
+ * email: 'john@example.com',
401
+ * source: 'git',
402
+ * }
403
+ * ```
404
+ */
405
+ export interface Identity {
406
+ /** User's name */
407
+ name: string;
408
+ /** User's email (optional) */
409
+ email?: string;
410
+ /** How the identity was resolved */
411
+ source: IdentitySource;
412
+ }
413
+ /**
414
+ * Operation status values.
415
+ *
416
+ * - pending: Operation started but not finished
417
+ * - success: Operation completed successfully
418
+ * - failed: Operation failed with error
419
+ * - reverted: Operation was reverted
420
+ * - stale: Operation's schema objects were torn down (needs re-run)
421
+ */
422
+ export type OperationStatus = "pending" | "success" | "failed" | "reverted" | "stale";
423
+ /**
424
+ * Direction values.
425
+ */
426
+ export type Direction = "change" | "revert";
427
+ /**
428
+ * File execution status values.
429
+ */
430
+ export type ExecutionStatus = "pending" | "success" | "failed" | "skipped";
431
+ /**
432
+ * Overview counts for all categories.
433
+ */
434
+ export interface ExploreOverview {
435
+ tables: number;
436
+ views: number;
437
+ procedures: number;
438
+ functions: number;
439
+ types: number;
440
+ indexes: number;
441
+ foreignKeys: number;
442
+ triggers: number;
443
+ locks: number;
444
+ connections: number;
445
+ }
446
+ /**
447
+ * Table summary for list display.
448
+ */
449
+ export interface TableSummary {
450
+ name: string;
451
+ schema?: string;
452
+ columnCount: number;
453
+ rowCountEstimate?: number;
454
+ }
455
+ /**
456
+ * Index summary for list display.
457
+ */
458
+ export interface IndexSummary {
459
+ name: string;
460
+ schema?: string;
461
+ tableName: string;
462
+ tableSchema?: string;
463
+ columns: string[];
464
+ isUnique: boolean;
465
+ isPrimary: boolean;
466
+ }
467
+ /**
468
+ * Foreign key summary for list display.
469
+ */
470
+ export interface ForeignKeySummary {
471
+ name: string;
472
+ schema?: string;
473
+ tableName: string;
474
+ tableSchema?: string;
475
+ columns: string[];
476
+ referencedTable: string;
477
+ referencedSchema?: string;
478
+ referencedColumns: string[];
479
+ onDelete?: string;
480
+ onUpdate?: string;
481
+ }
482
+ /**
483
+ * Column detail for tables/views.
484
+ */
485
+ export interface ColumnDetail {
486
+ name: string;
487
+ dataType: string;
488
+ isNullable: boolean;
489
+ defaultValue?: string;
490
+ isPrimaryKey: boolean;
491
+ ordinalPosition: number;
492
+ }
493
+ /**
494
+ * Full table detail.
495
+ */
496
+ export interface TableDetail {
497
+ name: string;
498
+ schema?: string;
499
+ columns: ColumnDetail[];
500
+ indexes: IndexSummary[];
501
+ foreignKeys: ForeignKeySummary[];
502
+ rowCountEstimate?: number;
503
+ }
504
+ /**
505
+ * Result of a truncate operation.
506
+ */
507
+ export interface TruncateResult {
508
+ /** Tables that were truncated */
509
+ truncated: string[];
510
+ /** Tables that were preserved */
511
+ preserved: string[];
512
+ /** SQL statements executed (or would execute in dry-run) */
513
+ statements: string[];
514
+ /** Duration in milliseconds */
515
+ durationMs: number;
516
+ }
517
+ /**
518
+ * Result of a teardown operation.
519
+ */
520
+ export interface TeardownResult {
521
+ /** Objects dropped by category */
522
+ dropped: {
523
+ tables: string[];
524
+ views: string[];
525
+ functions: string[];
526
+ procedures: string[];
527
+ types: string[];
528
+ foreignKeys: string[];
529
+ };
530
+ /** Objects preserved */
531
+ preserved: string[];
532
+ /** SQL statements executed (or would execute in dry-run) */
533
+ statements: string[];
534
+ /** Duration in milliseconds */
535
+ durationMs: number;
536
+ /** Post-script execution result (if any) */
537
+ postScriptResult?: {
538
+ executed: boolean;
539
+ error?: string;
540
+ };
541
+ /** Number of changes marked as stale (if configName provided) */
542
+ staleCount?: number;
543
+ /** ID of the reset record created (if configName provided) */
544
+ resetRecordId?: number;
545
+ }
546
+ /**
547
+ * Options for running SQL files.
548
+ *
549
+ * @example
550
+ * ```typescript
551
+ * const options: RunOptions = {
552
+ * force: true, // Re-run even if unchanged
553
+ * abortOnError: false // Continue on failures
554
+ * }
555
+ * ```
556
+ */
557
+ export interface RunOptions {
558
+ /** Re-run files even if unchanged. Default: false */
559
+ force?: boolean;
560
+ /** Number of files to run in parallel. Default: 1 (sequential for DDL safety) */
561
+ concurrency?: number;
562
+ /** Stop execution on first failure. Default: true */
563
+ abortOnError?: boolean;
564
+ /** Report what would run without executing. Default: false */
565
+ dryRun?: boolean;
566
+ /** Output rendered SQL without executing. Default: false */
567
+ preview?: boolean;
568
+ /** Write preview output to file instead of stdout. Default: null */
569
+ output?: string | null;
570
+ }
571
+ /**
572
+ * Why a file was skipped.
573
+ */
574
+ export type SkipReason = "unchanged" | "already-run";
575
+ /**
576
+ * Result of executing a single file.
577
+ *
578
+ * @example
579
+ * ```typescript
580
+ * const result: FileResult = {
581
+ * filepath: '/project/sql/001.sql',
582
+ * checksum: 'abc123...',
583
+ * status: 'success',
584
+ * durationMs: 42,
585
+ * }
586
+ * ```
587
+ */
588
+ export interface FileResult {
589
+ /** Absolute path to the file */
590
+ filepath: string;
591
+ /** SHA-256 hash of file contents */
592
+ checksum: string;
593
+ /** Execution status */
594
+ status: ExecutionStatus;
595
+ /** Why the file was skipped (only when status is 'skipped') */
596
+ skipReason?: SkipReason;
597
+ /** Execution time in milliseconds */
598
+ durationMs?: number;
599
+ /** Error message if failed */
600
+ error?: string;
601
+ /** Rendered SQL (only in preview mode) */
602
+ renderedSql?: string;
603
+ }
604
+ /**
605
+ * Overall status of a batch operation.
606
+ */
607
+ export type BatchStatus = "success" | "failed" | "partial";
608
+ /**
609
+ * Result of a batch operation (build, dir).
610
+ *
611
+ * @example
612
+ * ```typescript
613
+ * const result: BatchResult = {
614
+ * status: 'success',
615
+ * files: [...],
616
+ * filesRun: 5,
617
+ * filesSkipped: 2,
618
+ * filesFailed: 0,
619
+ * durationMs: 1234,
620
+ * }
621
+ * ```
622
+ */
623
+ export interface BatchResult {
624
+ /** Overall status */
625
+ status: BatchStatus;
626
+ /** Results for each file */
627
+ files: FileResult[];
628
+ /** Number of files executed */
629
+ filesRun: number;
630
+ /** Number of files skipped */
631
+ filesSkipped: number;
632
+ /** Number of files that failed */
633
+ filesFailed: number;
634
+ /** Total execution time in milliseconds */
635
+ durationMs: number;
636
+ /** Change ID in tracking table */
637
+ changeId?: number;
638
+ }
639
+ /**
640
+ * File extension type in changes.
641
+ *
642
+ * - 'sql' for direct SQL files (.sql, .sql.tmpl)
643
+ * - 'txt' for manifest files referencing build SQL
644
+ */
645
+ export type ChangeFileType = "sql" | "txt";
646
+ /**
647
+ * A single file within a change.
648
+ *
649
+ * @example
650
+ * ```typescript
651
+ * const file: ChangeFile = {
652
+ * filename: '001_alter-users.sql',
653
+ * path: '/project/changes/2024-01-15-add-users/change/001_alter-users.sql',
654
+ * type: 'sql',
655
+ * }
656
+ * ```
657
+ */
658
+ export interface ChangeFile {
659
+ /** Filename (e.g., "001_alter-users.sql") */
660
+ filename: string;
661
+ /** Absolute path to file */
662
+ path: string;
663
+ /** File type */
664
+ type: ChangeFileType;
665
+ /** For .txt files, the resolved SQL paths (relative to schema dir) */
666
+ resolvedPaths?: string[];
667
+ /** Execution status after running */
668
+ status?: ExecutionStatus;
669
+ /** Why the file was skipped */
670
+ skipReason?: string;
671
+ }
672
+ /**
673
+ * A change with merged disk and database information.
674
+ *
675
+ * Used by the list command to show all changes with status.
676
+ *
677
+ * @example
678
+ * ```typescript
679
+ * const item: ChangeListItem = {
680
+ * // From disk
681
+ * name: '2024-01-15-add-users',
682
+ * path: '/project/changes/...',
683
+ * // ...other Change fields
684
+ *
685
+ * // From DB
686
+ * status: 'success',
687
+ * appliedAt: new Date(),
688
+ * // ...other ChangeStatus fields
689
+ *
690
+ * // Computed
691
+ * isNew: false,
692
+ * orphaned: false,
693
+ * }
694
+ * ```
695
+ */
696
+ export interface ChangeListItem {
697
+ /** Change name (always present) */
698
+ name: string;
699
+ /** Absolute path to change folder */
700
+ path?: string;
701
+ /** Date parsed from name (null if no date prefix) */
702
+ date?: Date | null;
703
+ /** Human-readable description from name */
704
+ description?: string;
705
+ /** Files in change/ folder */
706
+ changeFiles?: ChangeFile[];
707
+ /** Files in revert/ folder */
708
+ revertFiles?: ChangeFile[];
709
+ /** Whether changelog.md exists */
710
+ hasChangelog?: boolean;
711
+ /** Current status */
712
+ status: OperationStatus;
713
+ /** When last applied (null if never) */
714
+ appliedAt: Date | null;
715
+ /** Who applied it */
716
+ appliedBy: string | null;
717
+ /** When reverted (null if not reverted) */
718
+ revertedAt: Date | null;
719
+ /** Error message if failed */
720
+ errorMessage: string | null;
721
+ /** True if exists on disk but no DB record */
722
+ isNew: boolean;
723
+ /** True if exists in DB but folder deleted from disk */
724
+ orphaned: boolean;
725
+ }
726
+ /**
727
+ * Options for executing a change.
728
+ *
729
+ * @example
730
+ * ```typescript
731
+ * const options: ChangeOptions = {
732
+ * force: false,
733
+ * dryRun: false,
734
+ * preview: false,
735
+ * }
736
+ * ```
737
+ */
738
+ export interface ChangeOptions {
739
+ /** Re-run even if already applied. Default: false */
740
+ force?: boolean;
741
+ /** Render to tmp/ without executing. Default: false */
742
+ dryRun?: boolean;
743
+ /** Output rendered SQL without executing. Default: false */
744
+ preview?: boolean;
745
+ /** Write preview output to file. Default: null */
746
+ output?: string | null;
747
+ }
748
+ /**
749
+ * Result of executing a single change.
750
+ *
751
+ * @example
752
+ * ```typescript
753
+ * const result: ChangeResult = {
754
+ * name: '2024-01-15-add-users',
755
+ * direction: 'change',
756
+ * status: 'success',
757
+ * files: [...],
758
+ * durationMs: 1234,
759
+ * }
760
+ * ```
761
+ */
762
+ export interface ChangeResult {
763
+ /** Change name */
764
+ name: string;
765
+ /** Operation direction */
766
+ direction: Direction;
767
+ /** Final status */
768
+ status: OperationStatus;
769
+ /** Individual file results */
770
+ files: ChangeFileResult[];
771
+ /** Total execution time */
772
+ durationMs: number;
773
+ /** Error message if failed */
774
+ error?: string;
775
+ /** Operation ID in tracking table */
776
+ operationId?: number;
777
+ }
778
+ /**
779
+ * Result of executing a single file within a change.
780
+ */
781
+ export interface ChangeFileResult {
782
+ /** File path */
783
+ filepath: string;
784
+ /** File checksum */
785
+ checksum: string;
786
+ /** Execution status */
787
+ status: ExecutionStatus;
788
+ /** Why skipped (if skipped) */
789
+ skipReason?: string;
790
+ /** Execution time in milliseconds */
791
+ durationMs?: number;
792
+ /** Error message if failed */
793
+ error?: string;
794
+ /** Rendered SQL (only in preview mode) */
795
+ renderedSql?: string;
796
+ }
797
+ /**
798
+ * Result of a batch operation (next, ff, rewind).
799
+ */
800
+ export interface BatchChangeResult {
801
+ /** Overall status */
802
+ status: "success" | "failed" | "partial";
803
+ /** Results for each change */
804
+ changes: ChangeResult[];
805
+ /** Number of changes executed */
806
+ executed: number;
807
+ /** Number of changes skipped */
808
+ skipped: number;
809
+ /** Number of changes that failed */
810
+ failed: number;
811
+ /** Total execution time */
812
+ durationMs: number;
813
+ }
814
+ /**
815
+ * A single execution record from history.
816
+ */
817
+ export interface ChangeHistoryRecord {
818
+ /** Record ID */
819
+ id: number;
820
+ /** Change name */
821
+ name: string;
822
+ /** Operation direction */
823
+ direction: Direction;
824
+ /** Status */
825
+ status: OperationStatus;
826
+ /** When executed */
827
+ executedAt: Date;
828
+ /** Who executed */
829
+ executedBy: string;
830
+ /** Duration in milliseconds */
831
+ durationMs: number;
832
+ /** Error message if failed */
833
+ errorMessage: string | null;
834
+ /** Checksum of files */
835
+ checksum: string;
836
+ }
837
+ /**
838
+ * Lock manager types.
839
+ *
840
+ * Defines the shape of locks and options for acquiring them.
841
+ * Used to prevent concurrent operations on the same database.
842
+ *
843
+ * WHY: Concurrent DDL operations can corrupt database state.
844
+ * Locks ensure only one process modifies the schema at a time.
845
+ */
846
+ /**
847
+ * Lock state returned after acquisition.
848
+ *
849
+ * @example
850
+ * ```typescript
851
+ * const lock = await lockManager.acquire('dev', 'alice@example.com')
852
+ * console.log(lock.lockedBy) // 'alice@example.com'
853
+ * console.log(lock.expiresAt) // Date object
854
+ * ```
855
+ */
856
+ interface Lock$1 {
857
+ /** Identity string of holder */
858
+ lockedBy: string;
859
+ /** When acquired */
860
+ lockedAt: Date;
861
+ /** Auto-expiry time */
862
+ expiresAt: Date;
863
+ /** Optional reason for acquiring */
864
+ reason?: string;
865
+ }
866
+ /**
867
+ * Options for acquiring a lock.
868
+ *
869
+ * @example
870
+ * ```typescript
871
+ * // Wait up to 30s for lock, polling every 500ms
872
+ * const options: LockOptions = {
873
+ * timeout: 60_000, // Lock expires after 60s
874
+ * wait: true, // Block until available
875
+ * waitTimeout: 30_000, // Max wait time
876
+ * pollInterval: 500, // Check every 500ms
877
+ * reason: 'Running migrations',
878
+ * }
879
+ * ```
880
+ */
881
+ interface LockOptions$1 {
882
+ /**
883
+ * Database dialect for date formatting.
884
+ *
885
+ * SQLite stores dates as ISO strings, other dialects can use Date objects.
886
+ * @default 'postgres'
887
+ */
888
+ dialect?: "postgres" | "mysql" | "sqlite" | "mssql";
889
+ /**
890
+ * Lock duration in milliseconds.
891
+ *
892
+ * After this time, the lock expires and can be claimed by others.
893
+ * @default 300_000 (5 minutes)
894
+ */
895
+ timeout?: number;
896
+ /**
897
+ * Block until lock is available?
898
+ *
899
+ * If true, will poll until the lock is acquired or waitTimeout is reached.
900
+ * @default false
901
+ */
902
+ wait?: boolean;
903
+ /**
904
+ * Max time to wait for lock in milliseconds.
905
+ *
906
+ * Only used if wait is true.
907
+ * @default 30_000 (30 seconds)
908
+ */
909
+ waitTimeout?: number;
910
+ /**
911
+ * How often to poll when waiting, in milliseconds.
912
+ *
913
+ * Only used if wait is true.
914
+ * @default 1_000 (1 second)
915
+ */
916
+ pollInterval?: number;
917
+ /**
918
+ * Optional reason for acquiring the lock.
919
+ *
920
+ * Shown to users who are blocked by this lock.
921
+ */
922
+ reason?: string;
923
+ }
924
+ /**
925
+ * Result of a lock status check.
926
+ */
927
+ export interface LockStatus {
928
+ /** Whether the lock is currently held */
929
+ isLocked: boolean;
930
+ /** Lock details if held, null if not */
931
+ lock: Lock$1 | null;
932
+ }
933
+ /**
934
+ * Lock-related errors.
935
+ *
936
+ * WHY: Specific error types allow callers to handle lock failures
937
+ * differently from other errors (e.g., prompt user to wait vs retry).
938
+ */
939
+ /**
940
+ * Error when lock cannot be acquired.
941
+ *
942
+ * Thrown when another process holds the lock and wait=false,
943
+ * or when waitTimeout is exceeded.
944
+ *
945
+ * @example
946
+ * ```typescript
947
+ * const [lock, err] = await attempt(() => lockManager.acquire(config, identity))
948
+ * if (err instanceof LockAcquireError) {
949
+ * console.log(`Blocked by ${err.holder} since ${err.heldSince}`)
950
+ * }
951
+ * ```
952
+ */
953
+ export declare class LockAcquireError extends Error {
954
+ readonly configName: string;
955
+ readonly holder: string;
956
+ readonly heldSince: Date;
957
+ readonly expiresAt: Date;
958
+ readonly reason?: string | undefined;
959
+ readonly name: "LockAcquireError";
960
+ constructor(configName: string, holder: string, heldSince: Date, expiresAt: Date, reason?: string | undefined);
961
+ }
962
+ /**
963
+ * Error when lock expires during operation.
964
+ *
965
+ * Thrown when validating lock before a critical operation
966
+ * and discovering it has expired.
967
+ *
968
+ * @example
969
+ * ```typescript
970
+ * // Before committing transaction
971
+ * const [, err] = await attempt(() => lockManager.validate(configName, identity))
972
+ * if (err instanceof LockExpiredError) {
973
+ * await transaction.rollback()
974
+ * throw new Error('Lock expired, operation aborted')
975
+ * }
976
+ * ```
977
+ */
978
+ export declare class LockExpiredError extends Error {
979
+ readonly configName: string;
980
+ readonly identity: string;
981
+ readonly expiredAt: Date;
982
+ readonly name: "LockExpiredError";
983
+ constructor(configName: string, identity: string, expiredAt: Date);
984
+ }
985
+ /**
986
+ * Result of processing a SQL file.
987
+ */
988
+ interface ProcessResult {
989
+ /**
990
+ * The SQL content (rendered if template, raw if .sql).
991
+ */
992
+ sql: string;
993
+ /**
994
+ * Whether the file was a template.
995
+ */
996
+ isTemplate: boolean;
997
+ /**
998
+ * Render duration in milliseconds (only for templates).
999
+ */
1000
+ durationMs?: number;
1001
+ }
1002
+ /**
1003
+ * SDK Types.
1004
+ *
1005
+ * All interfaces and types for the noorm programmatic SDK.
1006
+ */
1007
+ /**
1008
+ * Options for creating an SDK context.
1009
+ *
1010
+ * @example
1011
+ * ```typescript
1012
+ * // Basic usage with stored config
1013
+ * const ctx = await createContext({ config: 'dev' })
1014
+ *
1015
+ * // Require test database for safety
1016
+ * const ctx = await createContext({
1017
+ * config: 'test',
1018
+ * requireTest: true,
1019
+ * })
1020
+ *
1021
+ * // Allow destructive ops on protected config
1022
+ * const ctx = await createContext({
1023
+ * config: 'staging',
1024
+ * allowProtected: true,
1025
+ * })
1026
+ *
1027
+ * // Env-only mode (CI/CD) - no stored config needed
1028
+ * // Requires NOORM_CONNECTION_DIALECT and NOORM_CONNECTION_DATABASE
1029
+ * const ctx = await createContext()
1030
+ *
1031
+ * // Override stored config via NOORM_* env vars
1032
+ * // NOORM_CONNECTION_HOST=override.host
1033
+ * const ctx = await createContext({ config: 'prod' })
1034
+ * ```
1035
+ */
1036
+ export interface CreateContextOptions {
1037
+ /**
1038
+ * Config name from state.
1039
+ *
1040
+ * If omitted:
1041
+ * - Uses `NOORM_CONFIG` env var if set
1042
+ * - Falls back to env-only mode if `NOORM_CONNECTION_DIALECT`
1043
+ * and `NOORM_CONNECTION_DATABASE` are set
1044
+ */
1045
+ config?: string;
1046
+ /** Project root directory. Defaults to process.cwd() */
1047
+ projectRoot?: string;
1048
+ /** Refuse if config.isTest !== true. Default: false */
1049
+ requireTest?: boolean;
1050
+ /** Allow destructive ops on protected configs. Default: false */
1051
+ allowProtected?: boolean;
1052
+ /** Stage name for stage defaults (from settings.yml) */
1053
+ stage?: string;
1054
+ }
1055
+ /**
1056
+ * Result of an execute() call.
1057
+ */
1058
+ export interface ExecuteResult {
1059
+ /** Number of rows affected (if available) */
1060
+ rowsAffected?: number;
1061
+ }
1062
+ /**
1063
+ * Transaction context for use within transaction callbacks.
1064
+ *
1065
+ * @example
1066
+ * ```typescript
1067
+ * await ctx.transaction(async (tx) => {
1068
+ * const [user] = await tx.query('SELECT * FROM users WHERE id = $1', [id])
1069
+ * await tx.execute('UPDATE users SET login_count = login_count + 1 WHERE id = $1', [id])
1070
+ * return user
1071
+ * })
1072
+ * ```
1073
+ */
1074
+ export interface TransactionContext {
1075
+ /**
1076
+ * Execute a SELECT query within the transaction.
1077
+ */
1078
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
1079
+ /**
1080
+ * Execute an INSERT/UPDATE/DELETE within the transaction.
1081
+ */
1082
+ execute(sql: string, params?: unknown[]): Promise<ExecuteResult>;
1083
+ }
1084
+ /**
1085
+ * Options for build operations.
1086
+ */
1087
+ export interface BuildOptions {
1088
+ /** Skip checksum checks, rebuild everything. Default: false */
1089
+ force?: boolean;
1090
+ }
1091
+ export type Events<Shape> = keyof Shape;
1092
+ declare namespace ObserverEngine {
1093
+ interface EventCallback<Shape> {
1094
+ (data: Shape, info?: {
1095
+ event: string;
1096
+ listener: Function;
1097
+ } | undefined): void;
1098
+ }
1099
+ type RgxEmitData<Shape> = {
1100
+ event: Events<Shape>;
1101
+ data: Shape[Events<Shape>];
1102
+ };
1103
+ type Cleanup = () => void;
1104
+ type Component<Ev extends Record<string, any>> = {
1105
+ on: ObserverEngine<Ev>["on"];
1106
+ once: ObserverEngine<Ev>["once"];
1107
+ emit: ObserverEngine<Ev>["emit"];
1108
+ off: ObserverEngine<Ev>["off"];
1109
+ };
1110
+ type Child<C, Ev extends Record<string, any>> = C & Component<Ev> & {
1111
+ cleanup: Cleanup;
1112
+ clear: Cleanup;
1113
+ };
1114
+ type Instance<Ev extends Record<string, any>> = Component<Ev> & {
1115
+ observe: ObserverEngine<Ev>["observe"];
1116
+ $observer: ObserverEngine<Ev>;
1117
+ };
1118
+ type FuncName = "on" | "once" | "off" | "emit" | "cleanup";
1119
+ type SpyAction<Ev extends Record<string, any>> = {
1120
+ event: keyof Ev | RegExp | "*";
1121
+ listener?: Function | null | undefined;
1122
+ data?: unknown | undefined;
1123
+ fn: FuncName;
1124
+ context: ObserverEngine<Ev>;
1125
+ };
1126
+ interface Spy<Ev extends Record<string, any>> {
1127
+ (action: SpyAction<Ev>): void;
1128
+ }
1129
+ interface EmitValidator<Ev extends Record<string, any>> {
1130
+ (event: keyof Ev, data: Ev[keyof Ev], context: ObserverEngine<Ev>): void;
1131
+ (event: RegExp, data: unknown, context: ObserverEngine<Ev>): void;
1132
+ (event: "*", data: unknown, context: ObserverEngine<Ev>): void;
1133
+ }
1134
+ type Options<Ev extends Record<string, any>> = {
1135
+ name?: string | undefined;
1136
+ spy?: Spy<Ev> | undefined;
1137
+ emitValidator?: EmitValidator<Ev> | undefined;
1138
+ signal?: AbortSignal | undefined;
1139
+ };
1140
+ type ListenerOptions = {
1141
+ signal?: AbortSignal | undefined;
1142
+ };
1143
+ type ObserveOptions = {
1144
+ signal?: AbortSignal | undefined;
1145
+ };
1146
+ }
1147
+ /**
1148
+ * Generic function type for type-safe function signatures.
1149
+ *
1150
+ * Enables consistent typing across utility functions and higher-order functions
1151
+ * where function parameters need to be strongly typed.
1152
+ *
1153
+ * @example
1154
+ * function memoize<A extends any[], R>(fn: Func<A, R>): Func<A, R>
1155
+ * function debounce<A extends any[]>(fn: Func<A, void>, ms: number): Func<A, void>
1156
+ */
1157
+ export type Func<A extends any[] = any[], R = any> = (...args: A) => R;
1158
+ /**
1159
+ * Represents a value that can be either synchronous or asynchronous.
1160
+ *
1161
+ * Critical for utility functions that need to handle both sync and async
1162
+ * operations uniformly, enabling flexible API design.
1163
+ *
1164
+ * @example
1165
+ * function processData<T>(processor: () => MaybePromise<T>): Promise<T>
1166
+ * function transform<T, U>(value: T, transformer: (val: T) => MaybePromise<U>): Promise<U>
1167
+ */
1168
+ export type MaybePromise<T> = T | Promise<T>;
1169
+ export interface EventGeneratorOptions {
1170
+ signal?: AbortSignal | undefined;
1171
+ }
1172
+ declare class EventGenerator<S extends Record<string, any>, E extends Events<S> | RegExp = Events<S>> {
1173
+ #private;
1174
+ cleanup: ObserverEngine.Cleanup;
1175
+ next: () => Promise<EventData<S, E>>;
1176
+ constructor(observer: ObserverEngine<S>, event: E | RegExp, options?: EventGeneratorOptions);
1177
+ [Symbol.asyncIterator](): AsyncGenerator<Awaited<EventData<S, E>>, undefined, unknown>;
1178
+ get lastValue(): E extends Events<S> ? S[E] : E extends RegExp ? ObserverEngine.RgxEmitData<S> : S[Events<S>];
1179
+ get done(): boolean;
1180
+ emit(data?: (E extends Events<S> ? S[E] : S[Events<S>])): void;
1181
+ }
1182
+ declare class ObserverEngine<Shape extends Record<string, any> = Record<string, any>> {
1183
+ #private;
1184
+ constructor(options?: ObserverEngine.Options<Shape>);
1185
+ name: string;
1186
+ /**
1187
+ * Returns facts about the the internal state of the observable instance.
1188
+ */
1189
+ $facts(): {
1190
+ listeners: (keyof Shape)[];
1191
+ rgxListeners: string[];
1192
+ listenerCounts: Record<string, number>;
1193
+ hasSpy: boolean;
1194
+ };
1195
+ /**
1196
+ * The internals of the observable instance.
1197
+ *
1198
+ * NOTE: Do not use this to try to meddle with the
1199
+ * internals of the observable instance. This is for
1200
+ * debugging purposes only.
1201
+ */
1202
+ $internals(): {
1203
+ listenerMap: Map<keyof Shape, Set<Func>>;
1204
+ rgxListenerMap: Map<string, Set<Func>>;
1205
+ internalListener: EventTarget;
1206
+ name: string;
1207
+ spy: ObserverEngine.Spy<Shape> | undefined;
1208
+ };
1209
+ /**
1210
+ * Returns if the observable instance has the given event
1211
+ */
1212
+ $has(event: Events<Shape>): boolean;
1213
+ /**
1214
+ * Returns if the observable instance has a regex event
1215
+ */
1216
+ $has(event: RegExp): boolean;
1217
+ /**
1218
+ * Returns if the observable instance has the given event
1219
+ */
1220
+ $has(event: string): boolean;
1221
+ /**
1222
+ * Enables or disables debugging for the observable instance.
1223
+ * Works in conjunction with your spy function. Provides a
1224
+ * stack trace of events that are triggered, listened to, and
1225
+ * cleaned up.
1226
+ *
1227
+ * @param on Whether to enable or disable debugging
1228
+ */
1229
+ debug(on?: boolean): void;
1230
+ /**
1231
+ * Sets the spy function for the observable instance.
1232
+ * @param spy The spy function to set
1233
+ * @param force Whether to force the spy function to be set even if one is already set
1234
+ */
1235
+ spy(spy: ObserverEngine.Spy<Shape>, force?: boolean): void;
1236
+ /**
1237
+ * Observes given component as an extension of this observable instance.
1238
+ * @param component Component to wrap events around
1239
+ * @param options Optional configuration including signal for cleanup
1240
+ *
1241
+ * @example
1242
+ *
1243
+ * const obs = new ObserverEngine();
1244
+ *
1245
+ * const modal = {};
1246
+ *
1247
+ * obs.observe(modal);
1248
+ *
1249
+ * modal.on('modal-open', () => {});
1250
+ *
1251
+ * obs.trigger('modal-open'); // opens modal
1252
+ * modal.trigger('modal-open'); // opens modal
1253
+ *
1254
+ * modal.cleanup(); // clears all event listeners
1255
+ */
1256
+ observe<C>(component: C, options?: ObserverEngine.ObserveOptions): ObserverEngine.Child<C, Shape>;
1257
+ /**
1258
+ * Returns an event generator that will listen for the specified event
1259
+ *
1260
+ * @example
1261
+ *
1262
+ * const obs = new ObserverEngine();
1263
+ *
1264
+ * const something = obs.on('something');
1265
+ * const data = await something.next(); // waits for next event
1266
+ * something.emit('special'); // emits data to listeners
1267
+ *
1268
+ * something.cleanup(); // stops listening for events
1269
+ */
1270
+ on<E extends Events<Shape>>(event: E, options?: ObserverEngine.ListenerOptions): EventGenerator<Shape, E>;
1271
+ on<E extends string>(event: E, options?: ObserverEngine.ListenerOptions): EventGenerator<Record<E, any>>;
1272
+ /**
1273
+ * Listens for the specified event and executes the given callback
1274
+ *
1275
+ * @example
1276
+ *
1277
+ * const obs = new ObserverEngine();
1278
+ *
1279
+ * obs.on('something', (data) => {
1280
+ * console.log(data);
1281
+ * });
1282
+ */
1283
+ on<E extends Events<Shape>>(event: E, listener: ObserverEngine.EventCallback<Shape[E]>, options?: ObserverEngine.ListenerOptions): ObserverEngine.Cleanup;
1284
+ on<E extends string>(event: E, listener: ObserverEngine.EventCallback<Record<E, any>>, options?: ObserverEngine.ListenerOptions): ObserverEngine.Cleanup;
1285
+ /**
1286
+ * Returns an event generator that will listen for all events matching the regex
1287
+ *
1288
+ * @example
1289
+ *
1290
+ * const obs = new ObserverEngine();
1291
+ *
1292
+ * const onEvent = obs.on(/some/);
1293
+ * const { event, data } = await onEvent.next(); // waits for next event
1294
+ * onEvent.emit('something'); // emits data to listeners
1295
+ *
1296
+ * onEvent.cleanup(); // stops listening for events
1297
+ */
1298
+ on(event: RegExp, options?: ObserverEngine.ListenerOptions): EventGenerator<Shape, RegExp>;
1299
+ /**
1300
+ * Listens for all events matching the regex and executes the given callback
1301
+ *
1302
+ * @example
1303
+ *
1304
+ * const obs = new ObserverEngine();
1305
+ *
1306
+ * obs.on(/some/, ({ event, data }) => {
1307
+ * console.log(event, data);
1308
+ * });
1309
+ */
1310
+ on(event: RegExp, listener: ObserverEngine.EventCallback<ObserverEngine.RgxEmitData<Shape>>, options?: ObserverEngine.ListenerOptions): ObserverEngine.Cleanup;
1311
+ /**
1312
+ * Returns an event promise that resolves when
1313
+ * the specified event is emitted
1314
+ */
1315
+ once<E extends Events<Shape>>(event: E, options?: ObserverEngine.ListenerOptions): EventPromise<Shape[E]>;
1316
+ /**
1317
+ * Returns an event promise that resolves when
1318
+ * the specified event is emitted. This overload
1319
+ * is untyped and can be used to listen for any
1320
+ * event that is emitted.
1321
+ */
1322
+ once<E extends string>(event: E, options?: ObserverEngine.ListenerOptions): EventPromise<Record<E, any>>;
1323
+ /**
1324
+ * Executes a callback once when the specified
1325
+ * event is emitted
1326
+ */
1327
+ once<E extends Events<Shape>>(event: E, listener: ObserverEngine.EventCallback<Shape[E]>, options?: ObserverEngine.ListenerOptions): ObserverEngine.Cleanup;
1328
+ /**
1329
+ * Executes a callback once when the specified
1330
+ * event is emitted. This overload is untyped
1331
+ * and can be used to listen for any event that
1332
+ * is emitted.
1333
+ */
1334
+ once<E extends string>(event: E, listener: ObserverEngine.EventCallback<Record<E, any>>, options?: ObserverEngine.ListenerOptions): ObserverEngine.Cleanup;
1335
+ /**
1336
+ * Returns an event promise that resolves when
1337
+ * any events matching the regex are emitted
1338
+ */
1339
+ once(event: RegExp, options?: ObserverEngine.ListenerOptions): EventPromise<ObserverEngine.RgxEmitData<Shape>>;
1340
+ /**
1341
+ * Executes a callback once when any events
1342
+ * matching the regex are emitted
1343
+ */
1344
+ once(event: RegExp, listener: ObserverEngine.EventCallback<ObserverEngine.RgxEmitData<Shape>>, options?: ObserverEngine.ListenerOptions): ObserverEngine.Cleanup;
1345
+ /**
1346
+ * Stop listening for an event
1347
+ * @param event
1348
+ * @param listener
1349
+ */
1350
+ off(event: Events<Shape> | RegExp | string, listener?: Function): void;
1351
+ /** Emits an event */
1352
+ emit<E extends Events<Shape> | RegExp | string>(event: E, data?: E extends Events<Shape> ? Shape[E] : E extends string ? Record<E, any>[E] : unknown): void;
1353
+ clear(): void;
1354
+ queue<E extends Events<Shape> | RegExp>(event: E, process: (data: EventData<Shape, E>) => MaybePromise<any>, options: QueueOpts): EventQueue<Shape, E>;
1355
+ }
1356
+ export type EventData<S, E extends Events<S> | RegExp = Events<S>> = (E extends Events<S> ? S[E] : E extends RegExp ? ObserverEngine.RgxEmitData<S> : S[Events<S>]);
1357
+ declare class EventPromise<T> extends Promise<T> {
1358
+ cleanup?: () => void;
1359
+ reject?: (err: Error | string) => void;
1360
+ resolve?: (value: T) => void;
1361
+ }
1362
+ declare class InternalQueueEvent<T = unknown> {
1363
+ readonly data: T;
1364
+ constructor(data: T);
1365
+ }
1366
+ declare enum QueueRejectionReason {
1367
+ full = "Queue is full",
1368
+ notRunning = "Queue is not running"
1369
+ }
1370
+ export type QueueEventData<S, E extends Events<S> | RegExp = Events<S>> = {
1371
+ data?: EventData<S, E>;
1372
+ _taskId?: string;
1373
+ priority?: number;
1374
+ reason?: QueueRejectionReason;
1375
+ startedAt?: number;
1376
+ rateLimited?: boolean;
1377
+ elapsed?: number;
1378
+ error?: Error;
1379
+ force?: boolean;
1380
+ pending?: number;
1381
+ flushed?: number;
1382
+ drained?: number;
1383
+ count?: number;
1384
+ };
1385
+ export type QueueEvents<S extends Record<string, any>, E extends Events<S> | RegExp = Events<S>> = {
1386
+ added: QueueEventData<S, E>;
1387
+ start: void;
1388
+ started: void;
1389
+ stopped: void;
1390
+ processing: QueueEventData<S, E> & {
1391
+ startedAt: number;
1392
+ rateLimited: boolean;
1393
+ };
1394
+ success: QueueEventData<S, E> & {
1395
+ startedAt: number;
1396
+ elapsed: number;
1397
+ rateLimited: boolean;
1398
+ };
1399
+ error: QueueEventData<S, E> & {
1400
+ error: Error;
1401
+ rateLimited: boolean;
1402
+ };
1403
+ timeout: QueueEventData<S, E> & {
1404
+ error: Error;
1405
+ rateLimited: boolean;
1406
+ };
1407
+ rejected: QueueEventData<S, E> & {
1408
+ reason: QueueRejectionReason;
1409
+ };
1410
+ "rate-limited": QueueEventData<S, E> & {
1411
+ rateLimited: boolean;
1412
+ };
1413
+ empty: void;
1414
+ idle: void;
1415
+ drain: {
1416
+ pending: number;
1417
+ };
1418
+ drained: {
1419
+ pending?: number;
1420
+ drained?: number;
1421
+ };
1422
+ flush: {
1423
+ pending: number;
1424
+ };
1425
+ flushed: {
1426
+ flushed: number;
1427
+ };
1428
+ paused: void;
1429
+ resumed: void;
1430
+ cleanup: void;
1431
+ purged: {
1432
+ count: number;
1433
+ };
1434
+ shutdown: {
1435
+ force: boolean;
1436
+ pending?: number;
1437
+ };
1438
+ };
1439
+ declare enum QueueState {
1440
+ running = "running",
1441
+ paused = "paused",
1442
+ stopped = "stopped",
1443
+ draining = "draining"
1444
+ }
1445
+ /**
1446
+ * The options for the queue
1447
+ */
1448
+ export interface QueueOpts {
1449
+ /**
1450
+ * The name of the queue
1451
+ */
1452
+ name: string;
1453
+ /**
1454
+ * The type of queue to use
1455
+ *
1456
+ * @default 'fifo'
1457
+ */
1458
+ type?: "fifo" | "lifo";
1459
+ /**
1460
+ * The concurrency of the queue
1461
+ *
1462
+ * @default 1
1463
+ */
1464
+ concurrency?: number;
1465
+ /**
1466
+ * The poll interval in milliseconds before the queue will
1467
+ * check for new items after idle.
1468
+ *
1469
+ * @default 100
1470
+ */
1471
+ pollIntervalMs?: number;
1472
+ /**
1473
+ * The jitter percentage to displace
1474
+ * the next process time. This stops all concurrent
1475
+ * processes from happening at the same time.
1476
+ *
1477
+ * @range [0, 1]
1478
+ * @default 1
1479
+ */
1480
+ jitterFactor?: number;
1481
+ /**
1482
+ * The interval in milliseconds before picking up the next item
1483
+ *
1484
+ * @note If the interval is 0, the queue will not wait between items
1485
+ *
1486
+ * @default 0
1487
+ */
1488
+ processIntervalMs?: number;
1489
+ /**
1490
+ * The timeout in milliseconds before the task is considered timed out
1491
+ *
1492
+ * @note If the timeout is 0, the task will not be considered timed out
1493
+ *
1494
+ * @default 0
1495
+ */
1496
+ taskTimeoutMs?: number;
1497
+ /**
1498
+ * The maximum size of the queue
1499
+ *
1500
+ * @default 999_999_999
1501
+ */
1502
+ maxQueueSize?: number;
1503
+ /**
1504
+ * The rate limit of the queue in items per window
1505
+ *
1506
+ * @default 999_999_999
1507
+ */
1508
+ rateLimitCapacity?: number;
1509
+ /**
1510
+ * The rate limit window in milliseconds
1511
+ *
1512
+ * @default 1000
1513
+ */
1514
+ rateLimitIntervalMs?: number;
1515
+ /**
1516
+ * Automatically start the queue
1517
+ *
1518
+ * @default true
1519
+ */
1520
+ autoStart?: boolean;
1521
+ /**
1522
+ * Whether to enable debug mode. Can be set to 'info' or 'verbose' to
1523
+ * get more detailed output.
1524
+ *
1525
+ * @default false
1526
+ */
1527
+ debug?: boolean | "info" | "verbose";
1528
+ }
1529
+ declare class EventQueue<S extends Record<string, any>, E extends Events<S> | RegExp = Events<S>> {
1530
+ #private;
1531
+ private opts;
1532
+ constructor(opts: QueueOpts & {
1533
+ event: E | RegExp;
1534
+ process: (data: EventData<S, E>) => Promise<void>;
1535
+ observer?: ObserverEngine<S>;
1536
+ });
1537
+ /**
1538
+ * Start the queue if it is not already started
1539
+ */
1540
+ start(): Promise<void> | undefined;
1541
+ /**
1542
+ * Stop the queue
1543
+ */
1544
+ stop(): void;
1545
+ /**
1546
+ * Pause the queue
1547
+ */
1548
+ pause(): Promise<void> | undefined;
1549
+ /**
1550
+ * Resume the queue
1551
+ */
1552
+ resume(): Promise<void> | undefined;
1553
+ /**
1554
+ * Empty and process all items in the queue and
1555
+ * then stop the queue. Returns the number of items
1556
+ * processed.
1557
+ */
1558
+ shutdown(force?: boolean): Promise<number>;
1559
+ /**
1560
+ * Executes `limit` items in the queue
1561
+ * and then emits a `flush` event. Returns the number
1562
+ * of items processed.
1563
+ */
1564
+ flush(limit?: number): Promise<number>;
1565
+ /**
1566
+ * Clears the queue and emits a `purge` event. Returns
1567
+ * the number of items purged.
1568
+ */
1569
+ purge(): number;
1570
+ /**
1571
+ * Emit an event to the observer, which then gets
1572
+ * picked up by the queue and processed.
1573
+ *
1574
+ * @param data The data to add to the queue
1575
+ * @param priority The priority of the data
1576
+ *
1577
+ * @note If priority is provided, it will be used to determine the order of the data in the queue.
1578
+ * The higher the priority, the sooner the data will be processed.
1579
+ *
1580
+ * @note If priority is not provided, the data will be added to the queue with a priority of 0.
1581
+ */
1582
+ add(data: EventData<S, E>, priority?: number): boolean;
1583
+ debug(on?: boolean | "info" | "verbose"): void;
1584
+ on<K extends keyof QueueEvents<S, E>>(event: K, listener: ((payload: InternalQueueEvent<QueueEvents<S, E>[K]>) => void)): ObserverEngine.Cleanup;
1585
+ on<K extends keyof QueueEvents<S, E>>(event: K): EventGenerator<QueueEvents<S, E>, K>;
1586
+ once<K extends keyof QueueEvents<S, E>>(event: K): EventPromise<InternalQueueEvent<QueueEvents<S, E>[K]>>;
1587
+ once<K extends keyof QueueEvents<S, E>>(event: K, listener: ((payload: InternalQueueEvent<QueueEvents<S, E>[K]>) => void)): ObserverEngine.Cleanup;
1588
+ off<K extends keyof QueueEvents<S, E>>(event: K, listener?: Function): void;
1589
+ /**
1590
+ * The name of the queue
1591
+ */
1592
+ get name(): string;
1593
+ /**
1594
+ * Whether the queue is waiting for the
1595
+ * the
1596
+ */
1597
+ get isWaiting(): boolean;
1598
+ /**
1599
+ * Whether the queue is idle
1600
+ */
1601
+ get isIdle(): boolean;
1602
+ /**
1603
+ * Whether the queue is running
1604
+ */
1605
+ get isRunning(): boolean;
1606
+ /**
1607
+ * Whether the queue is paused
1608
+ */
1609
+ get isPaused(): boolean;
1610
+ /**
1611
+ * Whether the queue is stopped
1612
+ */
1613
+ get isStopped(): boolean;
1614
+ /**
1615
+ * Whether the queue is draining
1616
+ */
1617
+ get isDraining(): boolean;
1618
+ /**
1619
+ * The current state of the queue
1620
+ */
1621
+ get state(): QueueState;
1622
+ /**
1623
+ * The number of items in the queue
1624
+ */
1625
+ get pending(): number;
1626
+ /**
1627
+ * The stats of the queue
1628
+ */
1629
+ get stats(): {
1630
+ processed: number;
1631
+ processing: number;
1632
+ avgProcessingTime: number;
1633
+ success: number;
1634
+ error: number;
1635
+ rejected: number;
1636
+ };
1637
+ get snapshot(): {
1638
+ name: string;
1639
+ state: QueueState;
1640
+ pending: number;
1641
+ stats: {
1642
+ processed: number;
1643
+ processing: number;
1644
+ avgProcessingTime: number;
1645
+ success: number;
1646
+ error: number;
1647
+ rejected: number;
1648
+ };
1649
+ rateLimiter: {
1650
+ currentTokens: number;
1651
+ capacity: number;
1652
+ refillIntervalMs: number;
1653
+ totalRequests: number;
1654
+ rejectedRequests: number;
1655
+ successfulRequests: number;
1656
+ rejectionRate: number;
1657
+ totalWaitTime: number;
1658
+ waitCount: number;
1659
+ averageWaitTime: number;
1660
+ uptime: number;
1661
+ createdAt: number;
1662
+ };
1663
+ activeRunners: number;
1664
+ runningNodes: Set<`${number}-${number}`>;
1665
+ isIdle: boolean;
1666
+ isWaiting: boolean;
1667
+ isRunning: boolean;
1668
+ isPaused: boolean;
1669
+ isStopped: boolean;
1670
+ isDraining: boolean;
1671
+ isEmpty: boolean;
1672
+ };
1673
+ }
1674
+ /**
1675
+ * Shutdown phases in order of execution.
1676
+ *
1677
+ * Each phase has a specific purpose in the graceful shutdown sequence.
1678
+ */
1679
+ export type ShutdownPhase = "stopping" | "completing" | "releasing" | "flushing" | "exiting";
1680
+ /**
1681
+ * Shutdown phase status.
1682
+ */
1683
+ export type PhaseStatus = "pending" | "running" | "completed" | "timeout" | "skipped";
1684
+ /**
1685
+ * Reasons for shutdown.
1686
+ */
1687
+ export type ShutdownReason = "signal" | "user" | "error" | "programmatic";
1688
+ /**
1689
+ * Application mode.
1690
+ */
1691
+ export type AppMode = "tui" | "headless";
1692
+ /**
1693
+ * All events emitted by noorm core modules.
1694
+ *
1695
+ * Events are namespaced by module:
1696
+ * - `file:*` - Individual SQL file execution
1697
+ * - `build:*` - Schema build operations
1698
+ * - `run:*` - Ad-hoc file/dir execution
1699
+ * - `change:*` - Change execution
1700
+ * - `lock:*` - Lock acquisition/release
1701
+ * - `state:*` - State load/persist
1702
+ * - `config:*` - Config CRUD
1703
+ * - `secret:*` - Secret CRUD
1704
+ * - `db:*` - Database lifecycle
1705
+ * - `template:*` - Template rendering
1706
+ * - `identity:*` - Identity resolution
1707
+ * - `connection:*` - Database connections
1708
+ * - `settings:*` - Settings lifecycle and mutations
1709
+ * - `error` - Catch-all errors
1710
+ */
1711
+ export interface NoormEvents extends SettingsEvents {
1712
+ "file:before": {
1713
+ filepath: string;
1714
+ checksum: string;
1715
+ configName: string;
1716
+ };
1717
+ "file:after": {
1718
+ filepath: string;
1719
+ status: "success" | "failed";
1720
+ durationMs: number;
1721
+ error?: string;
1722
+ };
1723
+ "file:skip": {
1724
+ filepath: string;
1725
+ reason: "unchanged" | "already-run";
1726
+ };
1727
+ "file:dry-run": {
1728
+ filepath: string;
1729
+ outputPath: string;
1730
+ };
1731
+ "change:created": {
1732
+ name: string;
1733
+ path: string;
1734
+ };
1735
+ "change:start": {
1736
+ name: string;
1737
+ direction: "change" | "revert";
1738
+ files: string[];
1739
+ };
1740
+ "change:file": {
1741
+ change: string;
1742
+ filepath: string;
1743
+ index: number;
1744
+ total: number;
1745
+ };
1746
+ "change:complete": {
1747
+ name: string;
1748
+ direction: "change" | "revert";
1749
+ status: "success" | "failed";
1750
+ durationMs: number;
1751
+ };
1752
+ "change:skip": {
1753
+ name: string;
1754
+ reason: string;
1755
+ };
1756
+ "build:start": {
1757
+ sqlPath: string;
1758
+ fileCount: number;
1759
+ };
1760
+ "build:complete": {
1761
+ status: "success" | "failed" | "partial";
1762
+ filesRun: number;
1763
+ filesSkipped: number;
1764
+ filesFailed: number;
1765
+ durationMs: number;
1766
+ };
1767
+ "run:file": {
1768
+ filepath: string;
1769
+ configName: string;
1770
+ };
1771
+ "run:dir": {
1772
+ dirpath: string;
1773
+ fileCount: number;
1774
+ configName: string;
1775
+ };
1776
+ "lock:acquiring": {
1777
+ configName: string;
1778
+ identity: string;
1779
+ };
1780
+ "lock:acquired": {
1781
+ configName: string;
1782
+ identity: string;
1783
+ expiresAt: Date;
1784
+ };
1785
+ "lock:released": {
1786
+ configName: string;
1787
+ identity: string;
1788
+ };
1789
+ "lock:blocked": {
1790
+ configName: string;
1791
+ holder: string;
1792
+ heldSince: Date;
1793
+ };
1794
+ "lock:expired": {
1795
+ configName: string;
1796
+ previousHolder: string;
1797
+ };
1798
+ "state:loaded": {
1799
+ configCount: number;
1800
+ activeConfig: string | null;
1801
+ version: string;
1802
+ };
1803
+ "state:persisted": {
1804
+ configCount: number;
1805
+ };
1806
+ "state:migrated": {
1807
+ from: string;
1808
+ to: string;
1809
+ };
1810
+ "config:created": {
1811
+ name: string;
1812
+ };
1813
+ "config:updated": {
1814
+ name: string;
1815
+ fields: string[];
1816
+ };
1817
+ "config:deleted": {
1818
+ name: string;
1819
+ };
1820
+ "config:activated": {
1821
+ name: string;
1822
+ previous: string | null;
1823
+ };
1824
+ "secret:set": {
1825
+ configName: string;
1826
+ key: string;
1827
+ };
1828
+ "secret:deleted": {
1829
+ configName: string;
1830
+ key: string;
1831
+ };
1832
+ "global-secret:set": {
1833
+ key: string;
1834
+ };
1835
+ "global-secret:deleted": {
1836
+ key: string;
1837
+ };
1838
+ "known-user:added": {
1839
+ email: string;
1840
+ source: string;
1841
+ };
1842
+ "db:creating": {
1843
+ configName: string;
1844
+ database: string;
1845
+ };
1846
+ "db:created": {
1847
+ configName: string;
1848
+ database: string;
1849
+ durationMs: number;
1850
+ };
1851
+ "db:destroying": {
1852
+ configName: string;
1853
+ database: string;
1854
+ };
1855
+ "db:destroyed": {
1856
+ configName: string;
1857
+ database: string;
1858
+ };
1859
+ "db:bootstrap": {
1860
+ configName: string;
1861
+ tables: string[];
1862
+ };
1863
+ "template:render": {
1864
+ filepath: string;
1865
+ durationMs: number;
1866
+ };
1867
+ "template:load": {
1868
+ filepath: string;
1869
+ format: string;
1870
+ };
1871
+ "template:helpers": {
1872
+ filepath: string;
1873
+ count: number;
1874
+ };
1875
+ "identity:resolved": {
1876
+ name: string;
1877
+ email?: string;
1878
+ source: "state" | "git" | "system" | "config" | "env";
1879
+ };
1880
+ "identity:created": {
1881
+ identityHash: string;
1882
+ name: string;
1883
+ email: string;
1884
+ machine: string;
1885
+ };
1886
+ "identity:registered": {
1887
+ identityHash: string;
1888
+ name: string;
1889
+ email: string;
1890
+ };
1891
+ "identity:synced": {
1892
+ configName: string;
1893
+ registered: boolean;
1894
+ knownUsersCount: number;
1895
+ };
1896
+ "connection:open": {
1897
+ configName: string;
1898
+ dialect: string;
1899
+ };
1900
+ "connection:close": {
1901
+ configName: string;
1902
+ };
1903
+ "connection:error": {
1904
+ configName: string;
1905
+ error: string;
1906
+ };
1907
+ "app:starting": {
1908
+ mode: AppMode;
1909
+ };
1910
+ "app:ready": {
1911
+ mode: AppMode;
1912
+ startedAt: Date;
1913
+ };
1914
+ "app:shutdown": {
1915
+ reason: ShutdownReason;
1916
+ exitCode: number;
1917
+ };
1918
+ "app:shutdown:phase": {
1919
+ phase: ShutdownPhase;
1920
+ status: PhaseStatus;
1921
+ durationMs?: number;
1922
+ error?: Error;
1923
+ };
1924
+ "app:exit": {
1925
+ code: number;
1926
+ };
1927
+ "app:fatal": {
1928
+ error: Error;
1929
+ type?: "exception" | "rejection";
1930
+ };
1931
+ "router:navigated": {
1932
+ from: string;
1933
+ to: string;
1934
+ params: Record<string, string | number | boolean | undefined>;
1935
+ };
1936
+ "router:popped": {
1937
+ popped: string;
1938
+ to: string;
1939
+ };
1940
+ error: {
1941
+ source: string;
1942
+ error: Error;
1943
+ context?: Record<string, unknown>;
1944
+ };
1945
+ }
1946
+ export type NoormEventNames = Events<NoormEvents>;
1947
+ /**
1948
+ * SDK Context implementation.
1949
+ *
1950
+ * Provides programmatic access to all noorm operations.
1951
+ *
1952
+ * @example
1953
+ * ```typescript
1954
+ * const ctx = await createContext({ config: 'dev' })
1955
+ * await ctx.connect()
1956
+ *
1957
+ * // Run queries
1958
+ * const users = await ctx.query('SELECT * FROM users')
1959
+ *
1960
+ * // Clean disconnect
1961
+ * await ctx.disconnect()
1962
+ * ```
1963
+ */
1964
+ export declare class Context<DB = unknown> {
1965
+ #private;
1966
+ constructor(config: Config, settings: Settings, identity: Identity, options: CreateContextOptions, projectRoot: string);
1967
+ get config(): Config;
1968
+ get settings(): Settings;
1969
+ get identity(): Identity;
1970
+ get dialect(): Dialect;
1971
+ get connected(): boolean;
1972
+ get observer(): ObserverEngine<NoormEvents>;
1973
+ get kysely(): Kysely<DB>;
1974
+ connect(): Promise<void>;
1975
+ disconnect(): Promise<void>;
1976
+ query<T = Record<string, unknown>>(sqlStr: string, _params?: unknown[]): Promise<T[]>;
1977
+ execute(sqlStr: string, _params?: unknown[]): Promise<ExecuteResult>;
1978
+ transaction<T>(fn: (tx: TransactionContext) => Promise<T>): Promise<T>;
1979
+ listTables(): Promise<TableSummary[]>;
1980
+ describeTable(name: string, schema?: string): Promise<TableDetail | null>;
1981
+ overview(): Promise<ExploreOverview>;
1982
+ truncate(): Promise<TruncateResult>;
1983
+ teardown(): Promise<TeardownResult>;
1984
+ build(options?: BuildOptions): Promise<BatchResult>;
1985
+ reset(): Promise<void>;
1986
+ runFile(filepath: string, options?: RunOptions): Promise<FileResult>;
1987
+ runFiles(filepaths: string[], options?: RunOptions): Promise<BatchResult>;
1988
+ runDir(dirpath: string, options?: RunOptions): Promise<BatchResult>;
1989
+ applyChange(name: string, options?: ChangeOptions): Promise<ChangeResult>;
1990
+ revertChange(name: string, options?: ChangeOptions): Promise<ChangeResult>;
1991
+ fastForward(): Promise<BatchChangeResult>;
1992
+ getChangeStatus(): Promise<ChangeListItem[]>;
1993
+ getPendingChanges(): Promise<ChangeListItem[]>;
1994
+ getSecret(key: string): string | undefined;
1995
+ acquireLock(options?: LockOptions$1): Promise<Lock$1>;
1996
+ releaseLock(): Promise<void>;
1997
+ getLockStatus(): Promise<LockStatus>;
1998
+ withLock<T>(fn: () => Promise<T>, options?: LockOptions$1): Promise<T>;
1999
+ forceReleaseLock(): Promise<boolean>;
2000
+ renderTemplate(filepath: string): Promise<ProcessResult>;
2001
+ getHistory(limit?: number): Promise<ChangeHistoryRecord[]>;
2002
+ computeChecksum(filepath: string): Promise<string>;
2003
+ testConnection(): Promise<{
2004
+ ok: boolean;
2005
+ error?: string;
2006
+ }>;
2007
+ }
2008
+ /**
2009
+ * Error thrown when requireTest is enabled but config.isTest is false.
2010
+ *
2011
+ * @example
2012
+ * ```typescript
2013
+ * const ctx = await createContext({
2014
+ * config: 'prod',
2015
+ * requireTest: true, // Will throw RequireTestError
2016
+ * })
2017
+ * ```
2018
+ */
2019
+ export declare class RequireTestError extends Error {
2020
+ readonly configName: string;
2021
+ readonly name: "RequireTestError";
2022
+ constructor(configName: string);
2023
+ }
2024
+ /**
2025
+ * Error thrown when attempting destructive operations on protected configs.
2026
+ *
2027
+ * @example
2028
+ * ```typescript
2029
+ * // If config.protected is true and allowProtected is false
2030
+ * await ctx.truncate() // Throws ProtectedConfigError
2031
+ * ```
2032
+ */
2033
+ export declare class ProtectedConfigError extends Error {
2034
+ readonly configName: string;
2035
+ readonly operation: string;
2036
+ readonly name: "ProtectedConfigError";
2037
+ constructor(configName: string, operation: string);
2038
+ }
2039
+ /**
2040
+ * Create an SDK context for programmatic database access.
2041
+ *
2042
+ * Configuration is resolved using the full priority chain:
2043
+ * defaults <- stage <- stored <- env <- flags
2044
+ *
2045
+ * This enables:
2046
+ * - ENV var overrides (`NOORM_*`) for stored configs
2047
+ * - Env-only mode (no stored config) for CI/CD
2048
+ *
2049
+ * @param options - Context creation options
2050
+ * @returns Unconnected context (call connect() to use)
2051
+ *
2052
+ * @example
2053
+ * ```typescript
2054
+ * // Basic usage with stored config
2055
+ * const ctx = await createContext({ config: 'dev' })
2056
+ * await ctx.connect()
2057
+ *
2058
+ * // Require test database for safety in tests
2059
+ * const ctx = await createContext({
2060
+ * config: 'test',
2061
+ * requireTest: true,
2062
+ * })
2063
+ *
2064
+ * // Allow destructive ops on protected config
2065
+ * const ctx = await createContext({
2066
+ * config: 'staging',
2067
+ * allowProtected: true,
2068
+ * })
2069
+ *
2070
+ * // Env-only mode (CI/CD) - no stored config needed
2071
+ * // Requires NOORM_CONNECTION_DIALECT and NOORM_CONNECTION_DATABASE
2072
+ * const ctx = await createContext()
2073
+ * ```
2074
+ */
2075
+ export declare function createContext<DB = unknown>(options?: CreateContextOptions): Promise<Context<DB>>;
2076
+
2077
+ export {
2078
+ Lock$1 as Lock,
2079
+ LockOptions$1 as LockOptions,
2080
+ ProcessResult as TemplateResult,
2081
+ };
2082
+
2083
+ export {};