@quadnix/octo 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/README.md +108 -21
  2. package/dist/app.type.d.ts +141 -22
  3. package/dist/app.type.js.map +1 -1
  4. package/dist/decorators/model.decorator.d.ts +3 -5
  5. package/dist/decorators/model.decorator.js +1 -6
  6. package/dist/decorators/model.decorator.js.map +1 -1
  7. package/dist/decorators/on-event.decorator.d.ts +2 -2
  8. package/dist/decorators/on-event.decorator.js.map +1 -1
  9. package/dist/decorators/resource.decorator.d.ts +50 -1
  10. package/dist/decorators/resource.decorator.js +38 -1
  11. package/dist/decorators/resource.decorator.js.map +1 -1
  12. package/dist/errors/schema.error.d.ts +8 -1
  13. package/dist/errors/schema.error.js +5 -1
  14. package/dist/errors/schema.error.js.map +1 -1
  15. package/dist/errors/terraform.error.d.ts +4 -3
  16. package/dist/errors/terraform.error.js +4 -3
  17. package/dist/errors/terraform.error.js.map +1 -1
  18. package/dist/errors/transaction.error.d.ts +32 -2
  19. package/dist/errors/transaction.error.js +22 -0
  20. package/dist/errors/transaction.error.js.map +1 -1
  21. package/dist/events/action.event.d.ts +8 -2
  22. package/dist/events/action.event.js +2 -2
  23. package/dist/events/action.event.js.map +1 -1
  24. package/dist/events/command.event.d.ts +43 -0
  25. package/dist/events/command.event.js +33 -0
  26. package/dist/events/command.event.js.map +1 -0
  27. package/dist/events/event.model.d.ts +7 -5
  28. package/dist/events/event.model.js +5 -3
  29. package/dist/events/event.model.js.map +1 -1
  30. package/dist/events/hook/commit.hook.event.d.ts +1 -1
  31. package/dist/events/hook/commit.hook.event.js.map +1 -1
  32. package/dist/events/hook/hook.event.d.ts +1 -1
  33. package/dist/events/hook/hook.event.js.map +1 -1
  34. package/dist/events/hook/model.hook.event.d.ts +1 -1
  35. package/dist/events/hook/model.hook.event.js.map +1 -1
  36. package/dist/events/hook/resource.hook.event.d.ts +1 -1
  37. package/dist/events/hook/resource.hook.event.js.map +1 -1
  38. package/dist/events/index.d.ts +2 -0
  39. package/dist/events/index.js +2 -0
  40. package/dist/events/index.js.map +1 -1
  41. package/dist/events/mode-result.event.d.ts +1 -1
  42. package/dist/events/mode-result.event.js.map +1 -1
  43. package/dist/events/mode.event.d.ts +8 -4
  44. package/dist/events/mode.event.js.map +1 -1
  45. package/dist/events/module.event.d.ts +7 -3
  46. package/dist/events/module.event.js +3 -2
  47. package/dist/events/module.event.js.map +1 -1
  48. package/dist/events/registration.event.d.ts +1 -1
  49. package/dist/events/registration.event.js.map +1 -1
  50. package/dist/events/serialization.event.d.ts +1 -1
  51. package/dist/events/serialization.event.js.map +1 -1
  52. package/dist/events/state.event.d.ts +102 -0
  53. package/dist/events/state.event.js +69 -0
  54. package/dist/events/state.event.js.map +1 -0
  55. package/dist/events/terraform.event.d.ts +37 -1
  56. package/dist/events/terraform.event.js +24 -0
  57. package/dist/events/terraform.event.js.map +1 -1
  58. package/dist/events/transaction.event.d.ts +3 -3
  59. package/dist/events/transaction.event.js +1 -1
  60. package/dist/events/transaction.event.js.map +1 -1
  61. package/dist/functions/container/container.js +1 -1
  62. package/dist/functions/container/container.js.map +1 -1
  63. package/dist/functions/container/test-container.js +2 -2
  64. package/dist/functions/container/test-container.js.map +1 -1
  65. package/dist/functions/dependency/dependency.d.ts +9 -22
  66. package/dist/functions/dependency/dependency.js +5 -1
  67. package/dist/functions/dependency/dependency.js.map +1 -1
  68. package/dist/functions/diff/diff.d.ts +14 -7
  69. package/dist/functions/diff/diff.js.map +1 -1
  70. package/dist/functions/node/node-action.abstract.d.ts +8 -10
  71. package/dist/functions/node/node-action.abstract.js +10 -16
  72. package/dist/functions/node/node-action.abstract.js.map +1 -1
  73. package/dist/functions/node/node.abstract.js +21 -10
  74. package/dist/functions/node/node.abstract.js.map +1 -1
  75. package/dist/functions/node/node.interface.d.ts +2 -1
  76. package/dist/functions/schema/schema.js +1 -1
  77. package/dist/functions/schema/schema.js.map +1 -1
  78. package/dist/index.d.ts +8 -6
  79. package/dist/index.js +6 -4
  80. package/dist/index.js.map +1 -1
  81. package/dist/index.type.d.ts +5 -0
  82. package/dist/index.type.js +2 -0
  83. package/dist/index.type.js.map +1 -0
  84. package/dist/main.d.ts +4 -3
  85. package/dist/main.js +27 -11
  86. package/dist/main.js.map +1 -1
  87. package/dist/models/account/account.model.d.ts +6 -0
  88. package/dist/models/account/account.model.js +16 -0
  89. package/dist/models/account/account.model.js.map +1 -1
  90. package/dist/models/app/app.model.d.ts +0 -10
  91. package/dist/models/app/app.model.js +0 -30
  92. package/dist/models/app/app.model.js.map +1 -1
  93. package/dist/models/deployment/deployment.schema.d.ts +4 -0
  94. package/dist/models/deployment/deployment.schema.js +4 -0
  95. package/dist/models/deployment/deployment.schema.js.map +1 -1
  96. package/dist/models/environment/environment.model.d.ts +2 -2
  97. package/dist/models/environment/environment.model.js +2 -2
  98. package/dist/models/execution/execution.model.d.ts +1 -1
  99. package/dist/models/execution/execution.model.js +1 -1
  100. package/dist/models/image/image.model.d.ts +4 -3
  101. package/dist/models/image/image.model.js +7 -6
  102. package/dist/models/image/image.model.js.map +1 -1
  103. package/dist/models/model.abstract.js +1 -1
  104. package/dist/models/model.abstract.js.map +1 -1
  105. package/dist/models/pipeline/pipeline.model.d.ts +1 -1
  106. package/dist/models/pipeline/pipeline.model.js +1 -1
  107. package/dist/models/server/server.model.d.ts +2 -2
  108. package/dist/models/server/server.model.js +2 -2
  109. package/dist/models/service/service.model.d.ts +28 -11
  110. package/dist/models/service/service.model.js +56 -16
  111. package/dist/models/service/service.model.js.map +1 -1
  112. package/dist/models/service/service.schema.d.ts +46 -2
  113. package/dist/models/service/service.schema.js +54 -2
  114. package/dist/models/service/service.schema.js.map +1 -1
  115. package/dist/modes/commit.mode.d.ts +2 -2
  116. package/dist/modes/commit.mode.js +2 -2
  117. package/dist/modes/generate.mode.d.ts +2 -1
  118. package/dist/modes/generate.mode.js +3 -2
  119. package/dist/modes/generate.mode.js.map +1 -1
  120. package/dist/modes/run-action.mode.d.ts +1 -1
  121. package/dist/modes/run-action.mode.js +1 -1
  122. package/dist/modes/validate.mode.d.ts +24 -0
  123. package/dist/modes/validate.mode.js +89 -11
  124. package/dist/modes/validate.mode.js.map +1 -1
  125. package/dist/modules/module.container.d.ts +7 -1
  126. package/dist/modules/module.container.js +41 -5
  127. package/dist/modules/module.container.js.map +1 -1
  128. package/dist/modules/test-module.container.js +35 -83
  129. package/dist/modules/test-module.container.js.map +1 -1
  130. package/dist/overlays/overlay.abstract.js +1 -1
  131. package/dist/overlays/overlay.abstract.js.map +1 -1
  132. package/dist/resources/resource-action.interface.d.ts +4 -2
  133. package/dist/resources/resource.abstract.d.ts +50 -8
  134. package/dist/resources/resource.abstract.js +63 -32
  135. package/dist/resources/resource.abstract.js.map +1 -1
  136. package/dist/resources/resource.interface.d.ts +0 -2
  137. package/dist/resources/resource.schema.d.ts +11 -1
  138. package/dist/resources/resource.schema.js +11 -1
  139. package/dist/resources/resource.schema.js.map +1 -1
  140. package/dist/services/event/event.service.d.ts +2 -2
  141. package/dist/services/event/event.service.js.map +1 -1
  142. package/dist/services/input/input.service.d.ts +30 -2
  143. package/dist/services/input/input.service.js +71 -4
  144. package/dist/services/input/input.service.js.map +1 -1
  145. package/dist/services/serialization/model/model-serialization.service.d.ts +4 -6
  146. package/dist/services/serialization/model/model-serialization.service.js +20 -17
  147. package/dist/services/serialization/model/model-serialization.service.js.map +1 -1
  148. package/dist/services/serialization/resource/resource-serialization.service.d.ts +7 -6
  149. package/dist/services/serialization/resource/resource-serialization.service.js +25 -12
  150. package/dist/services/serialization/resource/resource-serialization.service.js.map +1 -1
  151. package/dist/services/state-management/local.state-provider.d.ts +25 -9
  152. package/dist/services/state-management/local.state-provider.js +127 -21
  153. package/dist/services/state-management/local.state-provider.js.map +1 -1
  154. package/dist/services/state-management/remote.state-provider.d.ts +216 -0
  155. package/dist/services/state-management/remote.state-provider.js +645 -0
  156. package/dist/services/state-management/remote.state-provider.js.map +1 -0
  157. package/dist/services/state-management/state-management.service.d.ts +83 -40
  158. package/dist/services/state-management/state-management.service.js +130 -26
  159. package/dist/services/state-management/state-management.service.js.map +1 -1
  160. package/dist/services/state-management/state-provider.interface.d.ts +50 -9
  161. package/dist/services/state-management/state-provider.interface.js +14 -1
  162. package/dist/services/state-management/state-provider.interface.js.map +1 -1
  163. package/dist/services/state-management/terraform-state.service.d.ts +32 -9
  164. package/dist/services/state-management/terraform-state.service.js +64 -21
  165. package/dist/services/state-management/terraform-state.service.js.map +1 -1
  166. package/dist/services/state-management/test.state-provider.d.ts +11 -10
  167. package/dist/services/state-management/test.state-provider.js +35 -14
  168. package/dist/services/state-management/test.state-provider.js.map +1 -1
  169. package/dist/services/terraform/terraform.service.d.ts +57 -3
  170. package/dist/services/terraform/terraform.service.js +95 -8
  171. package/dist/services/terraform/terraform.service.js.map +1 -1
  172. package/dist/services/terraform/terraform.type.js +1 -1
  173. package/dist/services/terraform/terraform.type.js.map +1 -1
  174. package/dist/services/transaction/transaction.service.d.ts +29 -5
  175. package/dist/services/transaction/transaction.service.js +198 -34
  176. package/dist/services/transaction/transaction.service.js.map +1 -1
  177. package/dist/services/validation/validation.service.d.ts +2 -2
  178. package/dist/{functions → utilities}/diff/diff.utility.d.ts +2 -1
  179. package/dist/{functions → utilities}/diff/diff.utility.js +4 -3
  180. package/dist/utilities/diff/diff.utility.js.map +1 -0
  181. package/dist/utilities/file/file.utility.d.ts +6 -7
  182. package/dist/utilities/file/file.utility.js +13 -68
  183. package/dist/utilities/file/file.utility.js.map +1 -1
  184. package/dist/utilities/hcl/hcl.utility.d.ts +83 -16
  185. package/dist/utilities/hcl/hcl.utility.js +200 -27
  186. package/dist/utilities/hcl/hcl.utility.js.map +1 -1
  187. package/dist/utilities/http/http.utility.d.ts +57 -0
  188. package/dist/utilities/http/http.utility.js +98 -0
  189. package/dist/utilities/http/http.utility.js.map +1 -0
  190. package/dist/utilities/string/string.utility.d.ts +23 -3
  191. package/dist/utilities/string/string.utility.js +29 -3
  192. package/dist/utilities/string/string.utility.js.map +1 -1
  193. package/dist/utilities/terraform/terraform.utility.d.ts +70 -21
  194. package/dist/utilities/terraform/terraform.utility.js +255 -66
  195. package/dist/utilities/terraform/terraform.utility.js.map +1 -1
  196. package/dist/utilities/test-helpers/test-classes.d.ts +19 -0
  197. package/dist/utilities/test-helpers/test-classes.js +22 -0
  198. package/dist/utilities/test-helpers/test-classes.js.map +1 -1
  199. package/dist/utilities/test-helpers/test-models.d.ts +8 -1
  200. package/dist/utilities/test-helpers/test-models.js +25 -12
  201. package/dist/utilities/test-helpers/test-models.js.map +1 -1
  202. package/dist/utilities/test-helpers/test-modes.d.ts +1 -1
  203. package/dist/utilities/test-helpers/test-modes.js +36 -13
  204. package/dist/utilities/test-helpers/test-modes.js.map +1 -1
  205. package/dist/utilities/test-helpers/test-modules.d.ts +6 -0
  206. package/dist/utilities/test-helpers/test-modules.js +6 -0
  207. package/dist/utilities/test-helpers/test-modules.js.map +1 -1
  208. package/dist/utilities/test-helpers/test-overlays.d.ts +3 -1
  209. package/dist/utilities/test-helpers/test-overlays.js +8 -2
  210. package/dist/utilities/test-helpers/test-overlays.js.map +1 -1
  211. package/dist/utilities/test-helpers/test-resources.d.ts +4 -2
  212. package/dist/utilities/test-helpers/test-resources.js +22 -20
  213. package/dist/utilities/test-helpers/test-resources.js.map +1 -1
  214. package/package.json +7 -2
  215. package/dist/functions/diff/diff.utility.js.map +0 -1
  216. package/dist/services/state-management/file-app-lock.utility.d.ts +0 -43
  217. package/dist/services/state-management/file-app-lock.utility.js +0 -124
  218. package/dist/services/state-management/file-app-lock.utility.js.map +0 -1
  219. package/dist/services/state-management/local-encryption.state-provider.d.ts +0 -71
  220. package/dist/services/state-management/local-encryption.state-provider.js +0 -116
  221. package/dist/services/state-management/local-encryption.state-provider.js.map +0 -1
