@tstdl/base 0.93.198 → 0.93.200
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/orm/schemas/json.js +3 -2
- package/orm/server/drizzle/schema-converter.js +2 -1
- package/package.json +3 -3
- package/reflection/registry.d.ts +2 -2
- package/reflection/utils.js +4 -4
- package/schema/schemas/function.js +1 -1
- package/schema/schemas/object.js +1 -1
- package/task-queue/postgres/task-queue.d.ts +3 -0
- package/task-queue/postgres/task-queue.js +66 -17
- package/task-queue/task-context.d.ts +2 -0
- package/task-queue/task-context.js +3 -3
- package/task-queue/task-queue.d.ts +14 -5
- package/task-queue/task-queue.js +7 -5
- package/task-queue/types.d.ts +1 -0
- package/test2.js +25 -7
package/orm/schemas/json.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createDecorator } from '../../reflection/index.js';
|
|
2
2
|
import { any, Class, object, Property, Schema, schemaTestableToSchema } from '../../schema/index.js';
|
|
3
|
-
import { isDefined } from '../../utils/type-guards.js';
|
|
3
|
+
import { isDefined, isUndefined } from '../../utils/type-guards.js';
|
|
4
4
|
export class JsonSchema extends Schema {
|
|
5
5
|
name = 'Json';
|
|
6
6
|
innerSchema;
|
|
@@ -21,6 +21,7 @@ export function JsonProperty(options) {
|
|
|
21
21
|
if (data.type == 'class') {
|
|
22
22
|
return Class({ schema: json({ schema: schema ?? object({}, { unknownPropertiesKey: any(), unknownProperties: any(), factory: { type: data.constructor } }) }) })(args[0]);
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
const propertyType = metadata.type;
|
|
25
|
+
return Property(json({ schema: schema ?? object({}, { unknownPropertiesKey: any(), unknownProperties: any(), factory: isUndefined(propertyType) ? undefined : { type: propertyType } }) }), optionsRest)(args[0], args[1], args[2]);
|
|
25
26
|
});
|
|
26
27
|
}
|
|
@@ -379,7 +379,8 @@ function getPostgresColumnEntries(type, dbSchema, tableName, path = new JsonPath
|
|
|
379
379
|
assertDefined(propertyMetadata, `Property "${property}" of type "${type.name}" does not have reflection metadata (path: ${path.toString()}).`);
|
|
380
380
|
const propertyPrefix = columnReflectionData?.embedded?.prefix;
|
|
381
381
|
const nestedPrefix = [prefix, isNull(propertyPrefix) ? '' : propertyPrefix ?? `${columnName}_`].join('');
|
|
382
|
-
|
|
382
|
+
const entryType = columnReflectionData?.embedded?.type ?? assertDefinedPass(propertyMetadata.type, 'Missing type for embedded property.');
|
|
383
|
+
return getPostgresColumnEntries(entryType, dbSchema, tableName, path.add(property), nestedPrefix, { ...options, inherited, table }, rootType);
|
|
383
384
|
}
|
|
384
385
|
const encrypted = columnReflectionData?.encrypted == true;
|
|
385
386
|
const unwrappedSchemaIsUint8Array = isInstanceOf(unwrapSchema(schema).schema, Uint8ArraySchema);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tstdl/base",
|
|
3
|
-
"version": "0.93.
|
|
3
|
+
"version": "0.93.200",
|
|
4
4
|
"author": "Patrick Hein",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -153,8 +153,8 @@
|
|
|
153
153
|
"type-fest": "^5.5"
|
|
154
154
|
},
|
|
155
155
|
"peerDependencies": {
|
|
156
|
-
"@aws-sdk/client-s3": "
|
|
157
|
-
"@aws-sdk/s3-request-presigner": "
|
|
156
|
+
"@aws-sdk/client-s3": "3",
|
|
157
|
+
"@aws-sdk/s3-request-presigner": "3",
|
|
158
158
|
"@genkit-ai/google-genai": "^1.31",
|
|
159
159
|
"@google-cloud/storage": "^7.19",
|
|
160
160
|
"@toon-format/toon": "^2.1.0",
|
package/reflection/registry.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export type TypeMetadata = MetadataBase<'type'> & {
|
|
|
19
19
|
};
|
|
20
20
|
export type PropertyMetadata = MetadataBase<'property'> & {
|
|
21
21
|
key: string | symbol;
|
|
22
|
-
type: AbstractConstructor;
|
|
22
|
+
type: AbstractConstructor | undefined;
|
|
23
23
|
isAccessor: boolean;
|
|
24
24
|
data: ContextDataMap;
|
|
25
25
|
inherited: boolean;
|
|
@@ -36,7 +36,7 @@ export type ConstructorParameterMetadata = MetadataBase<'constructor-parameter'>
|
|
|
36
36
|
data: ContextDataMap;
|
|
37
37
|
};
|
|
38
38
|
export type MethodParameterMetadata = MetadataBase<'method-parameter'> & {
|
|
39
|
-
type: AbstractConstructor;
|
|
39
|
+
type: AbstractConstructor | undefined;
|
|
40
40
|
index: number;
|
|
41
41
|
data: ContextDataMap;
|
|
42
42
|
};
|
package/reflection/utils.js
CHANGED
|
@@ -85,20 +85,20 @@ export function getTypeInfoString(type) {
|
|
|
85
85
|
lines.push(`${type.name}(${constructorParameters})`);
|
|
86
86
|
for (const [key, propertyMetadata] of (metadata?.staticProperties ?? [])) {
|
|
87
87
|
const propertyKey = isSymbol(key) ? `[${key.toString()}]` : key;
|
|
88
|
-
lines.push(` static ${propertyKey}: ${propertyMetadata.type
|
|
88
|
+
lines.push(` static ${propertyKey}: ${propertyMetadata.type?.name ?? '<unknown>'}`);
|
|
89
89
|
}
|
|
90
90
|
for (const [key, methodMetadata] of (metadata?.staticMethods ?? [])) {
|
|
91
91
|
const propertyKey = isSymbol(key) ? `[${key.toString()}]` : key;
|
|
92
|
-
const parameters = methodMetadata.parameters.map((parameter) => parameter.type
|
|
92
|
+
const parameters = methodMetadata.parameters.map((parameter) => parameter.type?.name ?? '<unknown>').join(', ');
|
|
93
93
|
lines.push(` static ${propertyKey}(${parameters}): ${methodMetadata.returnType?.name ?? 'void'}`);
|
|
94
94
|
}
|
|
95
95
|
for (const [key, propertyMetadata] of (metadata?.properties ?? [])) {
|
|
96
96
|
const propertyKey = isSymbol(key) ? `[${key.toString()}]` : key;
|
|
97
|
-
lines.push(` ${propertyKey}: ${propertyMetadata.type
|
|
97
|
+
lines.push(` ${propertyKey}: ${propertyMetadata.type?.name ?? '<unknown>'}`);
|
|
98
98
|
}
|
|
99
99
|
for (const [key, methodMetadata] of (metadata?.methods ?? [])) {
|
|
100
100
|
const propertyKey = isSymbol(key) ? `[${key.toString()}]` : key;
|
|
101
|
-
const parameters = methodMetadata.parameters.map((parameter) => parameter.type
|
|
101
|
+
const parameters = methodMetadata.parameters.map((parameter) => parameter.type?.name ?? '<unknown>').join(', ');
|
|
102
102
|
lines.push(` ${propertyKey}(${parameters}): ${methodMetadata.returnType?.name ?? 'void'}`);
|
|
103
103
|
}
|
|
104
104
|
return lines.join('\n');
|
|
@@ -56,7 +56,7 @@ export function tryGetFunctionSchemaFromReflection(type, method) {
|
|
|
56
56
|
}
|
|
57
57
|
const parameterSchemas = methodMetadata.parameters.map((parameterMetadata) => {
|
|
58
58
|
const parameterData = parameterMetadata.data.tryGet('schema') ?? {};
|
|
59
|
-
return schemaReflectionDataToSchema(parameterData, parameterMetadata.type, { type, key: `${String(method)}(parameter:${parameterMetadata.index})` });
|
|
59
|
+
return schemaReflectionDataToSchema(parameterData, parameterMetadata.type ?? null, { type, key: `${String(method)}(parameter:${parameterMetadata.index})` });
|
|
60
60
|
});
|
|
61
61
|
const parameterNames = methodMetadata.parameters.map((parameterMetadata) => {
|
|
62
62
|
const parameterData = parameterMetadata.data.tryGet('schema') ?? {};
|
package/schema/schemas/object.js
CHANGED
|
@@ -219,7 +219,7 @@ function getObjectSchemaPropertiesFromReflection(metadata, type) {
|
|
|
219
219
|
const properties = {};
|
|
220
220
|
for (const [key, propertyMetadata] of metadata.properties) {
|
|
221
221
|
const reflectionData = propertyMetadata.data.tryGet('schema');
|
|
222
|
-
const propertySchema = schemaReflectionDataToSchema(reflectionData, propertyMetadata.type, { type, key });
|
|
222
|
+
const propertySchema = schemaReflectionDataToSchema(reflectionData, propertyMetadata.type ?? null, { type, key });
|
|
223
223
|
properties[key] = propertySchema;
|
|
224
224
|
}
|
|
225
225
|
for (const [key] of metadata.methods) {
|
|
@@ -77,12 +77,15 @@ export declare class PostgresTaskQueue<Definitions extends TaskDefinitionMap = T
|
|
|
77
77
|
transaction?: Transaction;
|
|
78
78
|
}): Promise<TaskOfType<Definitions, Type>[]>;
|
|
79
79
|
reschedule(id: string, timestamp: number, options?: {
|
|
80
|
+
refundRateLimit?: boolean;
|
|
80
81
|
transaction?: Transaction;
|
|
81
82
|
}): Promise<void>;
|
|
82
83
|
rescheduleMany(ids: string[], timestamp: number, options?: {
|
|
84
|
+
refundRateLimit?: boolean;
|
|
83
85
|
transaction?: Transaction;
|
|
84
86
|
}): Promise<void>;
|
|
85
87
|
rescheduleManyByTags(tags: OneOrMany<string>, timestamp: number, options?: {
|
|
88
|
+
refundRateLimit?: boolean;
|
|
86
89
|
transaction?: Transaction;
|
|
87
90
|
}): Promise<void>;
|
|
88
91
|
touch<Type extends TaskTypes<Definitions>>(task: TaskOfType<Definitions, Type>, options?: {
|
|
@@ -187,6 +187,7 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
187
187
|
},
|
|
188
188
|
}));
|
|
189
189
|
const itemsWithIdempotency = entitiesWithIndex.filter((e) => isNotNull(e.entity.idempotencyKey));
|
|
190
|
+
itemsWithIdempotency.sort((a, b) => a.entity.idempotencyKey.localeCompare(b.entity.idempotencyKey));
|
|
190
191
|
const itemsWithoutIdempotency = entitiesWithIndex.filter((e) => isNull(e.entity.idempotencyKey));
|
|
191
192
|
const hasDependencies = itemsWithDistinctDependencies.some((item) => ((item.scheduleAfter?.length ?? 0) > 0) || ((item.completeAfter?.length ?? 0) > 0) || (isDefined(item.parentId) && (item.parentRequires != false) && !(isArray(item.parentRequires) && (item.parentRequires.length == 0))));
|
|
192
193
|
const mustUseTransaction = (entitiesWithIndex.length > 1) || hasDependencies;
|
|
@@ -382,7 +383,6 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
382
383
|
decrementsToApply.set(dep.taskId, current);
|
|
383
384
|
}
|
|
384
385
|
const values = [...decrementsToApply]
|
|
385
|
-
.toSorted(([idA], [idB]) => idA.localeCompare(idB))
|
|
386
386
|
.map(([taskId, d]) => sql `(${taskId}::uuid, ${d.schedule}::int, ${d.complete}::int)`);
|
|
387
387
|
const updates = session.$with('updates').as((qb) => qb
|
|
388
388
|
.select({
|
|
@@ -391,8 +391,14 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
391
391
|
completeIncrement: sql `(complete)::int`.as('complete_increment'),
|
|
392
392
|
})
|
|
393
393
|
.from(sql `(VALUES ${sql.join(values, sql `, `)}) AS t(id, schedule, complete)`));
|
|
394
|
+
const locked = session.$with('locked').as((qb) => qb
|
|
395
|
+
.select({ id: taskTable.id })
|
|
396
|
+
.from(taskTable)
|
|
397
|
+
.innerJoin(updates, eq(taskTable.id, updates.taskId))
|
|
398
|
+
.orderBy(asc(taskTable.id))
|
|
399
|
+
.for('update'));
|
|
394
400
|
const updatedRows = await session
|
|
395
|
-
.with(updates)
|
|
401
|
+
.with(updates, locked)
|
|
396
402
|
.update(taskTable)
|
|
397
403
|
.set({
|
|
398
404
|
unresolvedScheduleDependencies: sql `${taskTable.unresolvedScheduleDependencies} + ${updates.scheduleIncrement}`,
|
|
@@ -400,7 +406,7 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
400
406
|
status: caseWhen(and(eq(taskTable.status, TaskStatus.Pending), gt(sql `${taskTable.unresolvedScheduleDependencies} + ${updates.scheduleIncrement}`, 0)), TaskStatus.Waiting).else(taskTable.status),
|
|
401
407
|
})
|
|
402
408
|
.from(updates)
|
|
403
|
-
.where(eq(taskTable.id, updates.taskId))
|
|
409
|
+
.where(and(eq(taskTable.id, updates.taskId), inArray(taskTable.id, session.select({ id: locked.id }).from(locked))))
|
|
404
410
|
.returning({ id: taskTable.id, status: taskTable.status, namespace: taskTable.namespace });
|
|
405
411
|
const notifiedNamespaces = new Set();
|
|
406
412
|
for (const row of updatedRows) {
|
|
@@ -685,7 +691,7 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
685
691
|
}
|
|
686
692
|
async rescheduleMany(ids, timestamp, options) {
|
|
687
693
|
const session = options?.transaction?.pgTransaction ?? this.#repository.session;
|
|
688
|
-
await session
|
|
694
|
+
const result = await session
|
|
689
695
|
.update(taskTable)
|
|
690
696
|
.set({
|
|
691
697
|
status: caseWhen(gt(taskTable.unresolvedScheduleDependencies, 0), enumValue(taskStatus, TaskStatus.Waiting)).else(enumValue(taskStatus, TaskStatus.Pending)),
|
|
@@ -694,11 +700,20 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
694
700
|
visibilityDeadline: null,
|
|
695
701
|
tries: caseWhen(eq(taskTable.status, enumValue(taskStatus, TaskStatus.Running)), greatest(0, sql `${taskTable.tries} - 1`)).else(taskTable.tries),
|
|
696
702
|
})
|
|
697
|
-
.where(and(inArray(taskTable.id, ids), notInArray(taskTable.status, terminalStatuses)))
|
|
703
|
+
.where(and(inArray(taskTable.id, ids), notInArray(taskTable.status, terminalStatuses)))
|
|
704
|
+
.returning({ id: taskTable.id });
|
|
705
|
+
if ((result.length > 0) && (options?.refundRateLimit == true)) {
|
|
706
|
+
try {
|
|
707
|
+
await this.#rateLimiter.refund(this.#namespace, result.length);
|
|
708
|
+
}
|
|
709
|
+
catch (error) {
|
|
710
|
+
this.#logger.error(error);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
698
713
|
}
|
|
699
714
|
async rescheduleManyByTags(tags, timestamp, options) {
|
|
700
715
|
const session = options?.transaction?.pgTransaction ?? this.#repository.session;
|
|
701
|
-
await session
|
|
716
|
+
const result = await session
|
|
702
717
|
.update(taskTable)
|
|
703
718
|
.set({
|
|
704
719
|
status: caseWhen(gt(taskTable.unresolvedScheduleDependencies, 0), enumValue(taskStatus, TaskStatus.Waiting)).else(enumValue(taskStatus, TaskStatus.Pending)),
|
|
@@ -707,7 +722,16 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
707
722
|
visibilityDeadline: null,
|
|
708
723
|
tries: caseWhen(eq(taskTable.status, enumValue(taskStatus, TaskStatus.Running)), greatest(0, sql `${taskTable.tries} - 1`)).else(taskTable.tries),
|
|
709
724
|
})
|
|
710
|
-
.where(and(eq(taskTable.namespace, this.#namespace), arrayOverlaps(taskTable.tags, toArray(tags)), notInArray(taskTable.status, terminalStatuses)))
|
|
725
|
+
.where(and(eq(taskTable.namespace, this.#namespace), arrayOverlaps(taskTable.tags, toArray(tags)), notInArray(taskTable.status, terminalStatuses)))
|
|
726
|
+
.returning({ id: taskTable.id });
|
|
727
|
+
if ((result.length > 0) && (options?.refundRateLimit == true)) {
|
|
728
|
+
try {
|
|
729
|
+
await this.#rateLimiter.refund(this.#namespace, result.length);
|
|
730
|
+
}
|
|
731
|
+
catch (error) {
|
|
732
|
+
this.#logger.error(error);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
711
735
|
}
|
|
712
736
|
async touch(task, options) {
|
|
713
737
|
if (isNull(task.token)) {
|
|
@@ -755,6 +779,12 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
755
779
|
updateState: sql `(state)::jsonb`.as('update_state'),
|
|
756
780
|
})
|
|
757
781
|
.from(sql `(VALUES ${sql.join(rows, sql `, `)}) AS t(id, token, progress, state)`));
|
|
782
|
+
const locked = session.$with('locked').as((qb) => qb
|
|
783
|
+
.select({ id: taskTable.id })
|
|
784
|
+
.from(taskTable)
|
|
785
|
+
.innerJoin(updates, eq(taskTable.id, updates.updateId))
|
|
786
|
+
.orderBy(asc(taskTable.id))
|
|
787
|
+
.for('update'));
|
|
758
788
|
const updated = session.$with('updated').as(() => session
|
|
759
789
|
.update(taskTable)
|
|
760
790
|
.set({
|
|
@@ -763,10 +793,10 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
763
793
|
state: coalesce(updates.updateState, taskTable.state),
|
|
764
794
|
})
|
|
765
795
|
.from(updates)
|
|
766
|
-
.where(and(eq(taskTable.id, updates.updateId), sql `${taskTable.token} IS NOT DISTINCT FROM ${updates.updateToken}`, eq(taskTable.status, TaskStatus.Running), gt(taskTable.startTimestamp, sql `${TRANSACTION_TIMESTAMP} - ${interval(this.maxExecutionTime, 'milliseconds')}`)))
|
|
796
|
+
.where(and(eq(taskTable.id, updates.updateId), inArray(taskTable.id, session.select({ id: locked.id }).from(locked)), sql `${taskTable.token} IS NOT DISTINCT FROM ${updates.updateToken}`, eq(taskTable.status, TaskStatus.Running), gt(taskTable.startTimestamp, sql `${TRANSACTION_TIMESTAMP} - ${interval(this.maxExecutionTime, 'milliseconds')}`)))
|
|
767
797
|
.returning({ id: taskTable.id }));
|
|
768
798
|
const result = await session
|
|
769
|
-
.with(updates, updated)
|
|
799
|
+
.with(updates, locked, updated)
|
|
770
800
|
.select({ id: updated.id })
|
|
771
801
|
.from(updated);
|
|
772
802
|
return result.map((r) => r.id);
|
|
@@ -810,6 +840,12 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
810
840
|
updateProgress: sql `(progress)::numeric`.as('update_progress'),
|
|
811
841
|
})
|
|
812
842
|
.from(sql `(VALUES ${sql.join(rows, sql `, `)}) AS t(id, token, result, progress)`));
|
|
843
|
+
const locked = tx.pgTransaction.$with('locked').as((qb) => qb
|
|
844
|
+
.select({ id: taskTable.id })
|
|
845
|
+
.from(taskTable)
|
|
846
|
+
.innerJoin(updates, eq(taskTable.id, updates.updateId))
|
|
847
|
+
.orderBy(asc(taskTable.id))
|
|
848
|
+
.for('update'));
|
|
813
849
|
const updated = tx.pgTransaction.$with('updated').as(() => tx.pgTransaction
|
|
814
850
|
.update(taskTable)
|
|
815
851
|
.set({
|
|
@@ -821,10 +857,10 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
821
857
|
visibilityDeadline: null,
|
|
822
858
|
})
|
|
823
859
|
.from(updates)
|
|
824
|
-
.where(and(eq(taskTable.id, updates.updateId), sql `${taskTable.token} IS NOT DISTINCT FROM ${updates.updateToken}`))
|
|
860
|
+
.where(and(eq(taskTable.id, updates.updateId), inArray(taskTable.id, tx.pgTransaction.select({ id: locked.id }).from(locked)), sql `${taskTable.token} IS NOT DISTINCT FROM ${updates.updateToken}`))
|
|
825
861
|
.returning({ id: taskTable.id, status: taskTable.status }));
|
|
826
862
|
const updatedRows = await tx.pgTransaction
|
|
827
|
-
.with(updates, updated)
|
|
863
|
+
.with(updates, locked, updated)
|
|
828
864
|
.select({ id: updated.id, status: updated.status })
|
|
829
865
|
.from(updated);
|
|
830
866
|
if (updatedRows.length > 0) {
|
|
@@ -879,6 +915,12 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
879
915
|
updateComplete: sql `(complete_timestamp)`.as('update_complete'),
|
|
880
916
|
})
|
|
881
917
|
.from(sql `(VALUES ${sql.join(rows, sql `, `)}) AS t(id, token, tries, status, error, schedule_timestamp, complete_timestamp)`));
|
|
918
|
+
const locked = tx.pgTransaction.$with('locked').as((qb) => qb
|
|
919
|
+
.select({ id: taskTable.id })
|
|
920
|
+
.from(taskTable)
|
|
921
|
+
.innerJoin(updates, eq(taskTable.id, updates.updateId))
|
|
922
|
+
.orderBy(asc(taskTable.id))
|
|
923
|
+
.for('update'));
|
|
882
924
|
const updated = tx.pgTransaction.$with('updated').as(() => tx.pgTransaction
|
|
883
925
|
.update(taskTable)
|
|
884
926
|
.set({
|
|
@@ -890,10 +932,10 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
890
932
|
completeTimestamp: sql `${updates.updateComplete}`,
|
|
891
933
|
})
|
|
892
934
|
.from(updates)
|
|
893
|
-
.where(and(eq(taskTable.id, updates.updateId), sql `${taskTable.token} IS NOT DISTINCT FROM ${updates.updateToken}`, eq(taskTable.tries, updates.updateTries)))
|
|
935
|
+
.where(and(eq(taskTable.id, updates.updateId), inArray(taskTable.id, tx.pgTransaction.select({ id: locked.id }).from(locked)), sql `${taskTable.token} IS NOT DISTINCT FROM ${updates.updateToken}`, eq(taskTable.tries, updates.updateTries)))
|
|
894
936
|
.returning({ id: taskTable.id, status: taskTable.status }));
|
|
895
937
|
const result = await tx.pgTransaction
|
|
896
|
-
.with(updates, updated)
|
|
938
|
+
.with(updates, locked, updated)
|
|
897
939
|
.select({ id: updated.id, status: updated.status })
|
|
898
940
|
.from(updated);
|
|
899
941
|
if (result.length > 0) {
|
|
@@ -916,7 +958,9 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
916
958
|
await this.resolveDependenciesMany([{ id, status, namespace: options?.namespace }], options);
|
|
917
959
|
}
|
|
918
960
|
async resolveDependenciesMany(tasks, options) {
|
|
919
|
-
const tasksToResolve = tasks
|
|
961
|
+
const tasksToResolve = tasks
|
|
962
|
+
.filter((t) => terminalStatuses.includes(t.status))
|
|
963
|
+
.toSorted((a, b) => a.id.localeCompare(b.id));
|
|
920
964
|
if (tasksToResolve.length == 0) {
|
|
921
965
|
return;
|
|
922
966
|
}
|
|
@@ -1013,7 +1057,6 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
1013
1057
|
}
|
|
1014
1058
|
if (decrementsToApply.size > 0) {
|
|
1015
1059
|
const decrementValues = [...decrementsToApply]
|
|
1016
|
-
.toSorted(([idA], [idB]) => idA.localeCompare(idB))
|
|
1017
1060
|
.map(([taskId, d]) => sql `(${taskId}::uuid, ${d.schedule}::int, ${d.complete}::int)`);
|
|
1018
1061
|
const updates = tx.pgTransaction.$with('updates').as((qb) => qb
|
|
1019
1062
|
.select({
|
|
@@ -1022,8 +1065,14 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
1022
1065
|
completeDecrement: sql `(complete)::int`.as('complete_decrement'),
|
|
1023
1066
|
})
|
|
1024
1067
|
.from(sql `(VALUES ${sql.join(decrementValues, sql `, `)}) AS t(id, schedule, complete)`));
|
|
1068
|
+
const locked = tx.pgTransaction.$with('locked').as((qb) => qb
|
|
1069
|
+
.select({ id: taskTable.id })
|
|
1070
|
+
.from(taskTable)
|
|
1071
|
+
.innerJoin(updates, eq(taskTable.id, updates.taskId))
|
|
1072
|
+
.orderBy(asc(taskTable.id))
|
|
1073
|
+
.for('update'));
|
|
1025
1074
|
const updatedRows = await tx.pgTransaction
|
|
1026
|
-
.with(updates)
|
|
1075
|
+
.with(updates, locked)
|
|
1027
1076
|
.update(taskTable)
|
|
1028
1077
|
.set({
|
|
1029
1078
|
unresolvedScheduleDependencies: greatest(0, sql `${taskTable.unresolvedScheduleDependencies} - ${updates.scheduleDecrement}`),
|
|
@@ -1034,7 +1083,7 @@ let PostgresTaskQueue = class PostgresTaskQueue extends TaskQueue {
|
|
|
1034
1083
|
token: caseWhen(and(eq(taskTable.status, TaskStatus.WaitingChildren), eq(greatest(0, sql `${taskTable.unresolvedCompleteDependencies} - ${updates.completeDecrement}`), 0)), null).else(taskTable.token),
|
|
1035
1084
|
})
|
|
1036
1085
|
.from(updates)
|
|
1037
|
-
.where(eq(taskTable.id, updates.taskId))
|
|
1086
|
+
.where(and(eq(taskTable.id, updates.taskId), inArray(taskTable.id, tx.pgTransaction.select({ id: locked.id }).from(locked))))
|
|
1038
1087
|
.returning({
|
|
1039
1088
|
id: taskTable.id,
|
|
1040
1089
|
namespace: taskTable.namespace,
|
|
@@ -33,10 +33,12 @@ declare class TaskContextImplementation<Definitions extends TaskDefinitionMap, T
|
|
|
33
33
|
spawnMany<OtherDefinitions extends TaskDefinitionMap, SpawnType extends TaskTypes<OtherDefinitions>>(queue: TaskQueue<OtherDefinitions>, items: EnqueueManyItem<OtherDefinitions, SpawnType>[]): Promise<TaskOfType<OtherDefinitions, SpawnType>[]>;
|
|
34
34
|
/** Stop execution and reschedule the task for later without incrementing tries if possible */
|
|
35
35
|
reschedule(timestamp: number, options?: {
|
|
36
|
+
refundRateLimit?: boolean;
|
|
36
37
|
transaction?: Transaction;
|
|
37
38
|
}): Promise<void>;
|
|
38
39
|
reschedule(rescheduleOptions: {
|
|
39
40
|
delay: number;
|
|
41
|
+
refundRateLimit?: boolean;
|
|
40
42
|
transaction?: Transaction;
|
|
41
43
|
}): Promise<void>;
|
|
42
44
|
fail(error: unknown, options?: {
|
|
@@ -83,12 +83,12 @@ class TaskContextImplementation {
|
|
|
83
83
|
const items = queueOrItems.map((item) => ({ ...item, parentId: this.#task.id }));
|
|
84
84
|
return await this.#queue.enqueueMany(items, { returnTasks: true });
|
|
85
85
|
}
|
|
86
|
-
async reschedule(timestampOrDelay) {
|
|
86
|
+
async reschedule(timestampOrDelay, maybeOptions) {
|
|
87
87
|
const isTimestamp = isNumber(timestampOrDelay);
|
|
88
88
|
const timestamp = isTimestamp ? timestampOrDelay : (currentTimestamp() + timestampOrDelay.delay);
|
|
89
|
-
const
|
|
89
|
+
const options = isTimestamp ? maybeOptions : timestampOrDelay;
|
|
90
90
|
this.#logger.debug(`Rescheduling task for ${new Date(timestamp).toISOString()}.`);
|
|
91
|
-
await this.#queue.reschedule(this.#task.id, timestamp,
|
|
91
|
+
await this.#queue.reschedule(this.#task.id, timestamp, options);
|
|
92
92
|
this.#signal.set();
|
|
93
93
|
}
|
|
94
94
|
async fail(error, options) {
|
|
@@ -31,13 +31,19 @@ export declare class TaskProcessResult<Result = unknown> {
|
|
|
31
31
|
/**
|
|
32
32
|
* Creates a result that reschedules the task to a specific timestamp.
|
|
33
33
|
* @param timestamp The timestamp to reschedule to.
|
|
34
|
+
* @param options Optional refund flags.
|
|
34
35
|
*/
|
|
35
|
-
static RescheduleTo<R>(timestamp: number
|
|
36
|
+
static RescheduleTo<R>(timestamp: number, options?: {
|
|
37
|
+
refundRateLimit?: boolean;
|
|
38
|
+
}): TaskProcessResult<R>;
|
|
36
39
|
/**
|
|
37
40
|
* Creates a result that reschedules the task by a specific number of milliseconds.
|
|
38
41
|
* @param milliseconds The number of milliseconds to reschedule by.
|
|
42
|
+
* @param options Optional refund flags.
|
|
39
43
|
*/
|
|
40
|
-
static RescheduleBy<R>(milliseconds: number
|
|
44
|
+
static RescheduleBy<R>(milliseconds: number, options?: {
|
|
45
|
+
refundRateLimit?: boolean;
|
|
46
|
+
}): TaskProcessResult<R>;
|
|
41
47
|
}
|
|
42
48
|
/**
|
|
43
49
|
* Represents the status of a task in the queue.
|
|
@@ -521,27 +527,30 @@ export declare abstract class TaskQueue<Definitions extends TaskDefinitionMap =
|
|
|
521
527
|
* Reschedules a task to run at a specific time.
|
|
522
528
|
* @param id The ID of the task.
|
|
523
529
|
* @param timestamp The timestamp when the task should run.
|
|
524
|
-
* @param options Optional transaction.
|
|
530
|
+
* @param options Optional transaction and refund flags.
|
|
525
531
|
*/
|
|
526
532
|
abstract reschedule(id: string, timestamp: number, options?: {
|
|
533
|
+
refundRateLimit?: boolean;
|
|
527
534
|
transaction?: Transaction;
|
|
528
535
|
}): Promise<void>;
|
|
529
536
|
/**
|
|
530
537
|
* Reschedules multiple tasks to run at a specific time.
|
|
531
538
|
* @param ids The IDs of the tasks.
|
|
532
539
|
* @param timestamp The timestamp when the tasks should run.
|
|
533
|
-
* @param options Optional transaction.
|
|
540
|
+
* @param options Optional transaction and refund flags.
|
|
534
541
|
*/
|
|
535
542
|
abstract rescheduleMany(ids: string[], timestamp: number, options?: {
|
|
543
|
+
refundRateLimit?: boolean;
|
|
536
544
|
transaction?: Transaction;
|
|
537
545
|
}): Promise<void>;
|
|
538
546
|
/**
|
|
539
547
|
* Reschedules tasks with the given tags to run at a specific time.
|
|
540
548
|
* @param tags One or more tags.
|
|
541
549
|
* @param timestamp The timestamp when the tasks should run.
|
|
542
|
-
* @param options Optional transaction.
|
|
550
|
+
* @param options Optional transaction and refund flags.
|
|
543
551
|
*/
|
|
544
552
|
abstract rescheduleManyByTags(tags: OneOrMany<string>, timestamp: number, options?: {
|
|
553
|
+
refundRateLimit?: boolean;
|
|
545
554
|
transaction?: Transaction;
|
|
546
555
|
}): Promise<void>;
|
|
547
556
|
/**
|
package/task-queue/task-queue.js
CHANGED
|
@@ -87,17 +87,19 @@ export class TaskProcessResult {
|
|
|
87
87
|
/**
|
|
88
88
|
* Creates a result that reschedules the task to a specific timestamp.
|
|
89
89
|
* @param timestamp The timestamp to reschedule to.
|
|
90
|
+
* @param options Optional refund flags.
|
|
90
91
|
*/
|
|
91
|
-
static RescheduleTo(timestamp) {
|
|
92
|
-
return new TaskProcessResult({ action: 'reschedule', timestamp });
|
|
92
|
+
static RescheduleTo(timestamp, options) {
|
|
93
|
+
return new TaskProcessResult({ action: 'reschedule', timestamp, ...options });
|
|
93
94
|
}
|
|
94
95
|
/**
|
|
95
96
|
* Creates a result that reschedules the task by a specific number of milliseconds.
|
|
96
97
|
* @param milliseconds The number of milliseconds to reschedule by.
|
|
98
|
+
* @param options Optional refund flags.
|
|
97
99
|
*/
|
|
98
|
-
static RescheduleBy(milliseconds) {
|
|
100
|
+
static RescheduleBy(milliseconds, options) {
|
|
99
101
|
const timestamp = currentTimestamp() + milliseconds;
|
|
100
|
-
return this.RescheduleTo(timestamp);
|
|
102
|
+
return this.RescheduleTo(timestamp, options);
|
|
101
103
|
}
|
|
102
104
|
}
|
|
103
105
|
/**
|
|
@@ -275,7 +277,7 @@ export class TaskQueue extends Transactional {
|
|
|
275
277
|
break;
|
|
276
278
|
case 'reschedule':
|
|
277
279
|
context.logger.verbose('Rescheduling task');
|
|
278
|
-
await this.reschedule(task.id, result.payload.timestamp);
|
|
280
|
+
await this.reschedule(task.id, result.payload.timestamp, { refundRateLimit: result.payload.refundRateLimit });
|
|
279
281
|
break;
|
|
280
282
|
default:
|
|
281
283
|
throw new Error('Unsupported task result action.');
|
package/task-queue/types.d.ts
CHANGED
package/test2.js
CHANGED
|
@@ -1,16 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
2
10
|
import { Application } from './application/application.js';
|
|
3
11
|
import { provideModule } from './application/providers.js';
|
|
12
|
+
import { defineEnum } from './enumeration/enumeration.js';
|
|
4
13
|
import { PrettyPrintLogFormatter, provideConsoleLogTransport } from './logger/index.js';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
14
|
+
import { Enumeration } from './schema/index.js';
|
|
15
|
+
import { getDesignType } from './utils/reflection.js';
|
|
16
|
+
const Status = defineEnum('Status', {
|
|
17
|
+
Active: 'active',
|
|
18
|
+
Inactive: 'inactive',
|
|
19
|
+
Suspended: 'suspended',
|
|
8
20
|
});
|
|
21
|
+
class TestClass {
|
|
22
|
+
status;
|
|
23
|
+
}
|
|
24
|
+
__decorate([
|
|
25
|
+
Enumeration(Status),
|
|
26
|
+
__metadata("design:type", String)
|
|
27
|
+
], TestClass.prototype, "status", void 0);
|
|
9
28
|
function main() {
|
|
10
|
-
|
|
11
|
-
console.log(value);
|
|
29
|
+
console.log(getDesignType(TestClass.prototype, 'status'));
|
|
12
30
|
}
|
|
13
31
|
Application.run('Test', [
|
|
14
32
|
provideModule(main),
|
|
15
|
-
provideConsoleLogTransport(PrettyPrintLogFormatter)
|
|
33
|
+
provideConsoleLogTransport(PrettyPrintLogFormatter),
|
|
16
34
|
]);
|