velocious 1.0.552 → 1.0.555

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 (117) hide show
  1. package/README.md +2 -0
  2. package/build/background-jobs/status-reporter.js +15 -2
  3. package/build/background-jobs/worker.js +5 -1
  4. package/build/frontend-models/base.js +105 -47
  5. package/build/frontend-models/transport-deadline.js +66 -0
  6. package/build/src/background-jobs/status-reporter.d.ts +3 -1
  7. package/build/src/background-jobs/status-reporter.d.ts.map +1 -1
  8. package/build/src/background-jobs/status-reporter.js +16 -3
  9. package/build/src/background-jobs/worker.d.ts.map +1 -1
  10. package/build/src/background-jobs/worker.js +6 -2
  11. package/build/src/frontend-models/base.d.ts +10 -1
  12. package/build/src/frontend-models/base.d.ts.map +1 -1
  13. package/build/src/frontend-models/base.js +71 -26
  14. package/build/src/frontend-models/transport-deadline.d.ts +26 -0
  15. package/build/src/frontend-models/transport-deadline.d.ts.map +1 -0
  16. package/build/src/frontend-models/transport-deadline.js +61 -0
  17. package/build/src/testing/factory/association-declaration.d.ts +37 -0
  18. package/build/src/testing/factory/association-declaration.d.ts.map +1 -0
  19. package/build/src/testing/factory/association-declaration.js +41 -0
  20. package/build/src/testing/factory/declarations.d.ts +190 -0
  21. package/build/src/testing/factory/declarations.d.ts.map +1 -0
  22. package/build/src/testing/factory/declarations.js +98 -0
  23. package/build/src/testing/factory/definition-builder.d.ts +132 -0
  24. package/build/src/testing/factory/definition-builder.d.ts.map +1 -0
  25. package/build/src/testing/factory/definition-builder.js +417 -0
  26. package/build/src/testing/factory/errors.d.ts +62 -0
  27. package/build/src/testing/factory/errors.d.ts.map +1 -0
  28. package/build/src/testing/factory/errors.js +67 -0
  29. package/build/src/testing/factory/evaluation-context.d.ts +95 -0
  30. package/build/src/testing/factory/evaluation-context.d.ts.map +1 -0
  31. package/build/src/testing/factory/evaluation-context.js +175 -0
  32. package/build/src/testing/factory/events.d.ts +21 -0
  33. package/build/src/testing/factory/events.d.ts.map +1 -0
  34. package/build/src/testing/factory/events.js +26 -0
  35. package/build/src/testing/factory/factory-definition.d.ts +39 -0
  36. package/build/src/testing/factory/factory-definition.d.ts.map +1 -0
  37. package/build/src/testing/factory/factory-definition.js +35 -0
  38. package/build/src/testing/factory/factory-registry.d.ts +263 -0
  39. package/build/src/testing/factory/factory-registry.d.ts.map +1 -0
  40. package/build/src/testing/factory/factory-registry.js +438 -0
  41. package/build/src/testing/factory/factory-runner.d.ts +179 -0
  42. package/build/src/testing/factory/factory-runner.d.ts.map +1 -0
  43. package/build/src/testing/factory/factory-runner.js +266 -0
  44. package/build/src/testing/factory/index.d.ts +18 -0
  45. package/build/src/testing/factory/index.d.ts.map +1 -0
  46. package/build/src/testing/factory/index.js +21 -0
  47. package/build/src/testing/factory/linter.d.ts +55 -0
  48. package/build/src/testing/factory/linter.d.ts.map +1 -0
  49. package/build/src/testing/factory/linter.js +106 -0
  50. package/build/src/testing/factory/model-contract.d.ts +11 -0
  51. package/build/src/testing/factory/model-contract.d.ts.map +1 -0
  52. package/build/src/testing/factory/model-contract.js +26 -0
  53. package/build/src/testing/factory/node/load-definitions.d.ts +24 -0
  54. package/build/src/testing/factory/node/load-definitions.d.ts.map +1 -0
  55. package/build/src/testing/factory/node/load-definitions.js +87 -0
  56. package/build/src/testing/factory/sequence.d.ts +73 -0
  57. package/build/src/testing/factory/sequence.d.ts.map +1 -0
  58. package/build/src/testing/factory/sequence.js +78 -0
  59. package/build/src/testing/factory/strategies/attributes-for.d.ts +21 -0
  60. package/build/src/testing/factory/strategies/attributes-for.d.ts.map +1 -0
  61. package/build/src/testing/factory/strategies/attributes-for.js +22 -0
  62. package/build/src/testing/factory/strategies/base.d.ts +101 -0
  63. package/build/src/testing/factory/strategies/base.d.ts.map +1 -0
  64. package/build/src/testing/factory/strategies/base.js +191 -0
  65. package/build/src/testing/factory/strategies/build.d.ts +20 -0
  66. package/build/src/testing/factory/strategies/build.d.ts.map +1 -0
  67. package/build/src/testing/factory/strategies/build.js +34 -0
  68. package/build/src/testing/factory/strategies/create.d.ts +32 -0
  69. package/build/src/testing/factory/strategies/create.d.ts.map +1 -0
  70. package/build/src/testing/factory/strategies/create.js +57 -0
  71. package/build/src/testing/factory/trait-definition.d.ts +22 -0
  72. package/build/src/testing/factory/trait-definition.d.ts.map +1 -0
  73. package/build/src/testing/factory/trait-definition.js +22 -0
  74. package/build/testing/factory/association-declaration.js +50 -0
  75. package/build/testing/factory/declarations.js +110 -0
  76. package/build/testing/factory/definition-builder.js +476 -0
  77. package/build/testing/factory/errors.js +79 -0
  78. package/build/testing/factory/evaluation-context.js +199 -0
  79. package/build/testing/factory/events.js +30 -0
  80. package/build/testing/factory/factory-definition.js +41 -0
  81. package/build/testing/factory/factory-registry.js +501 -0
  82. package/build/testing/factory/factory-runner.js +296 -0
  83. package/build/testing/factory/index.js +24 -0
  84. package/build/testing/factory/linter.js +119 -0
  85. package/build/testing/factory/model-contract.js +31 -0
  86. package/build/testing/factory/node/load-definitions.js +103 -0
  87. package/build/testing/factory/sequence.js +91 -0
  88. package/build/testing/factory/strategies/attributes-for.js +24 -0
  89. package/build/testing/factory/strategies/base.js +209 -0
  90. package/build/testing/factory/strategies/build.js +42 -0
  91. package/build/testing/factory/strategies/create.js +68 -0
  92. package/build/testing/factory/trait-definition.js +24 -0
  93. package/build/tsconfig.tsbuildinfo +1 -1
  94. package/package.json +1 -1
  95. package/src/background-jobs/status-reporter.js +15 -2
  96. package/src/background-jobs/worker.js +5 -1
  97. package/src/frontend-models/base.js +105 -47
  98. package/src/frontend-models/transport-deadline.js +66 -0
  99. package/src/testing/factory/association-declaration.js +50 -0
  100. package/src/testing/factory/declarations.js +110 -0
  101. package/src/testing/factory/definition-builder.js +476 -0
  102. package/src/testing/factory/errors.js +79 -0
  103. package/src/testing/factory/evaluation-context.js +199 -0
  104. package/src/testing/factory/events.js +30 -0
  105. package/src/testing/factory/factory-definition.js +41 -0
  106. package/src/testing/factory/factory-registry.js +501 -0
  107. package/src/testing/factory/factory-runner.js +296 -0
  108. package/src/testing/factory/index.js +24 -0
  109. package/src/testing/factory/linter.js +119 -0
  110. package/src/testing/factory/model-contract.js +31 -0
  111. package/src/testing/factory/node/load-definitions.js +103 -0
  112. package/src/testing/factory/sequence.js +91 -0
  113. package/src/testing/factory/strategies/attributes-for.js +24 -0
  114. package/src/testing/factory/strategies/base.js +209 -0
  115. package/src/testing/factory/strategies/build.js +42 -0
  116. package/src/testing/factory/strategies/create.js +68 -0
  117. package/src/testing/factory/trait-definition.js +24 -0