@@ -0,0 +1,216 @@
1
+ import { type IStateProvider } from './state-provider.interface.js';
2
+ /**
3
+ * Keeps state in Octo's managed backend, so a team shares one store and no machine holds the only copy.
4
+ *
5
+ * A run reads and writes a state directory on disk, exactly as {@link LocalStateProvider} does, and
6
+ * the backend is kept in step with it:
7
+ *
8
+ * - {@link RemoteStateProvider.refreshState} brings the directory up to the newest sealed state, and
9
+ * is what `octo generate` plans against.
10
+ * - {@link RemoteStateProvider.lockApp} opens a new record for the run, refusing when the newest
11
+ * sealed state has moved since the directory was refreshed.
12
+ * - Every write under the lock lands in the directory and in the run's record before it returns, so
13
+ * a run that dies leaves a record somebody can finish from another machine.
14
+ * - {@link RemoteStateProvider.unlockApp} seals the record and releases the lock.
15
+ *
16
+ * Two files of the provider's own sit beside the state files and are never uploaded:
17
+ * `remote-state.lock` records the run the directory belongs to and is deleted when the lock is
18
+ * released, and `remote-state-upload.lock` makes the octo processes of one machine write to the
19
+ * backend one at a time and is deleted after every write.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * await octo.initialize(
24
+ * new RemoteStateProvider({
25
+ * appId: 'my-app',
26
+ * localStateDirectoryPath: './.octo',
27
+ * organizationId: 'my-organization',
28
+ * sessionId: process.env.OCTO_SESSION!,
29
+ * stateManagementServiceUrl: 'http://localhost:3619/state',
30
+ * }),
31
+ * );
32
+ * ```
33
+ *
34
+ * @group Services/State Management
35
+ *
36
+ * @see Techniques for [State Providers](/docs/techniques/state-providers).
37
+ */
38
+ export declare class RemoteStateProvider implements IStateProvider {
39
+ private readonly appId;
40
+ private readonly localStateDirectoryPath;
41
+ private readonly localStateProvider;
42
+ private readonly lockContext;
43
+ private readonly organizationId;
44
+ private readonly sessionId;
45
+ private readonly stateManagementServiceUrl;
46
+ /**
47
+ * @param options `organizationId` and `appId` name the app whose state this is, and `sessionId`
48
+ * is the session the run acts as. `stateManagementServiceUrl` is the backend service, including
49
+ * the prefix its routes sit behind. `localStateDirectoryPath` is the directory state files are
50
+ * written into. `lockContext` is what a blocked run is shown about the holder.
51
+ */
52
+ constructor(options: {
53
+ appId: string;
54
+ localStateDirectoryPath: string;
55
+ lockContext?: string;
56
+ organizationId: string;
57
+ sessionId: string;
58
+ stateManagementServiceUrl: string;
59
+ });
60
+ /**
61
+ * The id of the lock currently held, or `undefined` when the app is unlocked.
62
+ */
63
+ getAppLock(): Promise<string | undefined>;
64
+ /**
65
+ * Reads a state file.
66
+ *
67
+ * @throws {@link TransactionError} With the message `No state found!` when the file does not exist.
68
+ */
69
+ getState(stateFileName: string): Promise<Buffer>;
70
+ /**
71
+ * Whether `lockId` is the lock currently held.
72
+ *
73
+ * @throws {@link TransactionError} With the message `Invalid lock ID!` when a different lock is held,
74
+ * and when `lockId` is held but was neither taken nor resumed in this state directory.
75
+ */
76
+ isAppLocked(lockId: string): Promise<boolean>;
77
+ /**
78
+ * The names of the state files whose name starts with `prefix`.
79
+ */
80
+ listStates(prefix: string): Promise<string[]>;
81
+ /**
82
+ * Takes the app lock and opens the run's record with everything the state directory holds.
83
+ *
84
+ * With `lockId`, resumes the run already holding that lock instead - from any machine, as the
85
+ * account that took it or an organization administrator. The state directory is replaced with
86
+ * what that run wrote so far, its journal included, keeping this machine's journal files, which join
87
+ * the record, and every write after lands in its record. A run that wrote nothing is resumed from the
88
+ * newest sealed state, without that state's journal.
89
+ *
90
+ * @throws {@link AppLockError} When a lock is held, naming the id that holds it. When the newest
91
+ * sealed state moved since {@link RemoteStateProvider.refreshState} last ran. When `lockId` is
92
+ * given and is not held.
93
+ */
94
+ lockApp(lockId?: string): ReturnType<IStateProvider['lockApp']>;
95
+ /**
96
+ * Brings the state directory up to the newest sealed state, which is what a run plans against.
97
+ *
98
+ * A directory an open run is still writing is left alone. A directory the backend holds nothing
99
+ * for is left alone too: that is a first run, or a project arriving from another provider, and the
100
+ * files on disk are the only copy there is. Journal files are neither replaced nor taken.
101
+ */
102
+ refreshState(): Promise<{
103
+ commitId?: string;
104
+ previousCommitId?: string;
105
+ replaced: boolean;
106
+ }>;
107
+ /**
108
+ * Writes state files, deleting each one given no `data`, and then writes the run's record once for
109
+ * all of them when a run is open. A name holding `/` is the machine's own: it is written beneath
110
+ * the state directory and never reaches the record.
111
+ *
112
+ * @throws {@link TransactionError} When the backend would refuse a file's name, before anything is
113
+ * written. When the backend refuses the write, which is where the limits it holds the app to apply.
114
+ */
115
+ saveStates(stateFiles: Parameters<IStateProvider['saveStates']>[0]): Promise<void>;
116
+ /**
117
+ * Writes the directory into the run's record, seals it, and releases the app lock. Unlocking an app
118
+ * that is not locked is not an error.
119
+ *
120
+ * A write that failed earlier left the directory ahead of the record, so the record is written once
121
+ * more before it is sealed: a sealed record is what every machine plans against next.
122
+ *
123
+ * @throws {@link TransactionError} With the message `Invalid lock ID!` when a different lock is held.
124
+ * When the directory cannot be written into the record, keeping the lock held.
125
+ */
126
+ unlockApp(lockId: string): Promise<void>;
127
+ /**
128
+ * The route this app's state and locks are read and written at.
129
+ */
130
+ private appStateUrl;
131
+ /**
132
+ * The session this run acts as. Every route but the health check is behind it.
133
+ */
134
+ private authorizationHeader;
135
+ /**
136
+ * Deletes the lock file. Deleting one that is not there is not an error.
137
+ */
138
+ private deleteLockFile;
139
+ /**
140
+ * How long the lock has been held, as a short readable span. Undefined when the recorded time
141
+ * cannot be read as one, so a broken value is left out rather than reported as a negative age.
142
+ */
143
+ private describeAge;
144
+ /**
145
+ * Writes the directory into the run's record, when this directory belongs to an open run.
146
+ */
147
+ private pushRun;
148
+ /**
149
+ * Every lock the backend holds on this app, oldest first.
150
+ */
151
+ private readAppLocks;
152
+ /**
153
+ * The lock file. Empty when there is none.
154
+ */
155
+ private readLockFile;
156
+ /**
157
+ * The newest record the app holds, sealed or not, or `undefined` when it holds none.
158
+ *
159
+ * One run holds the whole app at a time, so records are opened in the order they are sealed.
160
+ */
161
+ private readNewestState;
162
+ /**
163
+ * Releases a lock, sealing the record its commit names. False when the lock is not held.
164
+ */
165
+ private releaseAppLock;
166
+ /**
167
+ * Replaces every state file in the directory with the ones a state body names, downloading each
168
+ * from its URL and checking it against its SHA-256, and returns the body. The directory's journal
169
+ * files are kept; with `takeJournal`, the body's are written beside them.
170
+ *
171
+ * Each file is streamed to a temporary file beside it, so a state is not bounded by memory.
172
+ */
173
+ private replaceStateFiles;
174
+ /**
175
+ * Resumes the run holding `lockId` in this state directory.
176
+ */
177
+ private resumeAppLock;
178
+ /**
179
+ * Every state file the directory holds, which is every file in it but the provider's two lock
180
+ * files and a write still in progress, and none of the directories a run writes beside them, such
181
+ * as a generated terragrunt folder.
182
+ */
183
+ private stateFileNames;
184
+ /**
185
+ * Runs `body` while holding `remote-state-upload.lock`, so one write to the backend is in flight at
186
+ * a time across every octo process on this machine. The file is deleted when `body` returns, and
187
+ * one left by a process that is gone is taken over.
188
+ */
189
+ private withUploadLock;
190
+ /**
191
+ * Writes the lock file.
192
+ */
193
+ private writeLockFile;
194
+ /**
195
+ * Writes the directory into the run's record as it stands, and returns the SHA-256 of every file the
196
+ * record holds afterwards.
197
+ *
198
+ * A write is three requests: the upload is asked for naming every file and its digest, the files
199
+ * the backend does not already hold are PUT to the URLs it answered with, and the upload is
200
+ * claimed. A file that fails to upload stops the write before the claim, and a claim with a file
201
+ * missing is refused, so a write lands whole or not at all. An upload left unclaimed is thrown away by
202
+ * the backend at the next write or when the lock is released. A directory holding nothing writes
203
+ * nothing, since the backend holds no empty state.
204
+ *
205
+ * The files are hard linked into a snapshot directory first and streamed from there, so a state is
206
+ * not bounded by memory, and a file another process replaces meanwhile is uploaded as it was
207
+ * hashed: a write replaces a file by renaming over it, which leaves the linked one as it was.
208
+ */
209
+ private writeRunState;
210
+ /**
211
+ * Writes one file into the state directory whole, from `data` or copied from `filePath`, so a process
212
+ * reading it never reads half of it. Replacing the file rather than writing into it is also what lets
213
+ * an upload freeze it with a hard link.
214
+ */
215
+ private writeStateFile;
216
+ }