@nest-batch/typeorm 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # `@nest-batch/typeorm`
2
2
 
3
3
  The TypeORM 1.0.0 adapter for [`@nest-batch/core`](../core). It owns
4
- the same six Spring Batch-compatible batch meta-tables that
4
+ the same five active batch meta-tables that
5
5
  `@nest-batch/mikro-orm` ships, expressed as TypeORM 1.0 entities plus
6
- a bundled migration. It exposes `TypeOrmJobRepository` and
6
+ an exported entity tuple. It exposes `TypeOrmJobRepository` and
7
7
  `TypeOrmTransactionManager` and runs the shared core contract suite
8
8
  against a real TypeORM `DataSource`.
9
9
 
@@ -36,9 +36,9 @@ Why 1.0.0 and not 0.3? Two reasons:
36
36
  Maintaining 0.3 support would mean running the full codebase
37
37
  through `Connection → DataSource` shims, which is a waste of
38
38
  effort when 0.3 is on a separate support track.
39
- 2. The entity / migration surface. A few decorators and migration
40
- helpers moved between 0.3 and 1.0. Supporting both means
41
- conditional entity definitions, which is a smell.
39
+ 2. The entity surface. A few decorators and metadata helpers moved
40
+ between 0.3 and 1.0. Supporting both means conditional entity
41
+ definitions, which is a smell.
42
42
 
43
43
  The peer range is `^1.0.0`, so any 1.x release works. The boundary
44
44
  test in core and a dedicated peer-range test in this package
@@ -72,25 +72,24 @@ without the host.
72
72
 
73
73
  ## Schema ownership
74
74
 
75
- This package owns the same six batch meta tables as
75
+ This package owns the same five active batch meta tables as
76
76
  `@nest-batch/mikro-orm`:
77
77
 
78
78
  | Table | Purpose |
79
79
  | ------------------------------ | ---------------------------------------------------------------------------------- |
80
80
  | `batch_job_instance` | One row per logical job (unique on `job_name`+`job_key`). |
81
81
  | `batch_job_execution` | One row per job run. Holds status, start/end, exit code/message. |
82
- | `batch_job_execution_params` | Composite-keyed params (one row per param name). |
83
82
  | `batch_step_execution` | One row per step run. Holds step status, exit code/message, last-chunk checkpoint. |
84
83
  | `batch_job_execution_context` | JSON checkpoint + execution context (job-scoped). |
85
84
  | `batch_step_execution_context` | JSON checkpoint + execution context (step-scoped). |
86
85
 
87
- The bundled migration lives at
88
- `src/migrations/1700000000000-CreateBatchMeta.ts` and is exported
89
- as `CreateBatchMeta1700000000000` from the package root. Apps that
90
- already have a TypeORM migration directory should copy the file in
91
- and renumber it to fit their own migration sequence.
86
+ This package does not publish a runnable migration class. Apps that
87
+ already have a TypeORM migration directory should spread
88
+ `batchMetaEntities()` into their `entities` list, run TypeORM's
89
+ `migration:generate` / `migration:create` flow in the app
90
+ repository, and own the resulting migration file there.
92
91
 
93
- > **Note:** The six batch meta entities are also exported as a
92
+ > **Note:** The five batch meta entities are also exported as a
94
93
  > single tuple under `batchMetaEntities` from the package root.
95
94
  > Because the adapter no longer bootstraps the `DataSource` (the
96
95
  > host owns the `TypeOrmModule.forRoot()` call), spreading
@@ -100,12 +99,11 @@ and renumber it to fit their own migration sequence.
100
99
  > for the meta tables fail silently and the repository throws at
101
100
  > first call.
102
101
 
103
- > The migration uses `datetime` (not `timestamptz`) on the SQLite
104
- > test driver and `timestamptz` on PostgreSQL production. The
105
- > entities declare `datetime` for portability, and the migration
106
- > handler emits the right per-driver type. This is intentional:
107
- > the test suite runs against `better-sqlite3` for speed, and the
108
- > production driver is PostgreSQL.
102
+ > The entities declare portable logical column types because the
103
+ > same contract is exercised against fast SQLite tests and real
104
+ > PostgreSQL/MySQL driver shells. Host-owned migrations may map
105
+ > those logical columns to dialect-specific types such as
106
+ > `timestamptz` or `datetime(6)`.
109
107
 
110
108
  ---
111
109
 
@@ -130,11 +128,7 @@ into its `entities` array, and pass the adapter's no-arg
130
128
  import { Module } from '@nestjs/common';
131
129
  import { TypeOrmModule } from '@nestjs/typeorm';
132
130
  import { NestBatchModule } from '@nest-batch/core';
133
- import {
134
- batchMetaEntities,
135
- CreateBatchMeta1700000000000,
136
- TypeOrmAdapter,
137
- } from '@nest-batch/typeorm';
131
+ import { batchMetaEntities, TypeOrmAdapter } from '@nest-batch/typeorm';
138
132
  import { ProductEntity } from './entities/product.entity';
139
133
 
140
134
  @Module({
@@ -147,7 +141,9 @@ import { ProductEntity } from './entities/product.entity';
147
141
  password: 'demo',
148
142
  database: 'nest_batch_demo',
149
143
  entities: [ProductEntity, ...batchMetaEntities()],
150
- migrations: [CreateBatchMeta1700000000000 /* your other migrations */],
144
+ migrations: [
145
+ /* your app-owned migrations */
146
+ ],
151
147
  migrationsRun: true,
152
148
  }),