@@ -0,0 +1,209 @@
1
+ // @ts-check
2
+
3
+ import * as inflection from "inflection"
4
+ import {assertModelClass} from "../model-contract.js"
5
+ import {ModelContractError} from "../errors.js"
6
+ import EvaluationContext from "../evaluation-context.js"
7
+
8
+ /**
9
+ * Shared behaviour for the build/create/attributesFor strategies: evaluation
10
+ * context creation, deterministic callback execution, guaranteed `afterAll`
11
+ * cleanup, record construction (default and `initializeWith`), and association
12
+ * wiring through public relationship reflection.
13
+ */
14
+ export default class BaseStrategy {
15
+ /**
16
+ * Creates an evaluation context for a plan.
17
+ * @param {import("../factory-registry.js").default} registry - Owning registry.
18
+ * @param {import("../factory-runner.js").CompiledPlan} plan - Compiled plan.
19
+ * @param {"attributesFor" | "build" | "create"} strategyName - Strategy name.
20
+ * @returns {EvaluationContext} - The context.
21
+ */
22
+ _newContext(registry, plan, strategyName) {
23
+ return new EvaluationContext({registry, plan, strategy: strategyName})
24
+ }
25
+
26
+ /**
27
+ * Builds the callback `context` object: evaluated transients exposed as plain
28
+ * properties (no Proxy) plus the named evaluator methods.
29
+ * @param {EvaluationContext} context - Evaluation context.
30
+ * @param {Record<string, ?>} transients - Evaluated transient values.
31
+ * @returns {Record<string, ?>} - The callback context.
32
+ */
33
+ _callbackContext(context, transients) {
34
+ return Object.assign({}, transients, context.contextFor([]))
35
+ }
36
+
37
+ /**
38
+ * Runs every deduped callback for an event in declaration order.
39
+ * @param {EvaluationContext} context - Evaluation context.
40
+ * @param {import("../factory-runner.js").CompiledPlan} plan - Compiled plan.
41
+ * @param {string} event - Event name (e.g. "afterCreate").
42
+ * @param {{record: ?, transients: Record<string, ?>, strategy: string}} state - Current record/transients/strategy.
43
+ * @returns {Promise<void>} - Resolves when all callbacks complete.
44
+ */
45
+ async _runCallbacks(context, plan, event, state) {
46
+ const callbacks = plan.callbacks.get(event)
47
+
48
+ if (!callbacks) return
49
+
50
+ const callbackContext = this._callbackContext(context, state.transients)
51
+
52
+ for (const callback of callbacks) {
53
+ await callback.fn({record: state.record, context: callbackContext, strategy: state.strategy})
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Runs `body`, then guarantees `afterAll` runs in `finally`. When both the body
59
+ * and cleanup fail, the body's primary error is preserved and the cleanup error
60
+ * is attached as a detail rather than masking it.
61
+ * @param {EvaluationContext} context - Evaluation context.
62
+ * @param {import("../factory-runner.js").CompiledPlan} plan - Compiled plan.
63
+ * @param {() => {record: ?, transients: Record<string, ?>, strategy: string}} state - Late-bound state accessor.
64
+ * @param {() => Promise<?>} body - The strategy body.
65
+ * @returns {Promise<?>} - The body's result.
66
+ */
67
+ async _runWithAfterAll(context, plan, state, body) {
68
+ /** @type {?} */
69
+ let result
70
+ /** @type {?} */
71
+ let primaryError
72
+ let hasPrimaryError = false
73
+
74
+ try {
75
+ result = await body()
76
+ } catch (error) {
77
+ primaryError = error
78
+ hasPrimaryError = true
79
+ }
80
+
81
+ /** @type {?} */
82
+ let cleanupError
83
+ let hasCleanupError = false
84
+
85
+ try {
86
+ await this._runCallbacks(context, plan, "afterAll", state())
87
+ } catch (error) {
88
+ cleanupError = error
89
+ hasCleanupError = true
90
+ }
91
+
92
+ if (hasPrimaryError) {
93
+ if (hasCleanupError) this._attachCleanupFailure(primaryError, cleanupError)
94
+
95
+ throw primaryError
96
+ }
97
+
98
+ if (hasCleanupError) throw cleanupError
99
+
100
+ return result
101
+ }
102
+
103
+ /**
104
+ * Attaches an afterAll cleanup failure to the primary error without masking it.
105
+ * @param {?} primaryError - The original error that will propagate.
106
+ * @param {?} cleanupError - The afterAll cleanup failure.
107
+ * @returns {void}
108
+ */
109
+ _attachCleanupFailure(primaryError, cleanupError) {
110
+ if (!primaryError || typeof primaryError !== "object" || !Object.isExtensible(primaryError)) return
111
+
112
+ if (!Array.isArray(primaryError.factoryCleanupErrors)) primaryError.factoryCleanupErrors = []
113
+ primaryError.factoryCleanupErrors.push(cleanupError)
114
+ }
115
+
116
+ /**
117
+ * Constructs a record from evaluated public attributes, honouring a custom
118
+ * `initializeWith` constructor and never assigning constructor-consumed
119
+ * attributes twice.
120
+ * @param {import("../factory-runner.js").CompiledPlan} plan - Compiled plan.
121
+ * @param {Record<string, ?>} publicAttributes - Evaluated public attributes.
122
+ * @param {EvaluationContext} context - Evaluation context.
123
+ * @param {Record<string, ?>} transients - Evaluated transients.
124
+ * @returns {Promise<?>} - The constructed record.
125
+ */
126
+ async _constructRecord(plan, publicAttributes, context, transients) {
127
+ const ModelClass = assertModelClass(plan.modelClass, plan.factoryName)
128
+
129
+ if (plan.initializeWith) {
130
+ return await this._constructWithInitializer(plan, ModelClass, publicAttributes, context, transients)
131
+ }
132
+
133
+ return new ModelClass(publicAttributes)
134
+ }
135
+
136
+ /**
137
+ * Constructs a record via a custom `initializeWith`, tracking which attributes
138
+ * the constructor consumed through its `get(name)` accessor and assigning only
139
+ * the remaining public attributes afterwards.
140
+ * @param {import("../factory-runner.js").CompiledPlan} plan - Compiled plan.
141
+ * @param {new (attributes?: Record<string, ?>) => import("../../../database/record/index.js").default} ModelClass - Validated declared model class.
142
+ * @param {Record<string, ?>} publicAttributes - Evaluated public attributes.
143
+ * @param {EvaluationContext} context - Evaluation context.
144
+ * @param {Record<string, ?>} transients - Evaluated transients.
145
+ * @returns {Promise<?>} - The constructed record.
146
+ */
147
+ async _constructWithInitializer(plan, ModelClass, publicAttributes, context, transients) {
148
+ /** @type {Set<string>} */
149
+ const consumed = new Set()
150
+ const get = (/** @type {string} */ name) => {
151
+ consumed.add(name)
152
+
153
+ return publicAttributes[name]
154
+ }
155
+ const initializeWith = /** @type {import("../declarations.js").InitializeWithDeclaration["fn"]} */ (plan.initializeWith)
156
+ const record = await initializeWith({attributes: {...publicAttributes}, get, context: this._callbackContext(context, transients)})
157
+
158
+ if (!(record instanceof ModelClass)) {
159
+ throw new ModelContractError(`Factory "${plan.factoryName}" initializeWith must return an instance of ${ModelClass.name}, got: ${String(record)}`)
160
+ }
161
+
162
+ /** @type {Record<string, ?>} */
163
+ const remaining = {}
164
+
165
+ for (const key of Object.keys(publicAttributes)) {
166
+ if (!consumed.has(key)) remaining[key] = publicAttributes[key]
167
+ }
168
+
169
+ if (Object.keys(remaining).length > 0) {
170
+ /** @type {?} */ (record).assign(remaining)
171
+ }
172
+
173
+ return record
174
+ }
175
+
176
+ /**
177
+ * Wires evaluated associations onto a record through public relationship
178
+ * reflection and generated setters (never private caches or guessed keys).
179
+ * @param {?} record - The owning record.
180
+ * @param {Array<{name: string, record: ?}>} associations - Evaluated associations.
181
+ * @returns {void}
182
+ */
183
+ _assignAssociations(record, associations) {
184
+ for (const {name, record: associatedRecord} of associations) {
185
+ const instanceRelationship = record.getRelationshipByName(name)
186
+ const relationshipType = instanceRelationship.getType()
187
+
188
+ if (relationshipType === "belongsTo") {
189
+ record[`set${inflection.camelize(name)}`](associatedRecord || null)
190
+ } else if (relationshipType === "hasOne") {
191
+ instanceRelationship.setLoaded(associatedRecord || undefined)
192
+ } else if (relationshipType === "hasMany") {
193
+ instanceRelationship.setLoaded(this._toRecordArray(associatedRecord))
194
+ }
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Normalizes a has-many association value into an array of records.
200
+ * @param {?} value - Association value (record, array, or null).
201
+ * @returns {Array<?>} - The normalized record array.
202
+ */
203
+ _toRecordArray(value) {
204
+ if (value == null) return []
205
+ if (Array.isArray(value)) return value
206
+
207
+ return [value]
208
+ }
209
+ }
@@ -0,0 +1,42 @@
1
+ // @ts-check
2
+
3
+ import BaseStrategy from "./base.js"
4
+
5
+ /**
6
+ * The `build` strategy. It recursively builds associated models (using the parent
7
+ * strategy) and constructs the root record without persisting anything. Runs the
8
+ * beforeAll/beforeBuild/afterBuild callbacks and guarantees afterAll cleanup.
9
+ */
10
+ export default class BuildStrategy extends BaseStrategy {
11
+ /**
12
+ * Runs the strategy.
13
+ * @param {object} args - Options.
14
+ * @param {import("../factory-registry.js").default} args.registry - Owning registry.
15
+ * @param {import("../factory-runner.js").CompiledPlan} args.plan - Compiled plan.
16
+ * @returns {Promise<?>} - The built (unsaved) record.
17
+ */
18
+ async run({registry, plan}) {
19
+ const context = this._newContext(registry, plan, "build")
20
+ /** @type {{record: ?, transients: Record<string, ?>}} */
21
+ const runState = {record: undefined, transients: {}}
22
+ const state = () => ({record: runState.record, transients: runState.transients, strategy: "build"})
23
+
24
+ return await this._runWithAfterAll(context, plan, state, async () => {
25
+ runState.transients = await context.resolveTransients()
26
+
27
+ await this._runCallbacks(context, plan, "beforeAll", state())
28
+ await this._runCallbacks(context, plan, "beforeBuild", state())
29
+
30
+ const {publicAttributes, transients, associations} = await context.resolveForConstruction()
31
+
32
+ const record = await this._constructRecord(plan, publicAttributes, context, transients)
33
+
34
+ this._assignAssociations(record, associations)
35
+ runState.record = record
36
+
37
+ await this._runCallbacks(context, plan, "afterBuild", state())
38
+
39
+ return record
40
+ })
41
+ }
42
+ }
@@ -0,0 +1,68 @@
1
+ // @ts-check
2
+
3
+ import BaseStrategy from "./base.js"
4
+
5
+ /**
6
+ * The `create` strategy. It builds the object graph (associations use the parent
7
+ * create strategy by default), runs beforeAll/beforeBuild/afterBuild, persists the
8
+ * root record through its native `save()` (letting Velocious own association
9
+ * autosave order and validation) or a custom `toCreate`, then runs
10
+ * beforeCreate/afterCreate and guarantees afterAll cleanup.
11
+ */
12
+ export default class CreateStrategy extends BaseStrategy {
13
+ /**
14
+ * Runs the strategy.
15
+ * @param {object} args - Options.
16
+ * @param {import("../factory-registry.js").default} args.registry - Owning registry.
17
+ * @param {import("../factory-runner.js").CompiledPlan} args.plan - Compiled plan.
18
+ * @returns {Promise<?>} - The persisted record.
19
+ */
20
+ async run({registry, plan}) {
21
+ const context = this._newContext(registry, plan, "create")
22
+ /** @type {{record: ?, transients: Record<string, ?>}} */
23
+ const runState = {record: undefined, transients: {}}
24
+ const state = () => ({record: runState.record, transients: runState.transients, strategy: "create"})
25
+
26
+ return await this._runWithAfterAll(context, plan, state, async () => {
27
+ runState.transients = await context.resolveTransients()
28
+
29
+ await this._runCallbacks(context, plan, "beforeAll", state())
30
+ await this._runCallbacks(context, plan, "beforeBuild", state())
31
+
32
+ const {publicAttributes, transients, associations} = await context.resolveForConstruction()
33
+
34
+ const record = await this._constructRecord(plan, publicAttributes, context, transients)
35
+
36
+ this._assignAssociations(record, associations)
37
+ runState.record = record
38
+
39
+ await this._runCallbacks(context, plan, "afterBuild", state())
40
+ await this._runCallbacks(context, plan, "beforeCreate", state())
41
+ await this._persist(plan, record, context, transients)
42
+ await this._runCallbacks(context, plan, "afterCreate", state())
43
+
44
+ return record
45
+ })
46
+ }
47
+
48
+ /**
49
+ * Persists the record via a custom `toCreate`, native `save()`, or not at all
50
+ * when `skipCreate` is declared.
51
+ * @param {import("../factory-runner.js").CompiledPlan} plan - Compiled plan.
52
+ * @param {?} record - The record to persist.
53
+ * @param {import("../evaluation-context.js").default} context - Evaluation context.
54
+ * @param {Record<string, ?>} transients - Evaluated transients.
55
+ * @returns {Promise<void>} - Resolves when persistence completes.
56
+ */
57
+ async _persist(plan, record, context, transients) {
58
+ if (plan.skipCreate) return
59
+
60
+ if (plan.toCreate) {
61
+ await plan.toCreate({record, context: this._callbackContext(context, transients)})
62
+
63
+ return
64
+ }
65
+
66
+ await record.save()
67
+ }
68
+ }
@@ -0,0 +1,24 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * An immutable compiled trait. A trait carries an ordered list of declarations
5
+ * (attributes, transients, associations, callbacks, custom construction, and
6
+ * base-trait inclusions) that are mixed into a factory run.
7
+ */
8
+ export default class TraitDefinition {
9
+ /**
10
+ * Builds a trait definition.
11
+ * @param {object} args - Options.
12
+ * @param {string} args.name - Trait name.
13
+ * @param {import("./declarations.js").Declaration[]} args.declarations - Ordered declarations.
14
+ */
15
+ constructor({name, declarations}) {
16
+ /** @type {string} - Trait name. */
17
+ this.name = name
18
+
19
+ /** @type {Array<import("./declarations.js").Declaration>} - Ordered declarations. */
20
+ this.declarations = /** @type {Array<import("./declarations.js").Declaration>} */ (Object.freeze([...declarations]))
21
+
22
+ Object.freeze(this)
23
+ }
24
+ }