153
149
  NestBatchModule.forRoot({
@@ -4,8 +4,8 @@
4
4
  * One row per logical job instance. Uniqueness is enforced on
5
5
  * (jobName, jobKey) so that the same canonical key resolves to the
6
6
  * same instance across restarts. The composite unique index is
7
- * declared on the entity and is also reified in the bundled
8
- * migration under the same name.
7
+ * declared on the entity so host-owned TypeORM migrations can
8
+ * generate the matching database constraint.
9
9
  */
10
10
  export declare class JobInstanceEntity {
11
11
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"job-meta.entities.d.ts","sourceRoot":"","sources":["../../../src/entities/job-meta.entities.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,qBAEa,iBAAiB;IAE5B,EAAE,EAAG,MAAM,CAAC;IAGZ,OAAO,EAAG,MAAM,CAAC;IAGjB,MAAM,EAAG,MAAM,CAAC;IAYhB,SAAS,EAAE,IAAI,CAAc;CAC9B;AAED;;;;;;;;GAQG;AACH,qBAEa,kBAAkB;IAE7B,EAAE,EAAG,MAAM,CAAC;IAGZ,aAAa,EAAG,MAAM,CAAC;IAGvB,MAAM,EAAG,MAAM,CAAC;IAGhB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAQ;IAG9B,OAAO,EAAE,IAAI,GAAG,IAAI,CAAQ;IAG5B,QAAQ,EAAG,MAAM,CAAC;IAGlB,WAAW,EAAG,MAAM,CAAC;IAErB;;;;;OAKG;IAEH,MAAM,EAAG,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,qBAEa,mBAAmB;IAE9B,EAAE,EAAG,MAAM,CAAC;IAGZ,cAAc,EAAG,MAAM,CAAC;IAGxB,QAAQ,EAAG,MAAM,CAAC;IAGlB,MAAM,EAAG,MAAM,CAAC;IAGhB,SAAS,EAAG,MAAM,CAAC;IAGnB,UAAU,EAAG,MAAM,CAAC;IAGpB,SAAS,EAAG,MAAM,CAAC;IAGnB,aAAa,EAAG,MAAM,CAAC;IAGvB,WAAW,EAAG,MAAM,CAAC;IAGrB,QAAQ,EAAG,MAAM,CAAC;IAGlB,WAAW,EAAG,MAAM,CAAC;IAOrB,SAAS,EAAE,IAAI,CAAc;CAC9B;AAED;;;;;GAKG;AACH,qBACa,yBAAyB;IAEpC,cAAc,EAAG,MAAM,CAAC;IAGxB,IAAI,EAAG,MAAM,CAAC;IAGd,OAAO,EAAG,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,qBACa,0BAA0B;IAErC,eAAe,EAAG,MAAM,CAAC;IAGzB,IAAI,EAAG,MAAM,CAAC;IAGd,OAAO,EAAG,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,iKAMtB,CAAC"}
1
+ {"version":3,"file":"job-meta.entities.d.ts","sourceRoot":"","sources":["../../../src/entities/job-meta.entities.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,qBAEa,iBAAiB;IAE5B,EAAE,EAAG,MAAM,CAAC;IAGZ,OAAO,EAAG,MAAM,CAAC;IAGjB,MAAM,EAAG,MAAM,CAAC;IAWhB,SAAS,EAAE,IAAI,CAAc;CAC9B;AAED;;;;;;;;GAQG;AACH,qBAEa,kBAAkB;IAE7B,EAAE,EAAG,MAAM,CAAC;IAGZ,aAAa,EAAG,MAAM,CAAC;IAGvB,MAAM,EAAG,MAAM,CAAC;IAGhB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAQ;IAG9B,OAAO,EAAE,IAAI,GAAG,IAAI,CAAQ;IAG5B,QAAQ,EAAG,MAAM,CAAC;IAGlB,WAAW,EAAG,MAAM,CAAC;IAErB;;;;;OAKG;IAEH,MAAM,EAAG,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,qBAEa,mBAAmB;IAE9B,EAAE,EAAG,MAAM,CAAC;IAGZ,cAAc,EAAG,MAAM,CAAC;IAGxB,QAAQ,EAAG,MAAM,CAAC;IAGlB,MAAM,EAAG,MAAM,CAAC;IAGhB,SAAS,EAAG,MAAM,CAAC;IAGnB,UAAU,EAAG,MAAM,CAAC;IAGpB,SAAS,EAAG,MAAM,CAAC;IAGnB,aAAa,EAAG,MAAM,CAAC;IAGvB,WAAW,EAAG,MAAM,CAAC;IAGrB,QAAQ,EAAG,MAAM,CAAC;IAGlB,WAAW,EAAG,MAAM,CAAC;IAOrB,SAAS,EAAE,IAAI,CAAc;CAC9B;AAED;;;;;GAKG;AACH,qBACa,yBAAyB;IAEpC,cAAc,EAAG,MAAM,CAAC;IAGxB,IAAI,EAAG,MAAM,CAAC;IAGd,OAAO,EAAG,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,qBACa,0BAA0B;IAErC,eAAe,EAAG,MAAM,CAAC;IAGzB,IAAI,EAAG,MAAM,CAAC;IAGd,OAAO,EAAG,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,iKAMtB,CAAC"}
@@ -71,10 +71,9 @@ _ts_decorate([
71
71
  (0, _typeorm.Column)({
72
72
  name: 'created_at',
73
73
  // `datetime` is portable across PostgreSQL and SQLite (the test
74
- // driver). The bundled migration uses timestamptz on
75
- // PostgreSQL by hand; SQLite loses the timezone qualifier,
76
- // which is acceptable for a creation-time stamp that is never
77
- // compared with sub-second precision in queries.
74
+ // driver). Hosts that generate PostgreSQL migrations can map the
75
+ // same logical column to timestamptz in their own migration
76
+ // pipeline.
78
77
  type: 'datetime',
79
78
  default: ()=>'CURRENT_TIMESTAMP'
80
79
  }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/entities/job-meta.entities.ts"],"sourcesContent":["import { Entity, PrimaryColumn, Column, Index } from 'typeorm';\n\n/**\n * `batch_job_instance` metadata row.\n *\n * One row per logical job instance. Uniqueness is enforced on\n * (jobName, jobKey) so that the same canonical key resolves to the\n * same instance across restarts. The composite unique index is\n * declared on the entity and is also reified in the bundled\n * migration under the same name.\n */\n@Entity('batch_job_instance')\n@Index('batch_job_instance_job_name_job_key_unique', ['jobName', 'jobKey'], { unique: true })\nexport class JobInstanceEntity {\n @PrimaryColumn({ type: 'varchar', length: 255 })\n id!: string;\n\n @Column({ name: 'job_name', type: 'varchar', length: 255 })\n jobName!: string;\n\n @Column({ name: 'job_key', type: 'varchar', length: 255 })\n jobKey!: string;\n\n @Column({\n name: 'created_at',\n // `datetime` is portable across PostgreSQL and SQLite (the test\n // driver). The bundled migration uses timestamptz on\n // PostgreSQL by hand; SQLite loses the timezone qualifier,\n // which is acceptable for a creation-time stamp that is never\n // compared with sub-second precision in queries.\n type: 'datetime',\n default: () => 'CURRENT_TIMESTAMP',\n })\n createdAt: Date = new Date();\n}\n\n/**\n * `batch_job_execution` metadata row.\n *\n * One row per job run. `status` is the stringified JobStatus enum\n * (kept as a plain varchar — TypeORM enum support varies across\n * drivers and v1.0.0 dropped a few columns we don't need). The\n * `jobInstanceId` column is indexed because every contract lookup\n * (\"is this instance running?\") scans by it.\n */\n@Entity('batch_job_execution')\n@Index('batch_job_execution_job_instance_id_index', ['jobInstanceId'])\nexport class JobExecutionEntity {\n @PrimaryColumn({ type: 'varchar', length: 255 })\n id!: string;\n\n @Column({ name: 'job_instance_id', type: 'varchar', length: 255 })\n jobInstanceId!: string;\n\n @Column({ type: 'varchar', length: 20 })\n status!: string;\n\n @Column({ name: 'start_time', type: 'datetime', nullable: true })\n startTime: Date | null = null;\n\n @Column({ name: 'end_time', type: 'datetime', nullable: true })\n endTime: Date | null = null;\n\n @Column({ name: 'exit_code', type: 'varchar', length: 255, default: '' })\n exitCode!: string;\n\n @Column({ name: 'exit_message', type: 'text', default: '' })\n exitMessage!: string;\n\n /**\n * JSON-serialized `JobParameters` snapshot. Stored as `text` (not\n * native `jsonb`) so the adapter works uniformly across SQLite (used\n * in unit tests) and PostgreSQL/MySQL — the column is always a\n * serialized payload, never queried by the ORM.\n */\n @Column({ name: 'params', type: 'text', default: '{}' })\n params!: string;\n}\n\n/**\n * `batch_step_execution` metadata row.\n *\n * One row per step run. Counters default to 0 so the entity can\n * be persisted immediately upon creation, before any items are\n * processed. `createdAt` is stamped on insert and used by\n * `findLatestStepExecution` to resolve the most recently created\n * step for a given `(jobExecutionId, stepName)` pair — a v4 UUID\n * primary key does not preserve insertion order, so an explicit\n * monotonic column is required.\n */\n@Entity('batch_step_execution')\n@Index('batch_step_execution_job_execution_id_index', ['jobExecutionId'])\nexport class StepExecutionEntity {\n @PrimaryColumn({ type: 'varchar', length: 255 })\n id!: string;\n\n @Column({ name: 'job_execution_id', type: 'varchar', length: 255 })\n jobExecutionId!: string;\n\n @Column({ name: 'step_name', type: 'varchar', length: 255 })\n stepName!: string;\n\n @Column({ type: 'varchar', length: 20 })\n status!: string;\n\n @Column({ name: 'read_count', type: 'int', default: 0 })\n readCount!: number;\n\n @Column({ name: 'write_count', type: 'int', default: 0 })\n writeCount!: number;\n\n @Column({ name: 'skip_count', type: 'int', default: 0 })\n skipCount!: number;\n\n @Column({ name: 'rollback_count', type: 'int', default: 0 })\n rollbackCount!: number;\n\n @Column({ name: 'commit_count', type: 'int', default: 0 })\n commitCount!: number;\n\n @Column({ name: 'exit_code', type: 'varchar', length: 255, default: '' })\n exitCode!: string;\n\n @Column({ name: 'exit_message', type: 'text', default: '' })\n exitMessage!: string;\n\n @Column({\n name: 'created_at',\n type: 'datetime',\n default: () => 'CURRENT_TIMESTAMP',\n })\n createdAt: Date = new Date();\n}\n\n/**\n * `batch_job_execution_context` metadata row.\n *\n * `data` is a JSON-serialized ExecutionContext payload. `version`\n * guards against lost updates during concurrent writers.\n */\n@Entity('batch_job_execution_context')\nexport class JobExecutionContextEntity {\n @PrimaryColumn({ name: 'job_execution_id', type: 'varchar', length: 255 })\n jobExecutionId!: string;\n\n @Column({ type: 'text' })\n data!: string;\n\n @Column({ type: 'int', default: 0 })\n version!: number;\n}\n\n/**\n * `batch_step_execution_context` metadata row.\n *\n * Mirrors the job-level context table but scoped to a single\n * step execution. There is intentionally no params sibling table\n * for steps — step parameters are derivable from the parent job\n * execution params + the step execution context.\n */\n@Entity('batch_step_execution_context')\nexport class StepExecutionContextEntity {\n @PrimaryColumn({ name: 'step_execution_id', type: 'varchar', length: 255 })\n stepExecutionId!: string;\n\n @Column({ type: 'text' })\n data!: string;\n\n @Column({ type: 'int', default: 0 })\n version!: number;\n}\n\n/**\n * All batch meta entities owned by this package. Hand to\n * `DataSource#entityMetadatas` (or `entities:`) so TypeORM\n * discovers them through the standard decorator scan.\n */\nexport const BATCH_META_ENTITIES = [\n JobInstanceEntity,\n JobExecutionEntity,\n StepExecutionEntity,\n JobExecutionContextEntity,\n StepExecutionContextEntity,\n] as const;\n"],"names":["BATCH_META_ENTITIES","JobExecutionContextEntity","JobExecutionEntity","JobInstanceEntity","StepExecutionContextEntity","StepExecutionEntity","id","jobName","jobKey","createdAt","Date","type","length","name","default","unique","jobInstanceId","status","startTime","endTime","exitCode","exitMessage","params","nullable","jobExecutionId","stepName","readCount","writeCount","skipCount","rollbackCount","commitCount","data","version","stepExecutionId"],"mappings":";;;;;;;;;;;QAiLaA;eAAAA;;QApCAC;eAAAA;;QA9FAC;eAAAA;;QAlCAC;eAAAA;;QAoJAC;eAAAA;;QArEAC;eAAAA;;;yBA5FwC;;;;;;;;;;AAa9C,IAAA,AAAMF,oBAAN,MAAMA;IAEXG,GAAY;IAGZC,QAAiB;IAGjBC,OAAgB;IAYhBC,YAAkB,IAAIC,OAAO;AAC/B;;;QApBmBC,MAAM;QAAWC,QAAQ;;;;;;QAGhCC,MAAM;QAAYF,MAAM;QAAWC,QAAQ;;;;;;QAG3CC,MAAM;QAAWF,MAAM;QAAWC,QAAQ;;;;;;QAIlDC,MAAM;QACN,gEAAgE;QAChE,qDAAqD;QACrD,2DAA2D;QAC3D,8DAA8D;QAC9D,iDAAiD;QACjDF,MAAM;QACNG,SAAS,IAAM;;;;;;;QAnBmC;QAAW;;QAAaC,QAAQ;;;AAmC/E,IAAA,AAAMb,qBAAN,MAAMA;IAEXI,GAAY;IAGZU,cAAuB;IAGvBC,OAAgB;IAGhBC,YAAyB,KAAK;IAG9BC,UAAuB,KAAK;IAG5BC,SAAkB;IAGlBC,YAAqB;IAErB;;;;;GAKC,GACD,AACAC,OAAgB;AAClB;;;QA7BmBX,MAAM;QAAWC,QAAQ;;;;;;QAGhCC,MAAM;QAAmBF,MAAM;QAAWC,QAAQ;;;;;;QAGlDD,MAAM;QAAWC,QAAQ;;;;;;QAGzBC,MAAM;QAAcF,MAAM;QAAYY,UAAU;;;;;;QAGhDV,MAAM;QAAYF,MAAM;QAAYY,UAAU;;;;;;QAG9CV,MAAM;QAAaF,MAAM;QAAWC,QAAQ;QAAKE,SAAS;;;;;;QAG1DD,MAAM;QAAgBF,MAAM;QAAQG,SAAS;;;;;;QAS7CD,MAAM;QAAUF,MAAM;QAAQG,SAAS;;;;;;;QA7BE;;;AA8C9C,IAAA,AAAMT,sBAAN,MAAMA;IAEXC,GAAY;IAGZkB,eAAwB;IAGxBC,SAAkB;IAGlBR,OAAgB;IAGhBS,UAAmB;IAGnBC,WAAoB;IAGpBC,UAAmB;IAGnBC,cAAuB;IAGvBC,YAAqB;IAGrBV,SAAkB;IAGlBC,YAAqB;IAOrBZ,YAAkB,IAAIC,OAAO;AAC/B;;;QAvCmBC,MAAM;QAAWC,QAAQ;;;;;;QAGhCC,MAAM;QAAoBF,MAAM;QAAWC,QAAQ;;;;;;QAGnDC,MAAM;QAAaF,MAAM;QAAWC,QAAQ;;;;;;QAG5CD,MAAM;QAAWC,QAAQ;;;;;;QAGzBC,MAAM;QAAcF,MAAM;QAAOG,SAAS;;;;;;QAG1CD,MAAM;QAAeF,MAAM;QAAOG,SAAS;;;;;;QAG3CD,MAAM;QAAcF,MAAM;QAAOG,SAAS;;;;;;QAG1CD,MAAM;QAAkBF,MAAM;QAAOG,SAAS;;;;;;QAG9CD,MAAM;QAAgBF,MAAM;QAAOG,SAAS;;;;;;QAG5CD,MAAM;QAAaF,MAAM;QAAWC,QAAQ;QAAKE,SAAS;;;;;;QAG1DD,MAAM;QAAgBF,MAAM;QAAQG,SAAS;;;;;;QAIrDD,MAAM;QACNF,MAAM;QACNG,SAAS,IAAM;;;;;;;QAtCoC;;;AAkDhD,IAAA,AAAMb,4BAAN,MAAMA;IAEXuB,eAAwB;IAGxBO,KAAc;IAGdC,QAAiB;AACnB;;;QARmBnB,MAAM;QAAoBF,MAAM;QAAWC,QAAQ;;;;;;QAG1DD,MAAM;;;;;;QAGNA,MAAM;QAAOG,SAAS;;;;;;;AAa3B,IAAA,AAAMV,6BAAN,MAAMA;IAEX6B,gBAAyB;IAGzBF,KAAc;IAGdC,QAAiB;AACnB;;;QARmBnB,MAAM;QAAqBF,MAAM;QAAWC,QAAQ;;;;;;QAG3DD,MAAM;;;;;;QAGNA,MAAM;QAAOG,SAAS;;;;;;;AAS3B,MAAMd,sBAAsB;IACjCG;IACAD;IACAG;IACAJ;IACAG;CACD"}
1
+ {"version":3,"sources":["../../../src/entities/job-meta.entities.ts"],"sourcesContent":["import { Entity, PrimaryColumn, Column, Index } from 'typeorm';\n\n/**\n * `batch_job_instance` metadata row.\n *\n * One row per logical job instance. Uniqueness is enforced on\n * (jobName, jobKey) so that the same canonical key resolves to the\n * same instance across restarts. The composite unique index is\n * declared on the entity so host-owned TypeORM migrations can\n * generate the matching database constraint.\n */\n@Entity('batch_job_instance')\n@Index('batch_job_instance_job_name_job_key_unique', ['jobName', 'jobKey'], { unique: true })\nexport class JobInstanceEntity {\n @PrimaryColumn({ type: 'varchar', length: 255 })\n id!: string;\n\n @Column({ name: 'job_name', type: 'varchar', length: 255 })\n jobName!: string;\n\n @Column({ name: 'job_key', type: 'varchar', length: 255 })\n jobKey!: string;\n\n @Column({\n name: 'created_at',\n // `datetime` is portable across PostgreSQL and SQLite (the test\n // driver). Hosts that generate PostgreSQL migrations can map the\n // same logical column to timestamptz in their own migration\n // pipeline.\n type: 'datetime',\n default: () => 'CURRENT_TIMESTAMP',\n })\n createdAt: Date = new Date();\n}\n\n/**\n * `batch_job_execution` metadata row.\n *\n * One row per job run. `status` is the stringified JobStatus enum\n * (kept as a plain varchar — TypeORM enum support varies across\n * drivers and v1.0.0 dropped a few columns we don't need). The\n * `jobInstanceId` column is indexed because every contract lookup\n * (\"is this instance running?\") scans by it.\n */\n@Entity('batch_job_execution')\n@Index('batch_job_execution_job_instance_id_index', ['jobInstanceId'])\nexport class JobExecutionEntity {\n @PrimaryColumn({ type: 'varchar', length: 255 })\n id!: string;\n\n @Column({ name: 'job_instance_id', type: 'varchar', length: 255 })\n jobInstanceId!: string;\n\n @Column({ type: 'varchar', length: 20 })\n status!: string;\n\n @Column({ name: 'start_time', type: 'datetime', nullable: true })\n startTime: Date | null = null;\n\n @Column({ name: 'end_time', type: 'datetime', nullable: true })\n endTime: Date | null = null;\n\n @Column({ name: 'exit_code', type: 'varchar', length: 255, default: '' })\n exitCode!: string;\n\n @Column({ name: 'exit_message', type: 'text', default: '' })\n exitMessage!: string;\n\n /**\n * JSON-serialized `JobParameters` snapshot. Stored as `text` (not\n * native `jsonb`) so the adapter works uniformly across SQLite (used\n * in unit tests) and PostgreSQL/MySQL — the column is always a\n * serialized payload, never queried by the ORM.\n */\n @Column({ name: 'params', type: 'text', default: '{}' })\n params!: string;\n}\n\n/**\n * `batch_step_execution` metadata row.\n *\n * One row per step run. Counters default to 0 so the entity can\n * be persisted immediately upon creation, before any items are\n * processed. `createdAt` is stamped on insert and used by\n * `findLatestStepExecution` to resolve the most recently created\n * step for a given `(jobExecutionId, stepName)` pair — a v4 UUID\n * primary key does not preserve insertion order, so an explicit\n * monotonic column is required.\n */\n@Entity('batch_step_execution')\n@Index('batch_step_execution_job_execution_id_index', ['jobExecutionId'])\nexport class StepExecutionEntity {\n @PrimaryColumn({ type: 'varchar', length: 255 })\n id!: string;\n\n @Column({ name: 'job_execution_id', type: 'varchar', length: 255 })\n jobExecutionId!: string;\n\n @Column({ name: 'step_name', type: 'varchar', length: 255 })\n stepName!: string;\n\n @Column({ type: 'varchar', length: 20 })\n status!: string;\n\n @Column({ name: 'read_count', type: 'int', default: 0 })\n readCount!: number;\n\n @Column({ name: 'write_count', type: 'int', default: 0 })\n writeCount!: number;\n\n @Column({ name: 'skip_count', type: 'int', default: 0 })\n skipCount!: number;\n\n @Column({ name: 'rollback_count', type: 'int', default: 0 })\n rollbackCount!: number;\n\n @Column({ name: 'commit_count', type: 'int', default: 0 })\n commitCount!: number;\n\n @Column({ name: 'exit_code', type: 'varchar', length: 255, default: '' })\n exitCode!: string;\n\n @Column({ name: 'exit_message', type: 'text', default: '' })\n exitMessage!: string;\n\n @Column({\n name: 'created_at',\n type: 'datetime',\n default: () => 'CURRENT_TIMESTAMP',\n })\n createdAt: Date = new Date();\n}\n\n/**\n * `batch_job_execution_context` metadata row.\n *\n * `data` is a JSON-serialized ExecutionContext payload. `version`\n * guards against lost updates during concurrent writers.\n */\n@Entity('batch_job_execution_context')\nexport class JobExecutionContextEntity {\n @PrimaryColumn({ name: 'job_execution_id', type: 'varchar', length: 255 })\n jobExecutionId!: string;\n\n @Column({ type: 'text' })\n data!: string;\n\n @Column({ type: 'int', default: 0 })\n version!: number;\n}\n\n/**\n * `batch_step_execution_context` metadata row.\n *\n * Mirrors the job-level context table but scoped to a single\n * step execution. There is intentionally no params sibling table\n * for steps — step parameters are derivable from the parent job\n * execution params + the step execution context.\n */\n@Entity('batch_step_execution_context')\nexport class StepExecutionContextEntity {\n @PrimaryColumn({ name: 'step_execution_id', type: 'varchar', length: 255 })\n stepExecutionId!: string;\n\n @Column({ type: 'text' })\n data!: string;\n\n @Column({ type: 'int', default: 0 })\n version!: number;\n}\n\n/**\n * All batch meta entities owned by this package. Hand to\n * `DataSource#entityMetadatas` (or `entities:`) so TypeORM\n * discovers them through the standard decorator scan.\n */\nexport const BATCH_META_ENTITIES = [\n JobInstanceEntity,\n JobExecutionEntity,\n StepExecutionEntity,\n JobExecutionContextEntity,\n StepExecutionContextEntity,\n] as const;\n"],"names":["BATCH_META_ENTITIES","JobExecutionContextEntity","JobExecutionEntity","JobInstanceEntity","StepExecutionContextEntity","StepExecutionEntity","id","jobName","jobKey","createdAt","Date","type","length","name","default","unique","jobInstanceId","status","startTime","endTime","exitCode","exitMessage","params","nullable","jobExecutionId","stepName","readCount","writeCount","skipCount","rollbackCount","commitCount","data","version","stepExecutionId"],"mappings":";;;;;;;;;;;QAgLaA;eAAAA;;QApCAC;eAAAA;;QA9FAC;eAAAA;;QAjCAC;eAAAA;;QAmJAC;eAAAA;;QArEAC;eAAAA;;;yBA3FwC;;;;;;;;;;AAa9C,IAAA,AAAMF,oBAAN,MAAMA;IAEXG,GAAY;IAGZC,QAAiB;IAGjBC,OAAgB;IAWhBC,YAAkB,IAAIC,OAAO;AAC/B;;;QAnBmBC,MAAM;QAAWC,QAAQ;;;;;;QAGhCC,MAAM;QAAYF,MAAM;QAAWC,QAAQ;;;;;;QAG3CC,MAAM;QAAWF,MAAM;QAAWC,QAAQ;;;;;;QAIlDC,MAAM;QACN,gEAAgE;QAChE,iEAAiE;QACjE,4DAA4D;QAC5D,YAAY;QACZF,MAAM;QACNG,SAAS,IAAM;;;;;;;QAlBmC;QAAW;;QAAaC,QAAQ;;;AAkC/E,IAAA,AAAMb,qBAAN,MAAMA;IAEXI,GAAY;IAGZU,cAAuB;IAGvBC,OAAgB;IAGhBC,YAAyB,KAAK;IAG9BC,UAAuB,KAAK;IAG5BC,SAAkB;IAGlBC,YAAqB;IAErB;;;;;GAKC,GACD,AACAC,OAAgB;AAClB;;;QA7BmBX,MAAM;QAAWC,QAAQ;;;;;;QAGhCC,MAAM;QAAmBF,MAAM;QAAWC,QAAQ;;;;;;QAGlDD,MAAM;QAAWC,QAAQ;;;;;;QAGzBC,MAAM;QAAcF,MAAM;QAAYY,UAAU;;;;;;QAGhDV,MAAM;QAAYF,MAAM;QAAYY,UAAU;;;;;;QAG9CV,MAAM;QAAaF,MAAM;QAAWC,QAAQ;QAAKE,SAAS;;;;;;QAG1DD,MAAM;QAAgBF,MAAM;QAAQG,SAAS;;;;;;QAS7CD,MAAM;QAAUF,MAAM;QAAQG,SAAS;;;;;;;QA7BE;;;AA8C9C,IAAA,AAAMT,sBAAN,MAAMA;IAEXC,GAAY;IAGZkB,eAAwB;IAGxBC,SAAkB;IAGlBR,OAAgB;IAGhBS,UAAmB;IAGnBC,WAAoB;IAGpBC,UAAmB;IAGnBC,cAAuB;IAGvBC,YAAqB;IAGrBV,SAAkB;IAGlBC,YAAqB;IAOrBZ,YAAkB,IAAIC,OAAO;AAC/B;;;QAvCmBC,MAAM;QAAWC,QAAQ;;;;;;QAGhCC,MAAM;QAAoBF,MAAM;QAAWC,QAAQ;;;;;;QAGnDC,MAAM;QAAaF,MAAM;QAAWC,QAAQ;;;;;;QAG5CD,MAAM;QAAWC,QAAQ;;;;;;QAGzBC,MAAM;QAAcF,MAAM;QAAOG,SAAS;;;;;;QAG1CD,MAAM;QAAeF,MAAM;QAAOG,SAAS;;;;;;QAG3CD,MAAM;QAAcF,MAAM;QAAOG,SAAS;;;;;;QAG1CD,MAAM;QAAkBF,MAAM;QAAOG,SAAS;;;;;;QAG9CD,MAAM;QAAgBF,MAAM;QAAOG,SAAS;;;;;;QAG5CD,MAAM;QAAaF,MAAM;QAAWC,QAAQ;QAAKE,SAAS;;;;;;QAG1DD,MAAM;QAAgBF,MAAM;QAAQG,SAAS;;;;;;QAIrDD,MAAM;QACNF,MAAM;QACNG,SAAS,IAAM;;;;;;;QAtCoC;;;AAkDhD,IAAA,AAAMb,4BAAN,MAAMA;IAEXuB,eAAwB;IAGxBO,KAAc;IAGdC,QAAiB;AACnB;;;QARmBnB,MAAM;QAAoBF,MAAM;QAAWC,QAAQ;;;;;;QAG1DD,MAAM;;;;;;QAGNA,MAAM;QAAOG,SAAS;;;;;;;AAa3B,IAAA,AAAMV,6BAAN,MAAMA;IAEX6B,gBAAyB;IAGzBF,KAAc;IAGdC,QAAiB;AACnB;;;QARmBnB,MAAM;QAAqBF,MAAM;QAAWC,QAAQ;;;;;;QAG3DD,MAAM;;;;;;QAGNA,MAAM;QAAOG,SAAS;;;;;;;AAS3B,MAAMd,sBAAsB;IACjCG;IACAD;IACAG;IACAJ;IACAG;CACD"}
@@ -1,6 +1,9 @@
1
+ import { BATCH_META_ENTITIES } from './entities';
1
2
  export { TypeOrmJobRepository } from './repository/typeorm-job-repository';
2
3
  export type { TypeOrmTransactionContext } from './transaction/typeorm-transaction-manager';
3
4
  export { TypeOrmTransactionManager } from './transaction/typeorm-transaction-manager';
4
5
  export * from './adapters';
6
+ export { BATCH_META_ENTITIES } from './entities';
5
7
  export * from './typeorm.driver-provider';
8
+ export declare const batchMetaEntities: () => typeof BATCH_META_ENTITIES;
6
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAqCA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAC3E,YAAY,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AAC3F,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AACtF,cAAc,YAAY,CAAC;AAC3B,cAAc,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAoCA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAC3E,YAAY,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AAC3F,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AACtF,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,cAAc,2BAA2B,CAAC;AAE1C,eAAO,MAAM,iBAAiB,QAAO,OAAO,mBAA0C,CAAC"}
package/dist/src/index.js CHANGED
@@ -29,12 +29,11 @@
29
29
  // },
30
30
  // });
31
31
  //
32
- // The original `batchMetaEntities()` factory and the bundled
33
- // `CreateBatchMeta1700000000000` migration moved to
34
- // `@nest-batch/postgresql/src/migrations/`. The driver sibling owns
35
- // the TypeORM-specific entity classes and the migration scripts;
36
- // this package owns only the repository / transaction manager
37
- // shape and the driver-provider token.
32
+ // The TypeORM entity tuple stays in this package because it is the
33
+ // schema contract consumed by a host-owned TypeORM DataSource. Apps
34
+ // generate and own their runnable migration files in their own
35
+ // migration workflow. Driver siblings bind the
36
+ // `TypeOrmDriverProvider` token to a concrete database connection.
38
37
  "use strict";
39
38
  Object.defineProperty(exports, "__esModule", {
40
39
  value: true
@@ -46,13 +45,20 @@ function _export(target, all) {
46
45
  });
47
46
  }
48
47
  _export(exports, {
48
+ get BATCH_META_ENTITIES () {
49
+ return _entities.BATCH_META_ENTITIES;
50
+ },
49
51
  get TypeOrmJobRepository () {
50
52
  return _typeormjobrepository.TypeOrmJobRepository;
51
53
  },
52
54
  get TypeOrmTransactionManager () {
53
55
  return _typeormtransactionmanager.TypeOrmTransactionManager;
56
+ },
57
+ get batchMetaEntities () {
58
+ return batchMetaEntities;
54
59
  }
55
60
  });
61
+ const _entities = require("./entities");
56
62
  const _typeormjobrepository = require("./repository/typeorm-job-repository");
57
63
  const _typeormtransactionmanager = require("./transaction/typeorm-transaction-manager");
58
64
  _export_star(require("./adapters"), exports);
@@ -70,5 +76,6 @@ function _export_star(from, to) {
70
76
  });
71
77
  return from;
72
78
  }
79
+ const batchMetaEntities = ()=>_entities.BATCH_META_ENTITIES;
73
80
 
74
81
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["// Public API barrel for @nest-batch/typeorm.\n//\n// The package is a **driver-agnostic adapter SLOT**. It owns the\n// `TypeOrmAdapter` factory, the `TypeOrmJobRepository` /\n// `TypeOrmTransactionManager` interface shape, and the\n// `TypeOrmDriverProvider` injection token. It does NOT import\n// `@nestjs/typeorm` (which carries the Postgres driver) — the\n// driver implementation lives in the `@nest-batch/postgresql` (or\n// `@nest-batch/mysql`) sibling package, which binds the\n// `TypeOrmDriverProvider` token to the concrete `DataSource`\n// in its own `forRoot()` factory.\n//\n// Apps wire the persistence concern into `NestBatchModule.forRoot()`\n// via the new `BatchAdapter` factory pattern:\n//\n// import { NestBatchModule, InProcessAdapter } from '@nest-batch/core';\n// import { TypeOrmAdapter } from '@nest-batch/typeorm';\n// import { PostgresAdapter } from '@nest-batch/postgresql';\n//\n// // The host must also call\n// // `TypeOrmModule.forRoot({ ... })` in their `AppModule.imports`.\n// // The PostgresAdapter.forRoot() factory binds the\n// // TypeOrmDriverProvider token to the host's DataSource.\n//\n// NestBatchModule.forRoot({\n// adapters: {\n// persistence: PostgresAdapter.forRoot(),\n// transport: InProcessAdapter.forRoot(),\n// },\n// });\n//\n// The original `batchMetaEntities()` factory and the bundled\n// `CreateBatchMeta1700000000000` migration moved to\n// `@nest-batch/postgresql/src/migrations/`. The driver sibling owns\n// the TypeORM-specific entity classes and the migration scripts;\n// this package owns only the repository / transaction manager\n// shape and the driver-provider token.\nexport { TypeOrmJobRepository } from './repository/typeorm-job-repository';\nexport type { TypeOrmTransactionContext } from './transaction/typeorm-transaction-manager';\nexport { TypeOrmTransactionManager } from './transaction/typeorm-transaction-manager';\nexport * from './adapters';\nexport * from './typeorm.driver-provider';\n"],"names":["TypeOrmJobRepository","TypeOrmTransactionManager"],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,iEAAiE;AACjE,yDAAyD;AACzD,uDAAuD;AACvD,8DAA8D;AAC9D,8DAA8D;AAC9D,kEAAkE;AAClE,wDAAwD;AACxD,6DAA6D;AAC7D,kCAAkC;AAClC,EAAE;AACF,qEAAqE;AACrE,8CAA8C;AAC9C,EAAE;AACF,0EAA0E;AAC1E,0DAA0D;AAC1D,8DAA8D;AAC9D,EAAE;AACF,+BAA+B;AAC/B,sEAAsE;AACtE,uDAAuD;AACvD,6DAA6D;AAC7D,EAAE;AACF,8BAA8B;AAC9B,kBAAkB;AAClB,gDAAgD;AAChD,+CAA+C;AAC/C,SAAS;AACT,QAAQ;AACR,EAAE;AACF,6DAA6D;AAC7D,oDAAoD;AACpD,oEAAoE;AACpE,iEAAiE;AACjE,8DAA8D;AAC9D,uCAAuC;;;;;;;;;;;;QAC9BA;eAAAA,0CAAoB;;QAEpBC;eAAAA,oDAAyB;;;sCAFG;2CAEK;qBAC5B;qBACA"}
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["// Public API barrel for @nest-batch/typeorm.\n//\n// The package is a **driver-agnostic adapter SLOT**. It owns the\n// `TypeOrmAdapter` factory, the `TypeOrmJobRepository` /\n// `TypeOrmTransactionManager` interface shape, and the\n// `TypeOrmDriverProvider` injection token. It does NOT import\n// `@nestjs/typeorm` (which carries the Postgres driver) — the\n// driver implementation lives in the `@nest-batch/postgresql` (or\n// `@nest-batch/mysql`) sibling package, which binds the\n// `TypeOrmDriverProvider` token to the concrete `DataSource`\n// in its own `forRoot()` factory.\n//\n// Apps wire the persistence concern into `NestBatchModule.forRoot()`\n// via the new `BatchAdapter` factory pattern:\n//\n// import { NestBatchModule, InProcessAdapter } from '@nest-batch/core';\n// import { TypeOrmAdapter } from '@nest-batch/typeorm';\n// import { PostgresAdapter } from '@nest-batch/postgresql';\n//\n// // The host must also call\n// // `TypeOrmModule.forRoot({ ... })` in their `AppModule.imports`.\n// // The PostgresAdapter.forRoot() factory binds the\n// // TypeOrmDriverProvider token to the host's DataSource.\n//\n// NestBatchModule.forRoot({\n// adapters: {\n// persistence: PostgresAdapter.forRoot(),\n// transport: InProcessAdapter.forRoot(),\n// },\n// });\n//\n// The TypeORM entity tuple stays in this package because it is the\n// schema contract consumed by a host-owned TypeORM DataSource. Apps\n// generate and own their runnable migration files in their own\n// migration workflow. Driver siblings bind the\n// `TypeOrmDriverProvider` token to a concrete database connection.\nimport { BATCH_META_ENTITIES } from './entities';\n\nexport { TypeOrmJobRepository } from './repository/typeorm-job-repository';\nexport type { TypeOrmTransactionContext } from './transaction/typeorm-transaction-manager';\nexport { TypeOrmTransactionManager } from './transaction/typeorm-transaction-manager';\nexport * from './adapters';\nexport { BATCH_META_ENTITIES } from './entities';\nexport * from './typeorm.driver-provider';\n\nexport const batchMetaEntities = (): typeof BATCH_META_ENTITIES => BATCH_META_ENTITIES;\n"],"names":["BATCH_META_ENTITIES","TypeOrmJobRepository","TypeOrmTransactionManager","batchMetaEntities"],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,iEAAiE;AACjE,yDAAyD;AACzD,uDAAuD;AACvD,8DAA8D;AAC9D,8DAA8D;AAC9D,kEAAkE;AAClE,wDAAwD;AACxD,6DAA6D;AAC7D,kCAAkC;AAClC,EAAE;AACF,qEAAqE;AACrE,8CAA8C;AAC9C,EAAE;AACF,0EAA0E;AAC1E,0DAA0D;AAC1D,8DAA8D;AAC9D,EAAE;AACF,+BAA+B;AAC/B,sEAAsE;AACtE,uDAAuD;AACvD,6DAA6D;AAC7D,EAAE;AACF,8BAA8B;AAC9B,kBAAkB;AAClB,gDAAgD;AAChD,+CAA+C;AAC/C,SAAS;AACT,QAAQ;AACR,EAAE;AACF,mEAAmE;AACnE,oEAAoE;AACpE,+DAA+D;AAC/D,+CAA+C;AAC/C,mEAAmE;;;;;;;;;;;;QAO1DA;eAAAA,6BAAmB;;QAJnBC;eAAAA,0CAAoB;;QAEpBC;eAAAA,oDAAyB;;QAKrBC;eAAAA;;;0BATuB;sCAEC;2CAEK;qBAC5B;qBAEA;;;;;;;;;;;;;;AAEP,MAAMA,oBAAoB,IAAkCH,6BAAmB"}
@@ -8,8 +8,8 @@ import type { JobInstance, JobExecution, JobExecutionPatch, JobParameters, StepE
8
8
  * provided by the `@nest-batch/postgresql` (or future
9
9
  * `@nest-batch/mysql`) driver sibling via the `TypeOrmDriverProvider`
10
10
  * token. The repository itself uses raw SQL via `EntityManager.query`
11
- * so the column-shape contract is owned by the driver sibling
12
- * (the bundled 6-table migration).
11
+ * against the table contract represented by this package's exported
12
+ * TypeORM entities. The consuming app owns the runnable migration.
13
13
  *
14
14
  * The contract guarantees:
15
15
  * - `getOrCreateJobInstance` is race-safe via the (jobName, jobKey)
@@ -1 +1 @@
1
- {"version":3,"file":"typeorm-job-repository.d.ts","sourceRoot":"","sources":["../../../src/repository/typeorm-job-repository.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAqB,MAAM,SAAS,CAAC;AACxD,OAAO,EACL,aAAa,EAOd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,kBAAkB,CAAC;AAyG1B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBACa,oBAAqB,SAAQ,aAAa;IAEpB,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,UAAU;IAKxE,OAAO,CAAC,EAAE;IAIJ,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAsC1E,kBAAkB,CACtB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,YAAY,CAAC;IAoBlB,qBAAqB,CACzB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,YAAY,CAAC;IA6DlB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBhF,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IASzD,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAWlE,gBAAgB,CAAC,MAAM,GAAE,iBAAsB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAsBxE,iBAAiB,CAAC,MAAM,GAAE,kBAAuB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAgCpF,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAY3E,mBAAmB,CACvB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,CAAC;IAWnB,mBAAmB,CACvB,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,IAAI,CAAC;IAoBV,gBAAgB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAS/D,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAWnF;;;;;;;;OAQG;IACG,uBAAuB,CAC3B,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAY1B,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA4BrE,oBAAoB,CACxB,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,gBAAgB,EACrB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;CA0CjB"}
1
+ {"version":3,"file":"typeorm-job-repository.d.ts","sourceRoot":"","sources":["../../../src/repository/typeorm-job-repository.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAqB,MAAM,SAAS,CAAC;AACxD,OAAO,EACL,aAAa,EAOd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,kBAAkB,CAAC;AA6G1B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBACa,oBAAqB,SAAQ,aAAa;IACV,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,UAAU;IAIlF,OAAO,CAAC,EAAE;IAIJ,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAsC1E,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAoBvF,qBAAqB,CACzB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,YAAY,CAAC;IA6DlB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgChF,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IASzD,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAWlE,gBAAgB,CAAC,MAAM,GAAE,iBAAsB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAsBxE,iBAAiB,CAAC,MAAM,GAAE,kBAAuB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAgCpF,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAY3E,mBAAmB,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAWrF,mBAAmB,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IA4CtF,gBAAgB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAS/D,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAWnF;;;;;;;;OAQG;IACG,uBAAuB,CAC3B,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAY1B,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA4BrE,oBAAoB,CACxB,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,gBAAgB,EACrB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;CA4CjB"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/repository/typeorm-job-repository.ts"],"sourcesContent":["import { Inject, Injectable } from '@nestjs/common';\nimport { randomUUID } from 'node:crypto';\nimport { DataSource, EntityManager, In } from 'typeorm';\nimport {\n JobRepository,\n JobExecutionAlreadyRunningError,\n assertJsonSerializable,\n serializeContext,\n deserializeContext,\n JobStatus,\n StepStatus,\n} from '@nest-batch/core';\nimport type {\n JobInstance,\n JobExecution,\n JobExecutionPatch,\n JobParameters,\n StepExecution,\n StepExecutionPatch,\n ExecutionContext,\n ExecutionScope,\n JobInstanceFilter,\n JobExecutionFilter,\n} from '@nest-batch/core';\nimport { TypeOrmDriverProvider } from '../typeorm.driver-provider';\n\nfunction scopeKey(scope: ExecutionScope): string {\n if ('jobExecutionId' in scope) return `job::${scope.jobExecutionId}`;\n return `step::${scope.stepExecutionId}`;\n}\n\nfunction deepClone<T>(value: T): T {\n if (value === null || typeof value !== 'object') return value;\n if (value instanceof Date) return new Date(value.getTime()) as unknown as T;\n if (Array.isArray(value)) return value.map((v) => deepClone(v)) as unknown as T;\n const out: Record<string, unknown> = {};\n for (const k of Object.keys(value as Record<string, unknown>)) {\n out[k] = deepClone((value as Record<string, unknown>)[k]);\n }\n return out as T;\n}\n\ninterface JobInstanceRow {\n id: string;\n job_name: string;\n job_key: string;\n created_at: string | Date;\n}\n\ninterface JobExecutionRow {\n id: string;\n job_instance_id: string;\n status: string;\n start_time: string | Date | null;\n end_time: string | Date | null;\n exit_code: string;\n exit_message: string;\n params: string;\n}\n\ninterface StepExecutionRow {\n id: string;\n job_execution_id: string;\n step_name: string;\n status: string;\n read_count: number;\n write_count: number;\n skip_count: number;\n rollback_count: number;\n commit_count: number;\n exit_code: string;\n exit_message: string;\n created_at: string | Date;\n}\n\ninterface ContextRow {\n data: string;\n version: number;\n}\n\nfunction mapJobInstance(r: JobInstanceRow): JobInstance {\n return {\n id: r.id,\n jobName: r.job_name,\n jobKey: r.job_key,\n createdAt: r.created_at instanceof Date ? r.created_at : new Date(r.created_at),\n };\n}\n\nfunction mapJobExecution(r: JobExecutionRow): JobExecution {\n let params: JobParameters = {};\n if (r.params && r.params.length > 0) {\n try {\n params = deserializeContext<JobParameters>(r.params);\n } catch {\n params = {};\n }\n }\n return {\n id: r.id,\n jobInstanceId: r.job_instance_id,\n status: r.status as JobStatus,\n startTime: r.start_time ? (r.start_time instanceof Date ? r.start_time : new Date(r.start_time)) : null,\n endTime: r.end_time ? (r.end_time instanceof Date ? r.end_time : new Date(r.end_time)) : null,\n exitCode: r.exit_code,\n exitMessage: r.exit_message,\n params,\n };\n}\n\nfunction mapStepExecution(r: StepExecutionRow): StepExecution {\n return {\n id: r.id,\n jobExecutionId: r.job_execution_id,\n stepName: r.step_name,\n status: r.status as StepStatus,\n readCount: r.read_count,\n writeCount: r.write_count,\n skipCount: r.skip_count,\n rollbackCount: r.rollback_count,\n commitCount: r.commit_count,\n startTime: null,\n endTime: null,\n exitCode: r.exit_code,\n exitMessage: r.exit_message,\n };\n}\n\n/**\n * TypeORM 1.0.0-backed `JobRepository`.\n *\n * The package is driver-agnostic: the actual `DataSource` is\n * provided by the `@nest-batch/postgresql` (or future\n * `@nest-batch/mysql`) driver sibling via the `TypeOrmDriverProvider`\n * token. The repository itself uses raw SQL via `EntityManager.query`\n * so the column-shape contract is owned by the driver sibling\n * (the bundled 6-table migration).\n *\n * The contract guarantees:\n * - `getOrCreateJobInstance` is race-safe via the (jobName, jobKey)\n * unique index.\n * - `createExecutionAtomic` runs inside a single transaction that\n * (a) idempotently upserts the instance row, (b) acquires a row\n * lock with `SELECT ... FOR UPDATE SKIP LOCKED` (PostgreSQL) or\n * a plain select (SQLite test driver), and (c) rejects with\n * `JobExecutionAlreadyRunningError` if a STARTING/STARTED\n * execution already exists.\n * - `saveExecutionContext` deep-clones the data and auto-increments\n * the version counter when `version` is omitted.\n * - `findLatestStepExecution` orders by `created_at` descending.\n */\n@Injectable()\nexport class TypeOrmJobRepository extends JobRepository {\n constructor(\n @Inject(TypeOrmDriverProvider) private readonly dataSource: DataSource,\n ) {\n super();\n }\n\n private em(): EntityManager {\n return this.dataSource.manager;\n }\n\n async getOrCreateJobInstance(name: string, jobKey: string): Promise<JobInstance> {\n const existing = await this.em().query(\n `SELECT \"id\", \"job_name\", \"job_key\", \"created_at\"\n FROM \"batch_job_instance\"\n WHERE \"job_name\" = $1 AND \"job_key\" = $2\n LIMIT 1`,\n [name, jobKey],\n ) as JobInstanceRow[];\n if (existing.length > 0) return mapJobInstance(existing[0]!);\n\n const id = randomUUID();\n try {\n const inserted = await this.em().query(\n `INSERT INTO \"batch_job_instance\" (\"id\", \"job_name\", \"job_key\", \"created_at\")\n VALUES ($1, $2, $3, NOW())\n ON CONFLICT (\"job_name\", \"job_key\") DO NOTHING\n RETURNING \"id\", \"job_name\", \"job_key\", \"created_at\"`,\n [id, name, jobKey],\n ) as JobInstanceRow[];\n if (inserted.length > 0) return mapJobInstance(inserted[0]!);\n } catch {\n // Fall through to read-back.\n }\n const winner = await this.em().query(\n `SELECT \"id\", \"job_name\", \"job_key\", \"created_at\"\n FROM \"batch_job_instance\"\n WHERE \"job_name\" = $1 AND \"job_key\" = $2\n LIMIT 1`,\n [name, jobKey],\n ) as JobInstanceRow[];\n if (winner.length === 0) {\n throw new Error(\n `Failed to upsert JobInstance (${name}, ${jobKey}) and could not read it back`,\n );\n }\n return mapJobInstance(winner[0]!);\n }\n\n async createJobExecution(\n jobInstanceId: string,\n params: JobParameters,\n ): Promise<JobExecution> {\n const exec = {\n id: randomUUID(),\n job_instance_id: jobInstanceId,\n status: JobStatus.STARTING,\n start_time: null as Date | null,\n end_time: null as Date | null,\n exit_code: '',\n exit_message: '',\n params: serializeContext(deepClone(params)),\n };\n const rows = await this.em().query(\n `INSERT INTO \"batch_job_execution\" (\"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\")\n VALUES ($1, $2, $3, NULL, NULL, $4, $5, $6)\n RETURNING \"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\"`,\n [exec.id, exec.job_instance_id, exec.status, exec.exit_code, exec.exit_message, exec.params],\n ) as JobExecutionRow[];\n return mapJobExecution(rows[0]!);\n }\n\n async createExecutionAtomic(\n name: string,\n jobKey: string,\n params: JobParameters,\n ): Promise<JobExecution> {\n return this.dataSource.transaction(async (em) => {\n // 1. Idempotent INSERT.\n const instId = randomUUID();\n await em.query(\n `INSERT INTO \"batch_job_instance\" (\"id\", \"job_name\", \"job_key\", \"created_at\")\n VALUES ($1, $2, $3, NOW())\n ON CONFLICT (\"job_name\", \"job_key\") DO NOTHING`,\n [instId, name, jobKey],\n );\n\n // 2. Lock the instance row.\n const isSqlite = this.dataSource.options.type === 'better-sqlite3';\n let instanceId: string;\n if (isSqlite) {\n const rows = await em.query(\n `SELECT \"id\" FROM \"batch_job_instance\"\n WHERE \"job_name\" = $1 AND \"job_key\" = $2\n LIMIT 1`,\n [name, jobKey],\n ) as Array<{ id: string }>;\n if (rows.length === 0) {\n throw new JobExecutionAlreadyRunningError(name);\n }\n instanceId = rows[0]!.id;\n } else {\n const rows = await em.query(\n `SELECT \"id\" FROM \"batch_job_instance\"\n WHERE \"job_name\" = $1 AND \"job_key\" = $2\n FOR UPDATE SKIP LOCKED`,\n [name, jobKey],\n ) as Array<{ id: string }>;\n if (rows.length === 0) {\n throw new JobExecutionAlreadyRunningError(name);\n }\n instanceId = rows[0]!.id;\n }\n\n // 3. Under the lock, verify no running execution.\n const running = await em.query(\n `SELECT \"id\" FROM \"batch_job_execution\"\n WHERE \"job_instance_id\" = $1 AND \"status\" IN ($2, $3)\n LIMIT 1`,\n [instanceId, JobStatus.STARTING, JobStatus.STARTED],\n ) as Array<{ id: string }>;\n if (running.length > 0) {\n throw new JobExecutionAlreadyRunningError(running[0]!.id);\n }\n\n // 4. Create the new execution row.\n const execId = randomUUID();\n const inserted = await em.query(\n `INSERT INTO \"batch_job_execution\" (\"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\")\n VALUES ($1, $2, $3, NULL, NULL, '', '', $4)\n RETURNING \"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\"`,\n [execId, instanceId, JobStatus.STARTING, serializeContext(deepClone(params))],\n ) as JobExecutionRow[];\n return mapJobExecution(inserted[0]!);\n });\n }\n\n async updateJobExecution(executionId: string, patch: JobExecutionPatch): Promise<void> {\n const sets: string[] = [];\n const values: unknown[] = [];\n let i = 1;\n if (patch.status !== undefined) { sets.push(`\"status\" = $${i++}`); values.push(patch.status); }\n if (patch.startTime !== undefined) { sets.push(`\"start_time\" = $${i++}`); values.push(patch.startTime); }\n if (patch.endTime !== undefined) { sets.push(`\"end_time\" = $${i++}`); values.push(patch.endTime); }\n if (patch.exitCode !== undefined) { sets.push(`\"exit_code\" = $${i++}`); values.push(patch.exitCode); }\n if (patch.exitMessage !== undefined) { sets.push(`\"exit_message\" = $${i++}`); values.push(patch.exitMessage); }\n if (sets.length === 0) return;\n values.push(executionId);\n await this.em().query(\n `UPDATE \"batch_job_execution\" SET ${sets.join(', ')} WHERE \"id\" = $${i}`,\n values,\n );\n }\n\n async getJobExecution(executionId: string): Promise<JobExecution | null> {\n const rows = await this.em().query(\n `SELECT \"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\"\n FROM \"batch_job_execution\" WHERE \"id\" = $1 LIMIT 1`,\n [executionId],\n ) as JobExecutionRow[];\n return rows.length > 0 ? mapJobExecution(rows[0]!) : null;\n }\n\n override async getJobInstance(jobInstanceId: string): Promise<JobInstance | null> {\n const rows = await this.em().query(\n `SELECT \"id\", \"job_name\", \"job_key\", \"created_at\"\n FROM \"batch_job_instance\"\n WHERE \"id\" = $1\n LIMIT 1`,\n [jobInstanceId],\n ) as JobInstanceRow[];\n return rows.length > 0 ? mapJobInstance(rows[0]!) : null;\n }\n\n override async findJobInstances(filter: JobInstanceFilter = {}): Promise<JobInstance[]> {\n const where: string[] = [];\n const values: unknown[] = [];\n let i = 1;\n if (filter.jobName !== undefined) {\n where.push(`\"job_name\" = $${i++}`);\n values.push(filter.jobName);\n }\n if (filter.jobKey !== undefined) {\n where.push(`\"job_key\" = $${i++}`);\n values.push(filter.jobKey);\n }\n const rows = await this.em().query(\n `SELECT \"id\", \"job_name\", \"job_key\", \"created_at\"\n FROM \"batch_job_instance\"\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY \"created_at\" ASC, \"id\" ASC`,\n values,\n ) as JobInstanceRow[];\n return rows.map(mapJobInstance);\n }\n\n override async findJobExecutions(filter: JobExecutionFilter = {}): Promise<JobExecution[]> {\n const where: string[] = [];\n const values: unknown[] = [];\n let i = 1;\n if (filter.jobInstanceId !== undefined) {\n where.push(`\"job_instance_id\" = $${i++}`);\n values.push(filter.jobInstanceId);\n }\n if (filter.status !== undefined) {\n const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];\n const placeholders = statuses.map(() => `$${i++}`);\n where.push(`\"status\" IN (${placeholders.join(', ')})`);\n values.push(...statuses);\n }\n if (filter.startedAfter !== undefined) {\n where.push(`\"start_time\" >= $${i++}`);\n values.push(filter.startedAfter);\n }\n if (filter.startedBefore !== undefined) {\n where.push(`\"start_time\" <= $${i++}`);\n values.push(filter.startedBefore);\n }\n const rows = await this.em().query(\n `SELECT \"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\"\n FROM \"batch_job_execution\"\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY \"start_time\" DESC NULLS LAST, \"id\" DESC`,\n values,\n ) as JobExecutionRow[];\n return rows.map(mapJobExecution);\n }\n\n async getRunningJobExecution(jobInstanceId: string): Promise<JobExecution | null> {\n if (!jobInstanceId) return null;\n const rows = await this.em().query(\n `SELECT \"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\"\n FROM \"batch_job_execution\"\n WHERE \"job_instance_id\" = $1 AND \"status\" IN ($2, $3)\n ORDER BY \"start_time\" DESC NULLS LAST LIMIT 1`,\n [jobInstanceId, JobStatus.STARTING, JobStatus.STARTED],\n ) as JobExecutionRow[];\n return rows.length > 0 ? mapJobExecution(rows[0]!) : null;\n }\n\n async createStepExecution(\n jobExecutionId: string,\n stepName: string,\n ): Promise<StepExecution> {\n const stepId = randomUUID();\n const rows = await this.em().query(\n `INSERT INTO \"batch_step_execution\" (\"id\", \"job_execution_id\", \"step_name\", \"status\", \"read_count\", \"write_count\", \"skip_count\", \"rollback_count\", \"commit_count\", \"exit_code\", \"exit_message\", \"created_at\")\n VALUES ($1, $2, $3, $4, 0, 0, 0, 0, 0, '', '', NOW())\n RETURNING \"id\", \"job_execution_id\", \"step_name\", \"status\", \"read_count\", \"write_count\", \"skip_count\", \"rollback_count\", \"commit_count\", \"exit_code\", \"exit_message\", \"created_at\"`,\n [stepId, jobExecutionId, stepName, StepStatus.STARTING],\n ) as StepExecutionRow[];\n return mapStepExecution(rows[0]!);\n }\n\n async updateStepExecution(\n stepExecutionId: string,\n patch: StepExecutionPatch,\n ): Promise<void> {\n const sets: string[] = [];\n const values: unknown[] = [];\n let i = 1;\n if (patch.status !== undefined) { sets.push(`\"status\" = $${i++}`); values.push(patch.status); }\n if (patch.readCount !== undefined) { sets.push(`\"read_count\" = $${i++}`); values.push(patch.readCount); }\n if (patch.writeCount !== undefined) { sets.push(`\"write_count\" = $${i++}`); values.push(patch.writeCount); }\n if (patch.skipCount !== undefined) { sets.push(`\"skip_count\" = $${i++}`); values.push(patch.skipCount); }\n if (patch.rollbackCount !== undefined) { sets.push(`\"rollback_count\" = $${i++}`); values.push(patch.rollbackCount); }\n if (patch.commitCount !== undefined) { sets.push(`\"commit_count\" = $${i++}`); values.push(patch.commitCount); }\n if (patch.exitCode !== undefined) { sets.push(`\"exit_code\" = $${i++}`); values.push(patch.exitCode); }\n if (patch.exitMessage !== undefined) { sets.push(`\"exit_message\" = $${i++}`); values.push(patch.exitMessage); }\n if (sets.length === 0) return;\n values.push(stepExecutionId);\n await this.em().query(\n `UPDATE \"batch_step_execution\" SET ${sets.join(', ')} WHERE \"id\" = $${i}`,\n values,\n );\n }\n\n async getStepExecution(stepExecutionId: string): Promise<StepExecution | null> {\n const rows = await this.em().query(\n `SELECT \"id\", \"job_execution_id\", \"step_name\", \"status\", \"read_count\", \"write_count\", \"skip_count\", \"rollback_count\", \"commit_count\", \"exit_code\", \"exit_message\", \"created_at\"\n FROM \"batch_step_execution\" WHERE \"id\" = $1 LIMIT 1`,\n [stepExecutionId],\n ) as StepExecutionRow[];\n return rows.length > 0 ? mapStepExecution(rows[0]!) : null;\n }\n\n override async findStepExecutions(jobExecutionId: string): Promise<StepExecution[]> {\n const rows = await this.em().query(\n `SELECT \"id\", \"job_execution_id\", \"step_name\", \"status\", \"read_count\", \"write_count\", \"skip_count\", \"rollback_count\", \"commit_count\", \"exit_code\", \"exit_message\", \"created_at\"\n FROM \"batch_step_execution\"\n WHERE \"job_execution_id\" = $1\n ORDER BY \"created_at\" ASC, \"id\" ASC`,\n [jobExecutionId],\n ) as StepExecutionRow[];\n return rows.map(mapStepExecution);\n }\n\n /**\n * Find the most recently created step execution for the given\n * `(jobExecutionId, stepName)` pair, or `null` when none exists.\n * Insertion order is determined by the `created_at` column; the\n * primary key is a v4 UUID which is random, so a `id DESC` order\n * would not correspond to insertion time. The `created_at DESC,\n * id DESC` secondary order keeps the result stable when two rows\n * share the same `CURRENT_TIMESTAMP` resolution.\n */\n async findLatestStepExecution(\n jobExecutionId: string,\n stepName: string,\n ): Promise<StepExecution | null> {\n const rows = await this.em().query(\n `SELECT \"id\", \"job_execution_id\", \"step_name\", \"status\", \"read_count\", \"write_count\", \"skip_count\", \"rollback_count\", \"commit_count\", \"exit_code\", \"exit_message\", \"created_at\"\n FROM \"batch_step_execution\"\n WHERE \"job_execution_id\" = $1 AND \"step_name\" = $2\n ORDER BY \"created_at\" DESC, \"id\" DESC\n LIMIT 1`,\n [jobExecutionId, stepName],\n ) as StepExecutionRow[];\n return rows.length > 0 ? mapStepExecution(rows[0]!) : null;\n }\n\n async getExecutionContext(scope: ExecutionScope): Promise<ExecutionContext> {\n const key = scopeKey(scope);\n if (key.startsWith('job::')) {\n const rows = await this.em().query(\n `SELECT \"data\", \"version\" FROM \"batch_job_execution_context\" WHERE \"job_execution_id\" = $1 LIMIT 1`,\n [key.slice(5)],\n ) as ContextRow[];\n if (rows.length > 0) {\n return {\n data: rows[0]!.data.length > 0 ? deserializeContext(rows[0]!.data) : null,\n version: rows[0]!.version,\n };\n }\n } else {\n const rows = await this.em().query(\n `SELECT \"data\", \"version\" FROM \"batch_step_execution_context\" WHERE \"step_execution_id\" = $1 LIMIT 1`,\n [key.slice(6)],\n ) as ContextRow[];\n if (rows.length > 0) {\n return {\n data: rows[0]!.data.length > 0 ? deserializeContext(rows[0]!.data) : null,\n version: rows[0]!.version,\n };\n }\n }\n return { data: null, version: 0 };\n }\n\n async saveExecutionContext(\n scope: ExecutionScope,\n ctx: ExecutionContext,\n version?: number,\n ): Promise<void> {\n assertJsonSerializable(ctx.data);\n const key = scopeKey(scope);\n const serialized = serializeContext(deepClone(ctx.data));\n if (key.startsWith('job::')) {\n const jobExecutionId = key.slice(5);\n const existing = await this.em().query(\n `SELECT \"version\" FROM \"batch_job_execution_context\" WHERE \"job_execution_id\" = $1 LIMIT 1`,\n [jobExecutionId],\n ) as ContextRow[];\n const nextVersion = version !== undefined ? version : (existing.length > 0 ? existing[0]!.version + 1 : 0);\n if (existing.length > 0) {\n await this.em().query(\n `UPDATE \"batch_job_execution_context\" SET \"data\" = $1, \"version\" = $2 WHERE \"job_execution_id\" = $3`,\n [serialized, nextVersion, jobExecutionId],\n );\n } else {\n await this.em().query(\n `INSERT INTO \"batch_job_execution_context\" (\"job_execution_id\", \"data\", \"version\") VALUES ($1, $2, $3)`,\n [jobExecutionId, serialized, nextVersion],\n );\n }\n } else {\n const stepExecutionId = key.slice(6);\n const existing = await this.em().query(\n `SELECT \"version\" FROM \"batch_step_execution_context\" WHERE \"step_execution_id\" = $1 LIMIT 1`,\n [stepExecutionId],\n ) as ContextRow[];\n const nextVersion = version !== undefined ? version : (existing.length > 0 ? existing[0]!.version + 1 : 0);\n if (existing.length > 0) {\n await this.em().query(\n `UPDATE \"batch_step_execution_context\" SET \"data\" = $1, \"version\" = $2 WHERE \"step_execution_id\" = $3`,\n [serialized, nextVersion, stepExecutionId],\n );\n } else {\n await this.em().query(\n `INSERT INTO \"batch_step_execution_context\" (\"step_execution_id\", \"data\", \"version\") VALUES ($1, $2, $3)`,\n [stepExecutionId, serialized, nextVersion],\n );\n }\n }\n }\n}\n"],"names":["TypeOrmJobRepository","scopeKey","scope","jobExecutionId","stepExecutionId","deepClone","value","Date","getTime","Array","isArray","map","v","out","k","Object","keys","mapJobInstance","r","id","jobName","job_name","jobKey","job_key","createdAt","created_at","mapJobExecution","params","length","deserializeContext","jobInstanceId","job_instance_id","status","startTime","start_time","endTime","end_time","exitCode","exit_code","exitMessage","exit_message","mapStepExecution","job_execution_id","stepName","step_name","readCount","read_count","writeCount","write_count","skipCount","skip_count","rollbackCount","rollback_count","commitCount","commit_count","JobRepository","dataSource","em","manager","getOrCreateJobInstance","name","existing","query","randomUUID","inserted","winner","Error","createJobExecution","exec","JobStatus","STARTING","serializeContext","rows","createExecutionAtomic","transaction","instId","isSqlite","options","type","instanceId","JobExecutionAlreadyRunningError","running","STARTED","execId","updateJobExecution","executionId","patch","sets","values","i","undefined","push","join","getJobExecution","getJobInstance","findJobInstances","filter","where","findJobExecutions","statuses","placeholders","startedAfter","startedBefore","getRunningJobExecution","createStepExecution","stepId","StepStatus","updateStepExecution","getStepExecution","findStepExecutions","findLatestStepExecution","getExecutionContext","key","startsWith","slice","data","version","saveExecutionContext","ctx","assertJsonSerializable","serialized","nextVersion"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/repository/typeorm-job-repository.ts"],"sourcesContent":["import { Inject, Injectable } from '@nestjs/common';\nimport { randomUUID } from 'node:crypto';\nimport { DataSource, EntityManager, In } from 'typeorm';\nimport {\n JobRepository,\n JobExecutionAlreadyRunningError,\n assertJsonSerializable,\n serializeContext,\n deserializeContext,\n JobStatus,\n StepStatus,\n} from '@nest-batch/core';\nimport type {\n JobInstance,\n JobExecution,\n JobExecutionPatch,\n JobParameters,\n StepExecution,\n StepExecutionPatch,\n ExecutionContext,\n ExecutionScope,\n JobInstanceFilter,\n JobExecutionFilter,\n} from '@nest-batch/core';\nimport { TypeOrmDriverProvider } from '../typeorm.driver-provider';\n\nfunction scopeKey(scope: ExecutionScope): string {\n if ('jobExecutionId' in scope) return `job::${scope.jobExecutionId}`;\n return `step::${scope.stepExecutionId}`;\n}\n\nfunction deepClone<T>(value: T): T {\n if (value === null || typeof value !== 'object') return value;\n if (value instanceof Date) return new Date(value.getTime()) as unknown as T;\n if (Array.isArray(value)) return value.map((v) => deepClone(v)) as unknown as T;\n const out: Record<string, unknown> = {};\n for (const k of Object.keys(value as Record<string, unknown>)) {\n out[k] = deepClone((value as Record<string, unknown>)[k]);\n }\n return out as T;\n}\n\ninterface JobInstanceRow {\n id: string;\n job_name: string;\n job_key: string;\n created_at: string | Date;\n}\n\ninterface JobExecutionRow {\n id: string;\n job_instance_id: string;\n status: string;\n start_time: string | Date | null;\n end_time: string | Date | null;\n exit_code: string;\n exit_message: string;\n params: string;\n}\n\ninterface StepExecutionRow {\n id: string;\n job_execution_id: string;\n step_name: string;\n status: string;\n read_count: number;\n write_count: number;\n skip_count: number;\n rollback_count: number;\n commit_count: number;\n exit_code: string;\n exit_message: string;\n created_at: string | Date;\n}\n\ninterface ContextRow {\n data: string;\n version: number;\n}\n\nfunction mapJobInstance(r: JobInstanceRow): JobInstance {\n return {\n id: r.id,\n jobName: r.job_name,\n jobKey: r.job_key,\n createdAt: r.created_at instanceof Date ? r.created_at : new Date(r.created_at),\n };\n}\n\nfunction mapJobExecution(r: JobExecutionRow): JobExecution {\n let params: JobParameters = {};\n if (r.params && r.params.length > 0) {\n try {\n params = deserializeContext<JobParameters>(r.params);\n } catch {\n params = {};\n }\n }\n return {\n id: r.id,\n jobInstanceId: r.job_instance_id,\n status: r.status as JobStatus,\n startTime: r.start_time\n ? r.start_time instanceof Date\n ? r.start_time\n : new Date(r.start_time)\n : null,\n endTime: r.end_time ? (r.end_time instanceof Date ? r.end_time : new Date(r.end_time)) : null,\n exitCode: r.exit_code,\n exitMessage: r.exit_message,\n params,\n };\n}\n\nfunction mapStepExecution(r: StepExecutionRow): StepExecution {\n return {\n id: r.id,\n jobExecutionId: r.job_execution_id,\n stepName: r.step_name,\n status: r.status as StepStatus,\n readCount: r.read_count,\n writeCount: r.write_count,\n skipCount: r.skip_count,\n rollbackCount: r.rollback_count,\n commitCount: r.commit_count,\n startTime: null,\n endTime: null,\n exitCode: r.exit_code,\n exitMessage: r.exit_message,\n };\n}\n\n/**\n * TypeORM 1.0.0-backed `JobRepository`.\n *\n * The package is driver-agnostic: the actual `DataSource` is\n * provided by the `@nest-batch/postgresql` (or future\n * `@nest-batch/mysql`) driver sibling via the `TypeOrmDriverProvider`\n * token. The repository itself uses raw SQL via `EntityManager.query`\n * against the table contract represented by this package's exported\n * TypeORM entities. The consuming app owns the runnable migration.\n *\n * The contract guarantees:\n * - `getOrCreateJobInstance` is race-safe via the (jobName, jobKey)\n * unique index.\n * - `createExecutionAtomic` runs inside a single transaction that\n * (a) idempotently upserts the instance row, (b) acquires a row\n * lock with `SELECT ... FOR UPDATE SKIP LOCKED` (PostgreSQL) or\n * a plain select (SQLite test driver), and (c) rejects with\n * `JobExecutionAlreadyRunningError` if a STARTING/STARTED\n * execution already exists.\n * - `saveExecutionContext` deep-clones the data and auto-increments\n * the version counter when `version` is omitted.\n * - `findLatestStepExecution` orders by `created_at` descending.\n */\n@Injectable()\nexport class TypeOrmJobRepository extends JobRepository {\n constructor(@Inject(TypeOrmDriverProvider) private readonly dataSource: DataSource) {\n super();\n }\n\n private em(): EntityManager {\n return this.dataSource.manager;\n }\n\n async getOrCreateJobInstance(name: string, jobKey: string): Promise<JobInstance> {\n const existing = (await this.em().query(\n `SELECT \"id\", \"job_name\", \"job_key\", \"created_at\"\n FROM \"batch_job_instance\"\n WHERE \"job_name\" = $1 AND \"job_key\" = $2\n LIMIT 1`,\n [name, jobKey],\n )) as JobInstanceRow[];\n if (existing.length > 0) return mapJobInstance(existing[0]!);\n\n const id = randomUUID();\n try {\n const inserted = (await this.em().query(\n `INSERT INTO \"batch_job_instance\" (\"id\", \"job_name\", \"job_key\", \"created_at\")\n VALUES ($1, $2, $3, NOW())\n ON CONFLICT (\"job_name\", \"job_key\") DO NOTHING\n RETURNING \"id\", \"job_name\", \"job_key\", \"created_at\"`,\n [id, name, jobKey],\n )) as JobInstanceRow[];\n if (inserted.length > 0) return mapJobInstance(inserted[0]!);\n } catch {\n // Fall through to read-back.\n }\n const winner = (await this.em().query(\n `SELECT \"id\", \"job_name\", \"job_key\", \"created_at\"\n FROM \"batch_job_instance\"\n WHERE \"job_name\" = $1 AND \"job_key\" = $2\n LIMIT 1`,\n [name, jobKey],\n )) as JobInstanceRow[];\n if (winner.length === 0) {\n throw new Error(\n `Failed to upsert JobInstance (${name}, ${jobKey}) and could not read it back`,\n );\n }\n return mapJobInstance(winner[0]!);\n }\n\n async createJobExecution(jobInstanceId: string, params: JobParameters): Promise<JobExecution> {\n const exec = {\n id: randomUUID(),\n job_instance_id: jobInstanceId,\n status: JobStatus.STARTING,\n start_time: null as Date | null,\n end_time: null as Date | null,\n exit_code: '',\n exit_message: '',\n params: serializeContext(deepClone(params)),\n };\n const rows = (await this.em().query(\n `INSERT INTO \"batch_job_execution\" (\"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\")\n VALUES ($1, $2, $3, NULL, NULL, $4, $5, $6)\n RETURNING \"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\"`,\n [exec.id, exec.job_instance_id, exec.status, exec.exit_code, exec.exit_message, exec.params],\n )) as JobExecutionRow[];\n return mapJobExecution(rows[0]!);\n }\n\n async createExecutionAtomic(\n name: string,\n jobKey: string,\n params: JobParameters,\n ): Promise<JobExecution> {\n return this.dataSource.transaction(async (em) => {\n // 1. Idempotent INSERT.\n const instId = randomUUID();\n await em.query(\n `INSERT INTO \"batch_job_instance\" (\"id\", \"job_name\", \"job_key\", \"created_at\")\n VALUES ($1, $2, $3, NOW())\n ON CONFLICT (\"job_name\", \"job_key\") DO NOTHING`,\n [instId, name, jobKey],\n );\n\n // 2. Lock the instance row.\n const isSqlite = this.dataSource.options.type === 'better-sqlite3';\n let instanceId: string;\n if (isSqlite) {\n const rows = (await em.query(\n `SELECT \"id\" FROM \"batch_job_instance\"\n WHERE \"job_name\" = $1 AND \"job_key\" = $2\n LIMIT 1`,\n [name, jobKey],\n )) as Array<{ id: string }>;\n if (rows.length === 0) {\n throw new JobExecutionAlreadyRunningError(name);\n }\n instanceId = rows[0]!.id;\n } else {\n const rows = (await em.query(\n `SELECT \"id\" FROM \"batch_job_instance\"\n WHERE \"job_name\" = $1 AND \"job_key\" = $2\n FOR UPDATE SKIP LOCKED`,\n [name, jobKey],\n )) as Array<{ id: string }>;\n if (rows.length === 0) {\n throw new JobExecutionAlreadyRunningError(name);\n }\n instanceId = rows[0]!.id;\n }\n\n // 3. Under the lock, verify no running execution.\n const running = (await em.query(\n `SELECT \"id\" FROM \"batch_job_execution\"\n WHERE \"job_instance_id\" = $1 AND \"status\" IN ($2, $3)\n LIMIT 1`,\n [instanceId, JobStatus.STARTING, JobStatus.STARTED],\n )) as Array<{ id: string }>;\n if (running.length > 0) {\n throw new JobExecutionAlreadyRunningError(running[0]!.id);\n }\n\n // 4. Create the new execution row.\n const execId = randomUUID();\n const inserted = (await em.query(\n `INSERT INTO \"batch_job_execution\" (\"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\")\n VALUES ($1, $2, $3, NULL, NULL, '', '', $4)\n RETURNING \"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\"`,\n [execId, instanceId, JobStatus.STARTING, serializeContext(deepClone(params))],\n )) as JobExecutionRow[];\n return mapJobExecution(inserted[0]!);\n });\n }\n\n async updateJobExecution(executionId: string, patch: JobExecutionPatch): Promise<void> {\n const sets: string[] = [];\n const values: unknown[] = [];\n let i = 1;\n if (patch.status !== undefined) {\n sets.push(`\"status\" = $${i++}`);\n values.push(patch.status);\n }\n if (patch.startTime !== undefined) {\n sets.push(`\"start_time\" = $${i++}`);\n values.push(patch.startTime);\n }\n if (patch.endTime !== undefined) {\n sets.push(`\"end_time\" = $${i++}`);\n values.push(patch.endTime);\n }\n if (patch.exitCode !== undefined) {\n sets.push(`\"exit_code\" = $${i++}`);\n values.push(patch.exitCode);\n }\n if (patch.exitMessage !== undefined) {\n sets.push(`\"exit_message\" = $${i++}`);\n values.push(patch.exitMessage);\n }\n if (sets.length === 0) return;\n values.push(executionId);\n await this.em().query(\n `UPDATE \"batch_job_execution\" SET ${sets.join(', ')} WHERE \"id\" = $${i}`,\n values,\n );\n }\n\n async getJobExecution(executionId: string): Promise<JobExecution | null> {\n const rows = (await this.em().query(\n `SELECT \"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\"\n FROM \"batch_job_execution\" WHERE \"id\" = $1 LIMIT 1`,\n [executionId],\n )) as JobExecutionRow[];\n return rows.length > 0 ? mapJobExecution(rows[0]!) : null;\n }\n\n override async getJobInstance(jobInstanceId: string): Promise<JobInstance | null> {\n const rows = (await this.em().query(\n `SELECT \"id\", \"job_name\", \"job_key\", \"created_at\"\n FROM \"batch_job_instance\"\n WHERE \"id\" = $1\n LIMIT 1`,\n [jobInstanceId],\n )) as JobInstanceRow[];\n return rows.length > 0 ? mapJobInstance(rows[0]!) : null;\n }\n\n override async findJobInstances(filter: JobInstanceFilter = {}): Promise<JobInstance[]> {\n const where: string[] = [];\n const values: unknown[] = [];\n let i = 1;\n if (filter.jobName !== undefined) {\n where.push(`\"job_name\" = $${i++}`);\n values.push(filter.jobName);\n }\n if (filter.jobKey !== undefined) {\n where.push(`\"job_key\" = $${i++}`);\n values.push(filter.jobKey);\n }\n const rows = (await this.em().query(\n `SELECT \"id\", \"job_name\", \"job_key\", \"created_at\"\n FROM \"batch_job_instance\"\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY \"created_at\" ASC, \"id\" ASC`,\n values,\n )) as JobInstanceRow[];\n return rows.map(mapJobInstance);\n }\n\n override async findJobExecutions(filter: JobExecutionFilter = {}): Promise<JobExecution[]> {\n const where: string[] = [];\n const values: unknown[] = [];\n let i = 1;\n if (filter.jobInstanceId !== undefined) {\n where.push(`\"job_instance_id\" = $${i++}`);\n values.push(filter.jobInstanceId);\n }\n if (filter.status !== undefined) {\n const statuses = Array.isArray(filter.status) ? filter.status : [filter.status];\n const placeholders = statuses.map(() => `$${i++}`);\n where.push(`\"status\" IN (${placeholders.join(', ')})`);\n values.push(...statuses);\n }\n if (filter.startedAfter !== undefined) {\n where.push(`\"start_time\" >= $${i++}`);\n values.push(filter.startedAfter);\n }\n if (filter.startedBefore !== undefined) {\n where.push(`\"start_time\" <= $${i++}`);\n values.push(filter.startedBefore);\n }\n const rows = (await this.em().query(\n `SELECT \"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\"\n FROM \"batch_job_execution\"\n ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}\n ORDER BY \"start_time\" DESC NULLS LAST, \"id\" DESC`,\n values,\n )) as JobExecutionRow[];\n return rows.map(mapJobExecution);\n }\n\n async getRunningJobExecution(jobInstanceId: string): Promise<JobExecution | null> {\n if (!jobInstanceId) return null;\n const rows = (await this.em().query(\n `SELECT \"id\", \"job_instance_id\", \"status\", \"start_time\", \"end_time\", \"exit_code\", \"exit_message\", \"params\"\n FROM \"batch_job_execution\"\n WHERE \"job_instance_id\" = $1 AND \"status\" IN ($2, $3)\n ORDER BY \"start_time\" DESC NULLS LAST LIMIT 1`,\n [jobInstanceId, JobStatus.STARTING, JobStatus.STARTED],\n )) as JobExecutionRow[];\n return rows.length > 0 ? mapJobExecution(rows[0]!) : null;\n }\n\n async createStepExecution(jobExecutionId: string, stepName: string): Promise<StepExecution> {\n const stepId = randomUUID();\n const rows = (await this.em().query(\n `INSERT INTO \"batch_step_execution\" (\"id\", \"job_execution_id\", \"step_name\", \"status\", \"read_count\", \"write_count\", \"skip_count\", \"rollback_count\", \"commit_count\", \"exit_code\", \"exit_message\", \"created_at\")\n VALUES ($1, $2, $3, $4, 0, 0, 0, 0, 0, '', '', NOW())\n RETURNING \"id\", \"job_execution_id\", \"step_name\", \"status\", \"read_count\", \"write_count\", \"skip_count\", \"rollback_count\", \"commit_count\", \"exit_code\", \"exit_message\", \"created_at\"`,\n [stepId, jobExecutionId, stepName, StepStatus.STARTING],\n )) as StepExecutionRow[];\n return mapStepExecution(rows[0]!);\n }\n\n async updateStepExecution(stepExecutionId: string, patch: StepExecutionPatch): Promise<void> {\n const sets: string[] = [];\n const values: unknown[] = [];\n let i = 1;\n if (patch.status !== undefined) {\n sets.push(`\"status\" = $${i++}`);\n values.push(patch.status);\n }\n if (patch.readCount !== undefined) {\n sets.push(`\"read_count\" = $${i++}`);\n values.push(patch.readCount);\n }\n if (patch.writeCount !== undefined) {\n sets.push(`\"write_count\" = $${i++}`);\n values.push(patch.writeCount);\n }\n if (patch.skipCount !== undefined) {\n sets.push(`\"skip_count\" = $${i++}`);\n values.push(patch.skipCount);\n }\n if (patch.rollbackCount !== undefined) {\n sets.push(`\"rollback_count\" = $${i++}`);\n values.push(patch.rollbackCount);\n }\n if (patch.commitCount !== undefined) {\n sets.push(`\"commit_count\" = $${i++}`);\n values.push(patch.commitCount);\n }\n if (patch.exitCode !== undefined) {\n sets.push(`\"exit_code\" = $${i++}`);\n values.push(patch.exitCode);\n }\n if (patch.exitMessage !== undefined) {\n sets.push(`\"exit_message\" = $${i++}`);\n values.push(patch.exitMessage);\n }\n if (sets.length === 0) return;\n values.push(stepExecutionId);\n await this.em().query(\n `UPDATE \"batch_step_execution\" SET ${sets.join(', ')} WHERE \"id\" = $${i}`,\n values,\n );\n }\n\n async getStepExecution(stepExecutionId: string): Promise<StepExecution | null> {\n const rows = (await this.em().query(\n `SELECT \"id\", \"job_execution_id\", \"step_name\", \"status\", \"read_count\", \"write_count\", \"skip_count\", \"rollback_count\", \"commit_count\", \"exit_code\", \"exit_message\", \"created_at\"\n FROM \"batch_step_execution\" WHERE \"id\" = $1 LIMIT 1`,\n [stepExecutionId],\n )) as StepExecutionRow[];\n return rows.length > 0 ? mapStepExecution(rows[0]!) : null;\n }\n\n override async findStepExecutions(jobExecutionId: string): Promise<StepExecution[]> {\n const rows = (await this.em().query(\n `SELECT \"id\", \"job_execution_id\", \"step_name\", \"status\", \"read_count\", \"write_count\", \"skip_count\", \"rollback_count\", \"commit_count\", \"exit_code\", \"exit_message\", \"created_at\"\n FROM \"batch_step_execution\"\n WHERE \"job_execution_id\" = $1\n ORDER BY \"created_at\" ASC, \"id\" ASC`,\n [jobExecutionId],\n )) as StepExecutionRow[];\n return rows.map(mapStepExecution);\n }\n\n /**\n * Find the most recently created step execution for the given\n * `(jobExecutionId, stepName)` pair, or `null` when none exists.\n * Insertion order is determined by the `created_at` column; the\n * primary key is a v4 UUID which is random, so a `id DESC` order\n * would not correspond to insertion time. The `created_at DESC,\n * id DESC` secondary order keeps the result stable when two rows\n * share the same `CURRENT_TIMESTAMP` resolution.\n */\n async findLatestStepExecution(\n jobExecutionId: string,\n stepName: string,\n ): Promise<StepExecution | null> {\n const rows = (await this.em().query(\n `SELECT \"id\", \"job_execution_id\", \"step_name\", \"status\", \"read_count\", \"write_count\", \"skip_count\", \"rollback_count\", \"commit_count\", \"exit_code\", \"exit_message\", \"created_at\"\n FROM \"batch_step_execution\"\n WHERE \"job_execution_id\" = $1 AND \"step_name\" = $2\n ORDER BY \"created_at\" DESC, \"id\" DESC\n LIMIT 1`,\n [jobExecutionId, stepName],\n )) as StepExecutionRow[];\n return rows.length > 0 ? mapStepExecution(rows[0]!) : null;\n }\n\n async getExecutionContext(scope: ExecutionScope): Promise<ExecutionContext> {\n const key = scopeKey(scope);\n if (key.startsWith('job::')) {\n const rows = (await this.em().query(\n `SELECT \"data\", \"version\" FROM \"batch_job_execution_context\" WHERE \"job_execution_id\" = $1 LIMIT 1`,\n [key.slice(5)],\n )) as ContextRow[];\n if (rows.length > 0) {\n return {\n data: rows[0]!.data.length > 0 ? deserializeContext(rows[0]!.data) : null,\n version: rows[0]!.version,\n };\n }\n } else {\n const rows = (await this.em().query(\n `SELECT \"data\", \"version\" FROM \"batch_step_execution_context\" WHERE \"step_execution_id\" = $1 LIMIT 1`,\n [key.slice(6)],\n )) as ContextRow[];\n if (rows.length > 0) {\n return {\n data: rows[0]!.data.length > 0 ? deserializeContext(rows[0]!.data) : null,\n version: rows[0]!.version,\n };\n }\n }\n return { data: null, version: 0 };\n }\n\n async saveExecutionContext(\n scope: ExecutionScope,\n ctx: ExecutionContext,\n version?: number,\n ): Promise<void> {\n assertJsonSerializable(ctx.data);\n const key = scopeKey(scope);\n const serialized = serializeContext(deepClone(ctx.data));\n if (key.startsWith('job::')) {\n const jobExecutionId = key.slice(5);\n const existing = (await this.em().query(\n `SELECT \"version\" FROM \"batch_job_execution_context\" WHERE \"job_execution_id\" = $1 LIMIT 1`,\n [jobExecutionId],\n )) as ContextRow[];\n const nextVersion =\n version !== undefined ? version : existing.length > 0 ? existing[0]!.version + 1 : 0;\n if (existing.length > 0) {\n await this.em().query(\n `UPDATE \"batch_job_execution_context\" SET \"data\" = $1, \"version\" = $2 WHERE \"job_execution_id\" = $3`,\n [serialized, nextVersion, jobExecutionId],\n );\n } else {\n await this.em().query(\n `INSERT INTO \"batch_job_execution_context\" (\"job_execution_id\", \"data\", \"version\") VALUES ($1, $2, $3)`,\n [jobExecutionId, serialized, nextVersion],\n );\n }\n } else {\n const stepExecutionId = key.slice(6);\n const existing = (await this.em().query(\n `SELECT \"version\" FROM \"batch_step_execution_context\" WHERE \"step_execution_id\" = $1 LIMIT 1`,\n [stepExecutionId],\n )) as ContextRow[];\n const nextVersion =\n version !== undefined ? version : existing.length > 0 ? existing[0]!.version + 1 : 0;\n if (existing.length > 0) {\n await this.em().query(\n `UPDATE \"batch_step_execution_context\" SET \"data\" = $1, \"version\" = $2 WHERE \"step_execution_id\" = $3`,\n [serialized, nextVersion, stepExecutionId],\n );\n } else {\n await this.em().query(\n `INSERT INTO \"batch_step_execution_context\" (\"step_execution_id\", \"data\", \"version\") VALUES ($1, $2, $3)`,\n [stepExecutionId, serialized, nextVersion],\n );\n }\n }\n }\n}\n"],"names":["TypeOrmJobRepository","scopeKey","scope","jobExecutionId","stepExecutionId","deepClone","value","Date","getTime","Array","isArray","map","v","out","k","Object","keys","mapJobInstance","r","id","jobName","job_name","jobKey","job_key","createdAt","created_at","mapJobExecution","params","length","deserializeContext","jobInstanceId","job_instance_id","status","startTime","start_time","endTime","end_time","exitCode","exit_code","exitMessage","exit_message","mapStepExecution","job_execution_id","stepName","step_name","readCount","read_count","writeCount","write_count","skipCount","skip_count","rollbackCount","rollback_count","commitCount","commit_count","JobRepository","dataSource","em","manager","getOrCreateJobInstance","name","existing","query","randomUUID","inserted","winner","Error","createJobExecution","exec","JobStatus","STARTING","serializeContext","rows","createExecutionAtomic","transaction","instId","isSqlite","options","type","instanceId","JobExecutionAlreadyRunningError","running","STARTED","execId","updateJobExecution","executionId","patch","sets","values","i","undefined","push","join","getJobExecution","getJobInstance","findJobInstances","filter","where","findJobExecutions","statuses","placeholders","startedAfter","startedBefore","getRunningJobExecution","createStepExecution","stepId","StepStatus","updateStepExecution","getStepExecution","findStepExecutions","findLatestStepExecution","getExecutionContext","key","startsWith","slice","data","version","saveExecutionContext","ctx","assertJsonSerializable","serialized","nextVersion"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nest-batch/typeorm",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "TypeORM 1.0.0 adapter SLOT for @nest-batch/core — JobRepository and TransactionManager interface shape, paired with @nest-batch/postgresql (Postgres driver) or @nest-batch/mysql (MySQL driver).",
5
5
  "license": "MIT",
6
6
  "author": "easdkr",
@@ -33,7 +33,7 @@
33
33
  "peerDependencies": {
34
34
  "@nestjs/common": "^10 || ^11",
35
35
  "typeorm": "^1.0.0",
36
- "@nest-batch/core": "^0.2.0"
36
+ "@nest-batch/core": "^0.2.2"
37
37
  },
38
38
  "peerDependenciesMeta": {
39
39
  "typeorm": {
@@ -58,7 +58,7 @@
58
58
  "typescript": "^5.5.0",
59
59
  "unplugin-swc": "^1.5.0",
60
60
  "vitest": "^2.0.0",
61
- "@nest-batch/core": "0.2.0"
61
+ "@nest-batch/core": "0.2.2"
62
62
  },
63
63
  "scripts": {
64
64
  "build": "swc src -d dist --config-file ../../.swcrc && tsc --emitDeclarationOnly -p tsconfig.build.json",
@@ -6,8 +6,8 @@ import { Entity, PrimaryColumn, Column, Index } from 'typeorm';
6
6
  * One row per logical job instance. Uniqueness is enforced on
7
7
  * (jobName, jobKey) so that the same canonical key resolves to the
8
8
  * same instance across restarts. The composite unique index is
9
- * declared on the entity and is also reified in the bundled
10
- * migration under the same name.
9
+ * declared on the entity so host-owned TypeORM migrations can
10
+ * generate the matching database constraint.
11
11
  */
12
12
  @Entity('batch_job_instance')
13
13
  @Index('batch_job_instance_job_name_job_key_unique', ['jobName', 'jobKey'], { unique: true })
@@ -24,10 +24,9 @@ export class JobInstanceEntity {
24
24
  @Column({
25
25
  name: 'created_at',
26
26
  // `datetime` is portable across PostgreSQL and SQLite (the test
27
- // driver). The bundled migration uses timestamptz on
28
- // PostgreSQL by hand; SQLite loses the timezone qualifier,
29
- // which is acceptable for a creation-time stamp that is never
30
- // compared with sub-second precision in queries.
27
+ // driver). Hosts that generate PostgreSQL migrations can map the
28
+ // same logical column to timestamptz in their own migration
29
+ // pipeline.
31
30
  type: 'datetime',
32
31
  default: () => 'CURRENT_TIMESTAMP',
33
32
  })
package/src/index.ts CHANGED
@@ -29,14 +29,18 @@
29
29
  // },
30
30
  // });
31
31
  //
32
- // The original `batchMetaEntities()` factory and the bundled
33
- // `CreateBatchMeta1700000000000` migration moved to
34
- // `@nest-batch/postgresql/src/migrations/`. The driver sibling owns
35
- // the TypeORM-specific entity classes and the migration scripts;
36
- // this package owns only the repository / transaction manager
37
- // shape and the driver-provider token.
32
+ // The TypeORM entity tuple stays in this package because it is the
33
+ // schema contract consumed by a host-owned TypeORM DataSource. Apps
34
+ // generate and own their runnable migration files in their own
35
+ // migration workflow. Driver siblings bind the
36
+ // `TypeOrmDriverProvider` token to a concrete database connection.
37
+ import { BATCH_META_ENTITIES } from './entities';
38
+
38
39
  export { TypeOrmJobRepository } from './repository/typeorm-job-repository';
39
40
  export type { TypeOrmTransactionContext } from './transaction/typeorm-transaction-manager';
40
41
  export { TypeOrmTransactionManager } from './transaction/typeorm-transaction-manager';
41
42
  export * from './adapters';
43
+ export { BATCH_META_ENTITIES } from './entities';
42
44
  export * from './typeorm.driver-provider';
45
+
46
+ export const batchMetaEntities = (): typeof BATCH_META_ENTITIES => BATCH_META_ENTITIES;
@@ -100,7 +100,11 @@ function mapJobExecution(r: JobExecutionRow): JobExecution {
100
100
  id: r.id,
101
101
  jobInstanceId: r.job_instance_id,
102
102
  status: r.status as JobStatus,
103
- startTime: r.start_time ? (r.start_time instanceof Date ? r.start_time : new Date(r.start_time)) : null,
103
+ startTime: r.start_time
104
+ ? r.start_time instanceof Date
105
+ ? r.start_time
106
+ : new Date(r.start_time)
107
+ : null,
104
108
  endTime: r.end_time ? (r.end_time instanceof Date ? r.end_time : new Date(r.end_time)) : null,
105
109
  exitCode: r.exit_code,
106
110
  exitMessage: r.exit_message,
@@ -133,8 +137,8 @@ function mapStepExecution(r: StepExecutionRow): StepExecution {
133
137
  * provided by the `@nest-batch/postgresql` (or future
134
138
  * `@nest-batch/mysql`) driver sibling via the `TypeOrmDriverProvider`
135
139
  * token. The repository itself uses raw SQL via `EntityManager.query`
136
- * so the column-shape contract is owned by the driver sibling
137
- * (the bundled 6-table migration).
140
+ * against the table contract represented by this package's exported
141
+ * TypeORM entities. The consuming app owns the runnable migration.
138
142
  *
139
143
  * The contract guarantees:
140
144
  * - `getOrCreateJobInstance` is race-safe via the (jobName, jobKey)
@@ -151,9 +155,7 @@ function mapStepExecution(r: StepExecutionRow): StepExecution {
151
155
  */
152
156
  @Injectable()
153
157
  export class TypeOrmJobRepository extends JobRepository {
154
- constructor(
155
- @Inject(TypeOrmDriverProvider) private readonly dataSource: DataSource,
156
- ) {
158
+ constructor(@Inject(TypeOrmDriverProvider) private readonly dataSource: DataSource) {
157
159
  super();
158
160
  }
159
161
 
@@ -162,35 +164,35 @@ export class TypeOrmJobRepository extends JobRepository {
162
164
  }
163
165
 
164
166
  async getOrCreateJobInstance(name: string, jobKey: string): Promise<JobInstance> {
165
- const existing = await this.em().query(
167
+ const existing = (await this.em().query(
166
168
  `SELECT "id", "job_name", "job_key", "created_at"
167
169
  FROM "batch_job_instance"
168
170
  WHERE "job_name" = $1 AND "job_key" = $2
169
171
  LIMIT 1`,
170
172
  [name, jobKey],
171
- ) as JobInstanceRow[];
173
+ )) as JobInstanceRow[];
172
174
  if (existing.length > 0) return mapJobInstance(existing[0]!);
173
175
 
174
176
  const id = randomUUID();
175
177
  try {
176
- const inserted = await this.em().query(
178
+ const inserted = (await this.em().query(
177
179
  `INSERT INTO "batch_job_instance" ("id", "job_name", "job_key", "created_at")
178
180
  VALUES ($1, $2, $3, NOW())
179
181
  ON CONFLICT ("job_name", "job_key") DO NOTHING
180
182
  RETURNING "id", "job_name", "job_key", "created_at"`,
181
183
  [id, name, jobKey],
182
- ) as JobInstanceRow[];
184
+ )) as JobInstanceRow[];
183
185
  if (inserted.length > 0) return mapJobInstance(inserted[0]!);
184
186
  } catch {
185
187
  // Fall through to read-back.
186
188
  }
187
- const winner = await this.em().query(
189
+ const winner = (await this.em().query(
188
190
  `SELECT "id", "job_name", "job_key", "created_at"
189
191
  FROM "batch_job_instance"
190
192
  WHERE "job_name" = $1 AND "job_key" = $2
191
193
  LIMIT 1`,
192
194
  [name, jobKey],
193
- ) as JobInstanceRow[];
195
+ )) as JobInstanceRow[];
194
196
  if (winner.length === 0) {
195
197
  throw new Error(
196
198
  `Failed to upsert JobInstance (${name}, ${jobKey}) and could not read it back`,
@@ -199,10 +201,7 @@ export class TypeOrmJobRepository extends JobRepository {
199
201
  return mapJobInstance(winner[0]!);
200
202
  }
201
203
 
202
- async createJobExecution(
203
- jobInstanceId: string,
204
- params: JobParameters,
205
- ): Promise<JobExecution> {
204
+ async createJobExecution(jobInstanceId: string, params: JobParameters): Promise<JobExecution> {
206
205
  const exec = {
207
206
  id: randomUUID(),
208
207
  job_instance_id: jobInstanceId,
@@ -213,12 +212,12 @@ export class TypeOrmJobRepository extends JobRepository {
213
212
  exit_message: '',
214
213
  params: serializeContext(deepClone(params)),
215
214
  };
216
- const rows = await this.em().query(
215
+ const rows = (await this.em().query(
217
216
  `INSERT INTO "batch_job_execution" ("id", "job_instance_id", "status", "start_time", "end_time", "exit_code", "exit_message", "params")
218
217
  VALUES ($1, $2, $3, NULL, NULL, $4, $5, $6)
219
218
  RETURNING "id", "job_instance_id", "status", "start_time", "end_time", "exit_code", "exit_message", "params"`,
220
219
  [exec.id, exec.job_instance_id, exec.status, exec.exit_code, exec.exit_message, exec.params],
221
- ) as JobExecutionRow[];
220
+ )) as JobExecutionRow[];
222
221
  return mapJobExecution(rows[0]!);
223
222
  }
224
223
 
@@ -241,23 +240,23 @@ export class TypeOrmJobRepository extends JobRepository {
241
240
  const isSqlite = this.dataSource.options.type === 'better-sqlite3';
242
241
  let instanceId: string;
243
242
  if (isSqlite) {
244
- const rows = await em.query(
243
+ const rows = (await em.query(
245
244
  `SELECT "id" FROM "batch_job_instance"
246
245
  WHERE "job_name" = $1 AND "job_key" = $2
247
246
  LIMIT 1`,
248
247
  [name, jobKey],
249
- ) as Array<{ id: string }>;
248
+ )) as Array<{ id: string }>;
250
249
  if (rows.length === 0) {
251
250
  throw new JobExecutionAlreadyRunningError(name);
252
251
  }
253
252
  instanceId = rows[0]!.id;
254
253
  } else {
255
- const rows = await em.query(
254
+ const rows = (await em.query(
256
255
  `SELECT "id" FROM "batch_job_instance"
257
256
  WHERE "job_name" = $1 AND "job_key" = $2
258
257
  FOR UPDATE SKIP LOCKED`,
259
258
  [name, jobKey],
260
- ) as Array<{ id: string }>;
259
+ )) as Array<{ id: string }>;
261
260
  if (rows.length === 0) {
262
261
  throw new JobExecutionAlreadyRunningError(name);
263
262
  }
@@ -265,24 +264,24 @@ export class TypeOrmJobRepository extends JobRepository {
265
264
  }
266
265
 
267
266
  // 3. Under the lock, verify no running execution.
268
- const running = await em.query(
267
+ const running = (await em.query(
269
268
  `SELECT "id" FROM "batch_job_execution"
270
269
  WHERE "job_instance_id" = $1 AND "status" IN ($2, $3)
271
270
  LIMIT 1`,
272
271
  [instanceId, JobStatus.STARTING, JobStatus.STARTED],
273
- ) as Array<{ id: string }>;
272
+ )) as Array<{ id: string }>;
274
273
  if (running.length > 0) {
275
274
  throw new JobExecutionAlreadyRunningError(running[0]!.id);
276
275
  }
277
276
 
278
277
  // 4. Create the new execution row.
279
278
  const execId = randomUUID();
280
- const inserted = await em.query(
279
+ const inserted = (await em.query(
281
280
  `INSERT INTO "batch_job_execution" ("id", "job_instance_id", "status", "start_time", "end_time", "exit_code", "exit_message", "params")
282
281
  VALUES ($1, $2, $3, NULL, NULL, '', '', $4)
283
282
  RETURNING "id", "job_instance_id", "status", "start_time", "end_time", "exit_code", "exit_message", "params"`,
284
283
  [execId, instanceId, JobStatus.STARTING, serializeContext(deepClone(params))],
285
- ) as JobExecutionRow[];
284
+ )) as JobExecutionRow[];
286
285
  return mapJobExecution(inserted[0]!);
287
286
  });
288
287
  }
@@ -291,11 +290,26 @@ export class TypeOrmJobRepository extends JobRepository {
291
290
  const sets: string[] = [];
292
291
  const values: unknown[] = [];
293
292
  let i = 1;
294
- if (patch.status !== undefined) { sets.push(`"status" = $${i++}`); values.push(patch.status); }
295
- if (patch.startTime !== undefined) { sets.push(`"start_time" = $${i++}`); values.push(patch.startTime); }
296
- if (patch.endTime !== undefined) { sets.push(`"end_time" = $${i++}`); values.push(patch.endTime); }
297
- if (patch.exitCode !== undefined) { sets.push(`"exit_code" = $${i++}`); values.push(patch.exitCode); }
298
- if (patch.exitMessage !== undefined) { sets.push(`"exit_message" = $${i++}`); values.push(patch.exitMessage); }
293
+ if (patch.status !== undefined) {
294
+ sets.push(`"status" = $${i++}`);
295
+ values.push(patch.status);
296
+ }
297
+ if (patch.startTime !== undefined) {
298
+ sets.push(`"start_time" = $${i++}`);
299
+ values.push(patch.startTime);
300
+ }
301
+ if (patch.endTime !== undefined) {
302
+ sets.push(`"end_time" = $${i++}`);
303
+ values.push(patch.endTime);
304
+ }
305
+ if (patch.exitCode !== undefined) {
306
+ sets.push(`"exit_code" = $${i++}`);
307
+ values.push(patch.exitCode);
308
+ }
309
+ if (patch.exitMessage !== undefined) {
310
+ sets.push(`"exit_message" = $${i++}`);
311
+ values.push(patch.exitMessage);
312
+ }
299
313
  if (sets.length === 0) return;
300
314
  values.push(executionId);
301
315
  await this.em().query(
@@ -305,22 +319,22 @@ export class TypeOrmJobRepository extends JobRepository {
305
319
  }
306
320
 
307
321
  async getJobExecution(executionId: string): Promise<JobExecution | null> {
308
- const rows = await this.em().query(
322
+ const rows = (await this.em().query(
309
323
  `SELECT "id", "job_instance_id", "status", "start_time", "end_time", "exit_code", "exit_message", "params"
310
324
  FROM "batch_job_execution" WHERE "id" = $1 LIMIT 1`,
311
325
  [executionId],
312
- ) as JobExecutionRow[];
326
+ )) as JobExecutionRow[];
313
327
  return rows.length > 0 ? mapJobExecution(rows[0]!) : null;
314
328
  }
315
329
 
316
330
  override async getJobInstance(jobInstanceId: string): Promise<JobInstance | null> {
317
- const rows = await this.em().query(
331
+ const rows = (await this.em().query(
318
332
  `SELECT "id", "job_name", "job_key", "created_at"
319
333
  FROM "batch_job_instance"
320
334
  WHERE "id" = $1
321
335
  LIMIT 1`,
322
336
  [jobInstanceId],
323
- ) as JobInstanceRow[];
337
+ )) as JobInstanceRow[];
324
338
  return rows.length > 0 ? mapJobInstance(rows[0]!) : null;
325
339
  }
326
340
 
@@ -336,13 +350,13 @@ export class TypeOrmJobRepository extends JobRepository {
336
350
  where.push(`"job_key" = $${i++}`);
337
351
  values.push(filter.jobKey);
338
352
  }
339
- const rows = await this.em().query(
353
+ const rows = (await this.em().query(
340
354
  `SELECT "id", "job_name", "job_key", "created_at"
341
355
  FROM "batch_job_instance"
342
356
  ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}
343
357
  ORDER BY "created_at" ASC, "id" ASC`,
344
358
  values,
345
- ) as JobInstanceRow[];
359
+ )) as JobInstanceRow[];
346
360
  return rows.map(mapJobInstance);
347
361
  }
348
362
 
@@ -368,57 +382,75 @@ export class TypeOrmJobRepository extends JobRepository {
368
382
  where.push(`"start_time" <= $${i++}`);
369
383
  values.push(filter.startedBefore);
370
384
  }
371
- const rows = await this.em().query(
385
+ const rows = (await this.em().query(
372
386
  `SELECT "id", "job_instance_id", "status", "start_time", "end_time", "exit_code", "exit_message", "params"
373
387
  FROM "batch_job_execution"
374
388
  ${where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''}
375
389
  ORDER BY "start_time" DESC NULLS LAST, "id" DESC`,
376
390
  values,
377
- ) as JobExecutionRow[];
391
+ )) as JobExecutionRow[];
378
392
  return rows.map(mapJobExecution);
379
393
  }
380
394
 
381
395
  async getRunningJobExecution(jobInstanceId: string): Promise<JobExecution | null> {
382
396
  if (!jobInstanceId) return null;
383
- const rows = await this.em().query(
397
+ const rows = (await this.em().query(
384
398
  `SELECT "id", "job_instance_id", "status", "start_time", "end_time", "exit_code", "exit_message", "params"
385
399
  FROM "batch_job_execution"
386
400
  WHERE "job_instance_id" = $1 AND "status" IN ($2, $3)
387
401
  ORDER BY "start_time" DESC NULLS LAST LIMIT 1`,
388
402
  [jobInstanceId, JobStatus.STARTING, JobStatus.STARTED],
389
- ) as JobExecutionRow[];
403
+ )) as JobExecutionRow[];
390
404
  return rows.length > 0 ? mapJobExecution(rows[0]!) : null;
391
405
  }
392
406
 
393
- async createStepExecution(
394
- jobExecutionId: string,
395
- stepName: string,
396
- ): Promise<StepExecution> {
407
+ async createStepExecution(jobExecutionId: string, stepName: string): Promise<StepExecution> {
397
408
  const stepId = randomUUID();
398
- const rows = await this.em().query(
409
+ const rows = (await this.em().query(
399
410
  `INSERT INTO "batch_step_execution" ("id", "job_execution_id", "step_name", "status", "read_count", "write_count", "skip_count", "rollback_count", "commit_count", "exit_code", "exit_message", "created_at")
400
411
  VALUES ($1, $2, $3, $4, 0, 0, 0, 0, 0, '', '', NOW())
401
412
  RETURNING "id", "job_execution_id", "step_name", "status", "read_count", "write_count", "skip_count", "rollback_count", "commit_count", "exit_code", "exit_message", "created_at"`,
402
413
  [stepId, jobExecutionId, stepName, StepStatus.STARTING],
403
- ) as StepExecutionRow[];
414
+ )) as StepExecutionRow[];
404
415
  return mapStepExecution(rows[0]!);
405
416
  }
406
417
 
407
- async updateStepExecution(
408
- stepExecutionId: string,
409
- patch: StepExecutionPatch,
410
- ): Promise<void> {
418
+ async updateStepExecution(stepExecutionId: string, patch: StepExecutionPatch): Promise<void> {
411
419
  const sets: string[] = [];
412
420
  const values: unknown[] = [];
413
421
  let i = 1;
414
- if (patch.status !== undefined) { sets.push(`"status" = $${i++}`); values.push(patch.status); }
415
- if (patch.readCount !== undefined) { sets.push(`"read_count" = $${i++}`); values.push(patch.readCount); }
416
- if (patch.writeCount !== undefined) { sets.push(`"write_count" = $${i++}`); values.push(patch.writeCount); }
417
- if (patch.skipCount !== undefined) { sets.push(`"skip_count" = $${i++}`); values.push(patch.skipCount); }
418
- if (patch.rollbackCount !== undefined) { sets.push(`"rollback_count" = $${i++}`); values.push(patch.rollbackCount); }
419
- if (patch.commitCount !== undefined) { sets.push(`"commit_count" = $${i++}`); values.push(patch.commitCount); }
420
- if (patch.exitCode !== undefined) { sets.push(`"exit_code" = $${i++}`); values.push(patch.exitCode); }
421
- if (patch.exitMessage !== undefined) { sets.push(`"exit_message" = $${i++}`); values.push(patch.exitMessage); }
422
+ if (patch.status !== undefined) {
423
+ sets.push(`"status" = $${i++}`);
424
+ values.push(patch.status);
425
+ }
426
+ if (patch.readCount !== undefined) {
427
+ sets.push(`"read_count" = $${i++}`);
428
+ values.push(patch.readCount);
429
+ }
430
+ if (patch.writeCount !== undefined) {
431
+ sets.push(`"write_count" = $${i++}`);
432
+ values.push(patch.writeCount);
433
+ }
434
+ if (patch.skipCount !== undefined) {
435
+ sets.push(`"skip_count" = $${i++}`);
436
+ values.push(patch.skipCount);
437
+ }
438
+ if (patch.rollbackCount !== undefined) {
439
+ sets.push(`"rollback_count" = $${i++}`);
440
+ values.push(patch.rollbackCount);
441
+ }
442
+ if (patch.commitCount !== undefined) {
443
+ sets.push(`"commit_count" = $${i++}`);
444
+ values.push(patch.commitCount);
445
+ }
446
+ if (patch.exitCode !== undefined) {
447
+ sets.push(`"exit_code" = $${i++}`);
448
+ values.push(patch.exitCode);
449
+ }
450
+ if (patch.exitMessage !== undefined) {
451
+ sets.push(`"exit_message" = $${i++}`);
452
+ values.push(patch.exitMessage);
453
+ }
422
454
  if (sets.length === 0) return;
423
455
  values.push(stepExecutionId);
424
456
  await this.em().query(
@@ -428,22 +460,22 @@ export class TypeOrmJobRepository extends JobRepository {
428
460
  }
429
461
 
430
462
  async getStepExecution(stepExecutionId: string): Promise<StepExecution | null> {
431
- const rows = await this.em().query(
463
+ const rows = (await this.em().query(
432
464
  `SELECT "id", "job_execution_id", "step_name", "status", "read_count", "write_count", "skip_count", "rollback_count", "commit_count", "exit_code", "exit_message", "created_at"
433
465
  FROM "batch_step_execution" WHERE "id" = $1 LIMIT 1`,
434
466
  [stepExecutionId],
435
- ) as StepExecutionRow[];
467
+ )) as StepExecutionRow[];
436
468
  return rows.length > 0 ? mapStepExecution(rows[0]!) : null;
437
469
  }
438
470
 
439
471
  override async findStepExecutions(jobExecutionId: string): Promise<StepExecution[]> {
440
- const rows = await this.em().query(
472
+ const rows = (await this.em().query(
441
473
  `SELECT "id", "job_execution_id", "step_name", "status", "read_count", "write_count", "skip_count", "rollback_count", "commit_count", "exit_code", "exit_message", "created_at"
442
474
  FROM "batch_step_execution"
443
475
  WHERE "job_execution_id" = $1
444
476
  ORDER BY "created_at" ASC, "id" ASC`,
445
477
  [jobExecutionId],
446
- ) as StepExecutionRow[];
478
+ )) as StepExecutionRow[];
447
479
  return rows.map(mapStepExecution);
448
480
  }
449
481
 
@@ -460,24 +492,24 @@ export class TypeOrmJobRepository extends JobRepository {
460
492
  jobExecutionId: string,
461
493
  stepName: string,
462
494
  ): Promise<StepExecution | null> {
463
- const rows = await this.em().query(
495
+ const rows = (await this.em().query(
464
496
  `SELECT "id", "job_execution_id", "step_name", "status", "read_count", "write_count", "skip_count", "rollback_count", "commit_count", "exit_code", "exit_message", "created_at"
465
497
  FROM "batch_step_execution"
466
498
  WHERE "job_execution_id" = $1 AND "step_name" = $2
467
499
  ORDER BY "created_at" DESC, "id" DESC
468
500
  LIMIT 1`,
469
501
  [jobExecutionId, stepName],
470
- ) as StepExecutionRow[];
502
+ )) as StepExecutionRow[];
471
503
  return rows.length > 0 ? mapStepExecution(rows[0]!) : null;
472
504
  }
473
505
 
474
506
  async getExecutionContext(scope: ExecutionScope): Promise<ExecutionContext> {
475
507
  const key = scopeKey(scope);
476
508
  if (key.startsWith('job::')) {
477
- const rows = await this.em().query(
509
+ const rows = (await this.em().query(
478
510
  `SELECT "data", "version" FROM "batch_job_execution_context" WHERE "job_execution_id" = $1 LIMIT 1`,
479
511
  [key.slice(5)],
480
- ) as ContextRow[];
512
+ )) as ContextRow[];
481
513
  if (rows.length > 0) {
482
514
  return {
483
515
  data: rows[0]!.data.length > 0 ? deserializeContext(rows[0]!.data) : null,
@@ -485,10 +517,10 @@ export class TypeOrmJobRepository extends JobRepository {
485
517
  };
486
518
  }
487
519
  } else {
488
- const rows = await this.em().query(
520
+ const rows = (await this.em().query(
489
521
  `SELECT "data", "version" FROM "batch_step_execution_context" WHERE "step_execution_id" = $1 LIMIT 1`,
490
522
  [key.slice(6)],
491
- ) as ContextRow[];
523
+ )) as ContextRow[];
492
524
  if (rows.length > 0) {
493
525
  return {
494
526
  data: rows[0]!.data.length > 0 ? deserializeContext(rows[0]!.data) : null,
@@ -509,11 +541,12 @@ export class TypeOrmJobRepository extends JobRepository {
509
541
  const serialized = serializeContext(deepClone(ctx.data));
510
542
  if (key.startsWith('job::')) {
511
543
  const jobExecutionId = key.slice(5);
512
- const existing = await this.em().query(
544
+ const existing = (await this.em().query(
513
545
  `SELECT "version" FROM "batch_job_execution_context" WHERE "job_execution_id" = $1 LIMIT 1`,
514
546
  [jobExecutionId],
515
- ) as ContextRow[];
516
- const nextVersion = version !== undefined ? version : (existing.length > 0 ? existing[0]!.version + 1 : 0);
547
+ )) as ContextRow[];
548
+ const nextVersion =
549
+ version !== undefined ? version : existing.length > 0 ? existing[0]!.version + 1 : 0;
517
550
  if (existing.length > 0) {
518
551
  await this.em().query(
519
552
  `UPDATE "batch_job_execution_context" SET "data" = $1, "version" = $2 WHERE "job_execution_id" = $3`,
@@ -527,11 +560,12 @@ export class TypeOrmJobRepository extends JobRepository {
527
560
  }
528
561
  } else {
529
562
  const stepExecutionId = key.slice(6);
530
- const existing = await this.em().query(
563
+ const existing = (await this.em().query(
531
564
  `SELECT "version" FROM "batch_step_execution_context" WHERE "step_execution_id" = $1 LIMIT 1`,
532
565
  [stepExecutionId],
533
- ) as ContextRow[];
534
- const nextVersion = version !== undefined ? version : (existing.length > 0 ? existing[0]!.version + 1 : 0);
566
+ )) as ContextRow[];
567
+ const nextVersion =
568
+ version !== undefined ? version : existing.length > 0 ? existing[0]!.version + 1 : 0;
535
569
  if (existing.length > 0) {
536
570
  await this.em().query(
537
571
  `UPDATE "batch_step_execution_context" SET "data" = $1, "version" = $2 WHERE "step_execution_id" = $3`,
@@ -1,28 +0,0 @@
1
- import { MigrationInterface, QueryRunner } from 'typeorm';
2
- /**
3
- * Creates the six batch meta-tables owned by
4
- * this package:
5
- *
6
- * - batch_job_instance (root, unique on (job_name, job_key))
7
- * - batch_job_execution (one per job run, indexed by instance)
8
- * - batch_step_execution (one per step run, indexed by exec)
9
- * - batch_job_execution_context (JSON payload + version, keyed by exec)
10
- * - batch_step_execution_context (JSON payload + version, keyed by step)
11
- *
12
- * This migration intentionally uses generic ANSI/PostgreSQL
13
- * syntax (varchar + text + timestamptz + int). The adapter
14
- * package's `typeorm-job-repository` is portable across SQLite
15
- * (test database) and PostgreSQL (production). Users targeting
16
- * MySQL or another driver should adjust the `down` (and
17
- * optionally `up`) to match their column types.
18
- *
19
- * The `params` column on `batch_job_execution` is a JSON
20
- * snapshot — stored as `text` to keep the schema portable and
21
- * always serialized, never queried structurally.
22
- */
23
- export declare class CreateBatchMeta1700000000000 implements MigrationInterface {
24
- name: string;
25
- up(queryRunner: QueryRunner): Promise<void>;
26
- down(queryRunner: QueryRunner): Promise<void>;
27
- }
28
- //# sourceMappingURL=1700000000000-CreateBatchMeta.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"1700000000000-CreateBatchMeta.d.ts","sourceRoot":"","sources":["../../../src/migrations/1700000000000-CreateBatchMeta.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,4BAA6B,YAAW,kBAAkB;IACrE,IAAI,SAAkC;IAEzB,EAAE,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAkE3C,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAO3D"}
@@ -1,83 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "CreateBatchMeta1700000000000", {
6
- enumerable: true,
7
- get: function() {
8
- return CreateBatchMeta1700000000000;
9
- }
10
- });
11
- let CreateBatchMeta1700000000000 = class CreateBatchMeta1700000000000 {
12
- name = 'CreateBatchMeta1700000000000';
13
- async up(queryRunner) {
14
- await queryRunner.query(`
15
- CREATE TABLE IF NOT EXISTS "batch_job_instance" (
16
- "id" varchar(255) PRIMARY KEY,
17
- "job_name" varchar(255) NOT NULL,
18
- "job_key" varchar(255) NOT NULL,
19
- "created_at" timestamptz NOT NULL DEFAULT now(),
20
- CONSTRAINT "batch_job_instance_job_name_job_key_unique" UNIQUE ("job_name", "job_key")
21
- )
22
- `);
23
- await queryRunner.query(`
24
- CREATE TABLE IF NOT EXISTS "batch_job_execution" (
25
- "id" varchar(255) PRIMARY KEY,
26
- "job_instance_id" varchar(255) NOT NULL,
27
- "status" varchar(20) NOT NULL,
28
- "start_time" timestamptz NULL,
29
- "end_time" timestamptz NULL,
30
- "exit_code" varchar(255) NOT NULL DEFAULT '',
31
- "exit_message" text NOT NULL DEFAULT '',
32
- "params" text NOT NULL DEFAULT '{}'
33
- )
34
- `);
35
- await queryRunner.query(`
36
- CREATE INDEX IF NOT EXISTS "batch_job_execution_job_instance_id_index"
37
- ON "batch_job_execution" ("job_instance_id")
38
- `);
39
- await queryRunner.query(`
40
- CREATE TABLE IF NOT EXISTS "batch_step_execution" (
41
- "id" varchar(255) PRIMARY KEY,
42
- "job_execution_id" varchar(255) NOT NULL,
43
- "step_name" varchar(255) NOT NULL,
44
- "status" varchar(20) NOT NULL,
45
- "read_count" int NOT NULL DEFAULT 0,
46
- "write_count" int NOT NULL DEFAULT 0,
47
- "skip_count" int NOT NULL DEFAULT 0,
48
- "rollback_count" int NOT NULL DEFAULT 0,
49
- "commit_count" int NOT NULL DEFAULT 0,
50
- "exit_code" varchar(255) NOT NULL DEFAULT '',
51
- "exit_message" text NOT NULL DEFAULT '',
52
- "created_at" timestamptz NOT NULL DEFAULT now()
53
- )
54
- `);
55
- await queryRunner.query(`
56
- CREATE INDEX IF NOT EXISTS "batch_step_execution_job_execution_id_index"
57
- ON "batch_step_execution" ("job_execution_id")
58
- `);
59
- await queryRunner.query(`
60
- CREATE TABLE IF NOT EXISTS "batch_job_execution_context" (
61
- "job_execution_id" varchar(255) PRIMARY KEY,
62
- "data" text NOT NULL,
63
- "version" int NOT NULL DEFAULT 0
64
- )
65
- `);
66
- await queryRunner.query(`
67
- CREATE TABLE IF NOT EXISTS "batch_step_execution_context" (
68
- "step_execution_id" varchar(255) PRIMARY KEY,
69
- "data" text NOT NULL,
70
- "version" int NOT NULL DEFAULT 0
71
- )
72
- `);
73
- }
74
- async down(queryRunner) {
75
- await queryRunner.query(`DROP TABLE IF EXISTS "batch_step_execution_context"`);
76
- await queryRunner.query(`DROP TABLE IF EXISTS "batch_job_execution_context"`);
77
- await queryRunner.query(`DROP TABLE IF EXISTS "batch_step_execution"`);
78
- await queryRunner.query(`DROP TABLE IF EXISTS "batch_job_execution"`);
79
- await queryRunner.query(`DROP TABLE IF EXISTS "batch_job_instance"`);
80
- }
81
- };
82
-
83
- //# sourceMappingURL=1700000000000-CreateBatchMeta.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/migrations/1700000000000-CreateBatchMeta.ts"],"sourcesContent":["import { MigrationInterface, QueryRunner } from 'typeorm';\n\n/**\n * Creates the six batch meta-tables owned by\n * this package:\n *\n * - batch_job_instance (root, unique on (job_name, job_key))\n * - batch_job_execution (one per job run, indexed by instance)\n * - batch_step_execution (one per step run, indexed by exec)\n * - batch_job_execution_context (JSON payload + version, keyed by exec)\n * - batch_step_execution_context (JSON payload + version, keyed by step)\n *\n * This migration intentionally uses generic ANSI/PostgreSQL\n * syntax (varchar + text + timestamptz + int). The adapter\n * package's `typeorm-job-repository` is portable across SQLite\n * (test database) and PostgreSQL (production). Users targeting\n * MySQL or another driver should adjust the `down` (and\n * optionally `up`) to match their column types.\n *\n * The `params` column on `batch_job_execution` is a JSON\n * snapshot — stored as `text` to keep the schema portable and\n * always serialized, never queried structurally.\n */\nexport class CreateBatchMeta1700000000000 implements MigrationInterface {\n name = 'CreateBatchMeta1700000000000';\n\n public async up(queryRunner: QueryRunner): Promise<void> {\n await queryRunner.query(`\n CREATE TABLE IF NOT EXISTS \"batch_job_instance\" (\n \"id\" varchar(255) PRIMARY KEY,\n \"job_name\" varchar(255) NOT NULL,\n \"job_key\" varchar(255) NOT NULL,\n \"created_at\" timestamptz NOT NULL DEFAULT now(),\n CONSTRAINT \"batch_job_instance_job_name_job_key_unique\" UNIQUE (\"job_name\", \"job_key\")\n )\n `);\n\n await queryRunner.query(`\n CREATE TABLE IF NOT EXISTS \"batch_job_execution\" (\n \"id\" varchar(255) PRIMARY KEY,\n \"job_instance_id\" varchar(255) NOT NULL,\n \"status\" varchar(20) NOT NULL,\n \"start_time\" timestamptz NULL,\n \"end_time\" timestamptz NULL,\n \"exit_code\" varchar(255) NOT NULL DEFAULT '',\n \"exit_message\" text NOT NULL DEFAULT '',\n \"params\" text NOT NULL DEFAULT '{}'\n )\n `);\n await queryRunner.query(`\n CREATE INDEX IF NOT EXISTS \"batch_job_execution_job_instance_id_index\"\n ON \"batch_job_execution\" (\"job_instance_id\")\n `);\n\n await queryRunner.query(`\n CREATE TABLE IF NOT EXISTS \"batch_step_execution\" (\n \"id\" varchar(255) PRIMARY KEY,\n \"job_execution_id\" varchar(255) NOT NULL,\n \"step_name\" varchar(255) NOT NULL,\n \"status\" varchar(20) NOT NULL,\n \"read_count\" int NOT NULL DEFAULT 0,\n \"write_count\" int NOT NULL DEFAULT 0,\n \"skip_count\" int NOT NULL DEFAULT 0,\n \"rollback_count\" int NOT NULL DEFAULT 0,\n \"commit_count\" int NOT NULL DEFAULT 0,\n \"exit_code\" varchar(255) NOT NULL DEFAULT '',\n \"exit_message\" text NOT NULL DEFAULT '',\n \"created_at\" timestamptz NOT NULL DEFAULT now()\n )\n `);\n await queryRunner.query(`\n CREATE INDEX IF NOT EXISTS \"batch_step_execution_job_execution_id_index\"\n ON \"batch_step_execution\" (\"job_execution_id\")\n `);\n\n await queryRunner.query(`\n CREATE TABLE IF NOT EXISTS \"batch_job_execution_context\" (\n \"job_execution_id\" varchar(255) PRIMARY KEY,\n \"data\" text NOT NULL,\n \"version\" int NOT NULL DEFAULT 0\n )\n `);\n\n await queryRunner.query(`\n CREATE TABLE IF NOT EXISTS \"batch_step_execution_context\" (\n \"step_execution_id\" varchar(255) PRIMARY KEY,\n \"data\" text NOT NULL,\n \"version\" int NOT NULL DEFAULT 0\n )\n `);\n }\n\n public async down(queryRunner: QueryRunner): Promise<void> {\n await queryRunner.query(`DROP TABLE IF EXISTS \"batch_step_execution_context\"`);\n await queryRunner.query(`DROP TABLE IF EXISTS \"batch_job_execution_context\"`);\n await queryRunner.query(`DROP TABLE IF EXISTS \"batch_step_execution\"`);\n await queryRunner.query(`DROP TABLE IF EXISTS \"batch_job_execution\"`);\n await queryRunner.query(`DROP TABLE IF EXISTS \"batch_job_instance\"`);\n }\n}\n"],"names":["CreateBatchMeta1700000000000","name","up","queryRunner","query","down"],"mappings":";;;;+BAuBaA;;;eAAAA;;;AAAN,IAAA,AAAMA,+BAAN,MAAMA;IACXC,OAAO,+BAA+B;IAEtC,MAAaC,GAAGC,WAAwB,EAAiB;QACvD,MAAMA,YAAYC,KAAK,CAAC,CAAC;;;;;;;;IAQzB,CAAC;QAED,MAAMD,YAAYC,KAAK,CAAC,CAAC;;;;;;;;;;;IAWzB,CAAC;QACD,MAAMD,YAAYC,KAAK,CAAC,CAAC;;;IAGzB,CAAC;QAED,MAAMD,YAAYC,KAAK,CAAC,CAAC;;;;;;;;;;;;;;;IAezB,CAAC;QACD,MAAMD,YAAYC,KAAK,CAAC,CAAC;;;IAGzB,CAAC;QAED,MAAMD,YAAYC,KAAK,CAAC,CAAC;;;;;;IAMzB,CAAC;QAED,MAAMD,YAAYC,KAAK,CAAC,CAAC;;;;;;IAMzB,CAAC;IACH;IAEA,MAAaC,KAAKF,WAAwB,EAAiB;QACzD,MAAMA,YAAYC,KAAK,CAAC,CAAC,mDAAmD,CAAC;QAC7E,MAAMD,YAAYC,KAAK,CAAC,CAAC,kDAAkD,CAAC;QAC5E,MAAMD,YAAYC,KAAK,CAAC,CAAC,2CAA2C,CAAC;QACrE,MAAMD,YAAYC,KAAK,CAAC,CAAC,0CAA0C,CAAC;QACpE,MAAMD,YAAYC,KAAK,CAAC,CAAC,yCAAyC,CAAC;IACrE;AACF"}
@@ -1,100 +0,0 @@
1
- import { MigrationInterface, QueryRunner } from 'typeorm';
2
-
3
- /**
4
- * Creates the six batch meta-tables owned by
5
- * this package:
6
- *
7
- * - batch_job_instance (root, unique on (job_name, job_key))
8
- * - batch_job_execution (one per job run, indexed by instance)
9
- * - batch_step_execution (one per step run, indexed by exec)
10
- * - batch_job_execution_context (JSON payload + version, keyed by exec)
11
- * - batch_step_execution_context (JSON payload + version, keyed by step)
12
- *
13
- * This migration intentionally uses generic ANSI/PostgreSQL
14
- * syntax (varchar + text + timestamptz + int). The adapter
15
- * package's `typeorm-job-repository` is portable across SQLite
16
- * (test database) and PostgreSQL (production). Users targeting
17
- * MySQL or another driver should adjust the `down` (and
18
- * optionally `up`) to match their column types.
19
- *
20
- * The `params` column on `batch_job_execution` is a JSON
21
- * snapshot — stored as `text` to keep the schema portable and
22
- * always serialized, never queried structurally.
23
- */
24
- export class CreateBatchMeta1700000000000 implements MigrationInterface {
25
- name = 'CreateBatchMeta1700000000000';
26
-
27
- public async up(queryRunner: QueryRunner): Promise<void> {
28
- await queryRunner.query(`
29
- CREATE TABLE IF NOT EXISTS "batch_job_instance" (
30
- "id" varchar(255) PRIMARY KEY,
31
- "job_name" varchar(255) NOT NULL,
32
- "job_key" varchar(255) NOT NULL,
33
- "created_at" timestamptz NOT NULL DEFAULT now(),
34
- CONSTRAINT "batch_job_instance_job_name_job_key_unique" UNIQUE ("job_name", "job_key")
35
- )
36
- `);
37
-
38
- await queryRunner.query(`
39
- CREATE TABLE IF NOT EXISTS "batch_job_execution" (
40
- "id" varchar(255) PRIMARY KEY,
41
- "job_instance_id" varchar(255) NOT NULL,
42
- "status" varchar(20) NOT NULL,
43
- "start_time" timestamptz NULL,
44
- "end_time" timestamptz NULL,
45
- "exit_code" varchar(255) NOT NULL DEFAULT '',
46
- "exit_message" text NOT NULL DEFAULT '',
47
- "params" text NOT NULL DEFAULT '{}'
48
- )
49
- `);
50
- await queryRunner.query(`
51
- CREATE INDEX IF NOT EXISTS "batch_job_execution_job_instance_id_index"
52
- ON "batch_job_execution" ("job_instance_id")
53
- `);
54
-
55
- await queryRunner.query(`
56
- CREATE TABLE IF NOT EXISTS "batch_step_execution" (
57
- "id" varchar(255) PRIMARY KEY,
58
- "job_execution_id" varchar(255) NOT NULL,
59
- "step_name" varchar(255) NOT NULL,
60
- "status" varchar(20) NOT NULL,
61
- "read_count" int NOT NULL DEFAULT 0,
62
- "write_count" int NOT NULL DEFAULT 0,
63
- "skip_count" int NOT NULL DEFAULT 0,
64
- "rollback_count" int NOT NULL DEFAULT 0,
65
- "commit_count" int NOT NULL DEFAULT 0,
66
- "exit_code" varchar(255) NOT NULL DEFAULT '',
67
- "exit_message" text NOT NULL DEFAULT '',
68
- "created_at" timestamptz NOT NULL DEFAULT now()
69
- )
70
- `);
71
- await queryRunner.query(`
72
- CREATE INDEX IF NOT EXISTS "batch_step_execution_job_execution_id_index"
73
- ON "batch_step_execution" ("job_execution_id")
74
- `);
75
-
76
- await queryRunner.query(`
77
- CREATE TABLE IF NOT EXISTS "batch_job_execution_context" (
78
- "job_execution_id" varchar(255) PRIMARY KEY,
79
- "data" text NOT NULL,
80
- "version" int NOT NULL DEFAULT 0
81
- )
82
- `);
83
-
84
- await queryRunner.query(`
85
- CREATE TABLE IF NOT EXISTS "batch_step_execution_context" (
86
- "step_execution_id" varchar(255) PRIMARY KEY,
87
- "data" text NOT NULL,
88
- "version" int NOT NULL DEFAULT 0
89
- )
90
- `);
91
- }
92
-
93
- public async down(queryRunner: QueryRunner): Promise<void> {
94
- await queryRunner.query(`DROP TABLE IF EXISTS "batch_step_execution_context"`);
95
- await queryRunner.query(`DROP TABLE IF EXISTS "batch_job_execution_context"`);
96
- await queryRunner.query(`DROP TABLE IF EXISTS "batch_step_execution"`);
97
- await queryRunner.query(`DROP TABLE IF EXISTS "batch_job_execution"`);
98
- await queryRunner.query(`DROP TABLE IF EXISTS "batch_job_instance"`);
99
- }
100
- }