quidproquo-core 0.1.18 → 0.1.20
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/lib/commonjs/actions/keyValueStore/askKeyValueStoreUpsert.d.ts +1 -1
- package/lib/commonjs/actions/keyValueStore/askKeyValueStoreUpsert.js +6 -2
- package/lib/commonjs/actions/keyValueStore/askKeyValueStoreUpsert.js.map +1 -1
- package/lib/commonjs/actions/keyValueStore/askKeyValueStoreUpsertMany.d.ts +2 -1
- package/lib/commonjs/actions/keyValueStore/askKeyValueStoreUpsertMany.js +7 -0
- package/lib/commonjs/actions/keyValueStore/askKeyValueStoreUpsertMany.js.map +1 -1
- package/lib/commonjs/config/settings/schedule/InvalidScheduleRecurrenceError.d.ts +20 -0
- package/lib/commonjs/config/settings/schedule/InvalidScheduleRecurrenceError.js +29 -0
- package/lib/commonjs/config/settings/schedule/InvalidScheduleRecurrenceError.js.map +1 -0
- package/lib/commonjs/config/settings/schedule/defineRecurringSchedule.d.ts +37 -0
- package/lib/commonjs/config/settings/schedule/defineRecurringSchedule.js +58 -0
- package/lib/commonjs/config/settings/schedule/defineRecurringSchedule.js.map +1 -0
- package/lib/commonjs/config/settings/schedule/index.d.ts +4 -0
- package/lib/commonjs/config/settings/schedule/index.js +21 -0
- package/lib/commonjs/config/settings/schedule/index.js.map +1 -0
- package/lib/commonjs/config/settings/schedule/resolveScheduleFields.d.ts +12 -0
- package/lib/commonjs/config/settings/schedule/resolveScheduleFields.js +83 -0
- package/lib/commonjs/config/settings/schedule/resolveScheduleFields.js.map +1 -0
- package/lib/commonjs/config/settings/schedule/types/DayOfWeek.d.ts +18 -0
- package/lib/commonjs/config/settings/schedule/types/DayOfWeek.js +23 -0
- package/lib/commonjs/config/settings/schedule/types/DayOfWeek.js.map +1 -0
- package/lib/commonjs/config/settings/schedule/types/QPQConfigAdvancedScheduleSettings.d.ts +7 -0
- package/lib/commonjs/config/settings/schedule/types/QPQConfigAdvancedScheduleSettings.js +3 -0
- package/lib/commonjs/config/settings/schedule/types/QPQConfigAdvancedScheduleSettings.js.map +1 -0
- package/lib/commonjs/config/settings/schedule/types/ScheduleFields.d.ts +23 -0
- package/lib/commonjs/config/settings/schedule/types/ScheduleFields.js +3 -0
- package/lib/commonjs/config/settings/schedule/types/ScheduleFields.js.map +1 -0
- package/lib/commonjs/config/settings/schedule/types/ScheduleQPQConfigSetting.d.ts +12 -0
- package/lib/commonjs/config/settings/schedule/types/ScheduleQPQConfigSetting.js +3 -0
- package/lib/commonjs/config/settings/schedule/types/ScheduleQPQConfigSetting.js.map +1 -0
- package/lib/commonjs/config/settings/schedule/types/ScheduleRecurrence.d.ts +54 -0
- package/lib/commonjs/config/settings/schedule/types/ScheduleRecurrence.js +3 -0
- package/lib/commonjs/config/settings/schedule/types/ScheduleRecurrence.js.map +1 -0
- package/lib/commonjs/config/settings/schedule/types/ScheduleTypeEnum.d.ts +3 -0
- package/lib/commonjs/config/settings/schedule/types/ScheduleTypeEnum.js +8 -0
- package/lib/commonjs/config/settings/schedule/types/ScheduleTypeEnum.js.map +1 -0
- package/lib/commonjs/config/settings/schedule/types/index.d.ts +6 -0
- package/lib/commonjs/config/settings/schedule/types/index.js +23 -0
- package/lib/commonjs/config/settings/schedule/types/index.js.map +1 -0
- package/lib/commonjs/config/settings/userDirectory.d.ts +0 -1
- package/lib/commonjs/config/settings/userDirectory.js.map +1 -1
- package/lib/commonjs/stories/kvs/askKeyValueStoreUpsertWithRetry.js +4 -1
- package/lib/commonjs/stories/kvs/askKeyValueStoreUpsertWithRetry.js.map +1 -1
- package/lib/commonjs/types/QpqFunctionRuntime.d.ts +2 -4
- package/lib/commonjs/types/QpqFunctionRuntime.js.map +1 -1
- package/lib/commonjs/types/QpqPureFunction.d.ts +10 -0
- package/lib/commonjs/types/QpqPureFunction.js +3 -0
- package/lib/commonjs/types/QpqPureFunction.js.map +1 -0
- package/lib/commonjs/types/index.d.ts +1 -0
- package/lib/commonjs/types/index.js +1 -0
- package/lib/commonjs/types/index.js.map +1 -1
- package/lib/esm/actions/keyValueStore/askKeyValueStoreUpsert.d.ts +1 -1
- package/lib/esm/actions/keyValueStore/askKeyValueStoreUpsert.js +6 -2
- package/lib/esm/actions/keyValueStore/askKeyValueStoreUpsert.js.map +1 -1
- package/lib/esm/actions/keyValueStore/askKeyValueStoreUpsertMany.d.ts +2 -1
- package/lib/esm/actions/keyValueStore/askKeyValueStoreUpsertMany.js +7 -0
- package/lib/esm/actions/keyValueStore/askKeyValueStoreUpsertMany.js.map +1 -1
- package/lib/esm/config/settings/schedule/InvalidScheduleRecurrenceError.d.ts +20 -0
- package/lib/esm/config/settings/schedule/InvalidScheduleRecurrenceError.js +26 -0
- package/lib/esm/config/settings/schedule/InvalidScheduleRecurrenceError.js.map +1 -0
- package/lib/esm/config/settings/schedule/defineRecurringSchedule.d.ts +37 -0
- package/lib/esm/config/settings/schedule/defineRecurringSchedule.js +54 -0
- package/lib/esm/config/settings/schedule/defineRecurringSchedule.js.map +1 -0
- package/lib/esm/config/settings/schedule/index.d.ts +4 -0
- package/lib/esm/config/settings/schedule/index.js +5 -0
- package/lib/esm/config/settings/schedule/index.js.map +1 -0
- package/lib/esm/config/settings/schedule/resolveScheduleFields.d.ts +12 -0
- package/lib/esm/config/settings/schedule/resolveScheduleFields.js +78 -0
- package/lib/esm/config/settings/schedule/resolveScheduleFields.js.map +1 -0
- package/lib/esm/config/settings/schedule/types/DayOfWeek.d.ts +18 -0
- package/lib/esm/config/settings/schedule/types/DayOfWeek.js +20 -0
- package/lib/esm/config/settings/schedule/types/DayOfWeek.js.map +1 -0
- package/lib/esm/config/settings/schedule/types/QPQConfigAdvancedScheduleSettings.d.ts +7 -0
- package/lib/esm/config/settings/schedule/types/QPQConfigAdvancedScheduleSettings.js +2 -0
- package/lib/esm/config/settings/schedule/types/QPQConfigAdvancedScheduleSettings.js.map +1 -0
- package/lib/esm/config/settings/schedule/types/ScheduleFields.d.ts +23 -0
- package/lib/esm/config/settings/schedule/types/ScheduleFields.js +2 -0
- package/lib/esm/config/settings/schedule/types/ScheduleFields.js.map +1 -0
- package/lib/esm/config/settings/schedule/types/ScheduleQPQConfigSetting.d.ts +12 -0
- package/lib/esm/config/settings/schedule/types/ScheduleQPQConfigSetting.js +2 -0
- package/lib/esm/config/settings/schedule/types/ScheduleQPQConfigSetting.js.map +1 -0
- package/lib/esm/config/settings/schedule/types/ScheduleRecurrence.d.ts +54 -0
- package/lib/esm/config/settings/schedule/types/ScheduleRecurrence.js +2 -0
- package/lib/esm/config/settings/schedule/types/ScheduleRecurrence.js.map +1 -0
- package/lib/esm/config/settings/schedule/types/ScheduleTypeEnum.d.ts +3 -0
- package/lib/esm/config/settings/schedule/types/ScheduleTypeEnum.js +5 -0
- package/lib/esm/config/settings/schedule/types/ScheduleTypeEnum.js.map +1 -0
- package/lib/esm/config/settings/schedule/types/index.d.ts +6 -0
- package/lib/esm/config/settings/schedule/types/index.js +7 -0
- package/lib/esm/config/settings/schedule/types/index.js.map +1 -0
- package/lib/esm/config/settings/userDirectory.d.ts +0 -1
- package/lib/esm/config/settings/userDirectory.js.map +1 -1
- package/lib/esm/stories/kvs/askKeyValueStoreUpsertWithRetry.js +4 -1
- package/lib/esm/stories/kvs/askKeyValueStoreUpsertWithRetry.js.map +1 -1
- package/lib/esm/types/QpqFunctionRuntime.d.ts +2 -4
- package/lib/esm/types/QpqFunctionRuntime.js.map +1 -1
- package/lib/esm/types/QpqPureFunction.d.ts +10 -0
- package/lib/esm/types/QpqPureFunction.js +2 -0
- package/lib/esm/types/QpqPureFunction.js.map +1 -0
- package/lib/esm/types/index.d.ts +1 -0
- package/lib/esm/types/index.js +1 -0
- package/lib/esm/types/index.js.map +1 -1
- package/package.json +2 -2
- package/lib/commonjs/config/settings/schedule.d.ts +0 -79
- package/lib/commonjs/config/settings/schedule.js +0 -85
- package/lib/commonjs/config/settings/schedule.js.map +0 -1
- package/lib/esm/config/settings/schedule.d.ts +0 -79
- package/lib/esm/config/settings/schedule.js +0 -81
- package/lib/esm/config/settings/schedule.js.map +0 -1
|
@@ -10,5 +10,5 @@ export declare const askKeyValueStoreUpsertBase: import("../..").ActionRequester
|
|
|
10
10
|
keyValueStoreName: string;
|
|
11
11
|
item: KvsItemRecord;
|
|
12
12
|
options: KeyValueStoreUpsertOptions | undefined;
|
|
13
|
-
}, void, "Conflict" | "ResourceNotFound" | "ServiceUnavailable" | "InvalidScope" | "StoreNotFound">;
|
|
13
|
+
}, void, "Conflict" | "ResourceNotFound" | "ServiceUnavailable" | "InvalidScope" | "StoreNotFound" | "WriteContention">;
|
|
14
14
|
export declare function askKeyValueStoreUpsert<KvsItem>(keyValueStoreName: string, item: KvsItem, options?: KeyValueStoreUpsertOptions): AskResponse<void>;
|
|
@@ -9,10 +9,14 @@ exports.askKeyValueStoreUpsertBase = (0, createActionRequester_1.createActionReq
|
|
|
9
9
|
errorTypes: [
|
|
10
10
|
'ServiceUnavailable', // DynamoDB internal error / throttling
|
|
11
11
|
'ResourceNotFound', // the underlying table does not exist
|
|
12
|
-
// A conditional (ifNotExists) write lost to an existing item.
|
|
13
|
-
// not ErrorTypeEnum.Conflict
|
|
12
|
+
// A conditional (ifNotExists) write lost to an existing item. Final: the slot is taken.
|
|
13
|
+
// Namespaced, not ErrorTypeEnum.Conflict, so retry logic can target the write race
|
|
14
14
|
// specifically without also catching domain-level conflicts.
|
|
15
15
|
'Conflict',
|
|
16
|
+
// Another write (a transaction in flight) holds the item right now. Transient: the item
|
|
17
|
+
// may still be absent once it settles, so this is a retry, never "already exists".
|
|
18
|
+
// askKeyValueStoreUpsertWithRetry retries it.
|
|
19
|
+
'WriteContention',
|
|
16
20
|
'InvalidScope', // scope is malformed or the store's partition key is not string-typed
|
|
17
21
|
'StoreNotFound', // the store is not declared in the qpq config (misconfiguration)
|
|
18
22
|
],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"askKeyValueStoreUpsert.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/askKeyValueStoreUpsert.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"askKeyValueStoreUpsert.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/askKeyValueStoreUpsert.ts"],"names":[],"mappings":";;;AAsCA,wDAEC;AAvCD,mFAAgF;AAChF,uEAAoE;AAgBvD,QAAA,0BAA0B,GAAG,IAAA,6CAAqB,GAAQ,CAAC;IACtE,UAAU,EAAE,iDAAuB,CAAC,MAAM;IAC1C,UAAU,EAAE;QACV,oBAAoB,EAAE,uCAAuC;QAC7D,kBAAkB,EAAE,sCAAsC;QAC1D,wFAAwF;QACxF,mFAAmF;QACnF,6DAA6D;QAC7D,UAAU;QACV,wFAAwF;QACxF,mFAAmF;QACnF,8CAA8C;QAC9C,iBAAiB;QACjB,cAAc,EAAE,sEAAsE;QACtF,eAAe,EAAE,iEAAiE;KACnF;IACD,UAAU,EAAE,CAAC,iBAAyB,EAAE,IAAmB,EAAE,OAAoC,EAAE,EAAE,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;CAC7I,CAAC,CAAC;AAEH,+EAA+E;AAC/E,QAAe,CAAC,CAAC,sBAAsB,CAAU,iBAAyB,EAAE,IAAa,EAAE,OAAoC;IAC7H,OAAO,KAAK,CAAC,CAAC,IAAA,kCAA0B,EAAC,iBAAiB,EAAE,IAAqB,EAAE,OAAO,CAAC,CAAC;AAC9F,CAAC","sourcesContent":["import { AskResponse } from '../../types/StorySession';\nimport { createActionRequester } from '../../types/utils/createActionRequester';\nimport { KeyValueStoreActionType } from './KeyValueStoreActionType';\nimport { KvsItemRecord } from './types';\n\nexport type KeyValueStoreUpsertOptions = {\n ttlInSeconds?: number; // Time-to-live in seconds\n\n // Conditional insert: only write when no item with the same key exists.\n // A losing concurrent writer gets ErrorTypeEnum.Conflict instead of silently\n // overwriting — the primitive for optimistic-concurrency schemes (e.g.\n // append-only event logs where the sort key is a claimed index).\n ifNotExists?: boolean;\n\n // Composed into the item's partition key value by the processor; requires a string-typed partition key.\n scope?: string;\n};\n\nexport const askKeyValueStoreUpsertBase = createActionRequester<void>()({\n actionType: KeyValueStoreActionType.Upsert,\n errorTypes: [\n 'ServiceUnavailable', // DynamoDB internal error / throttling\n 'ResourceNotFound', // the underlying table does not exist\n // A conditional (ifNotExists) write lost to an existing item. Final: the slot is taken.\n // Namespaced, not ErrorTypeEnum.Conflict, so retry logic can target the write race\n // specifically without also catching domain-level conflicts.\n 'Conflict',\n // Another write (a transaction in flight) holds the item right now. Transient: the item\n // may still be absent once it settles, so this is a retry, never \"already exists\".\n // askKeyValueStoreUpsertWithRetry retries it.\n 'WriteContention',\n 'InvalidScope', // scope is malformed or the store's partition key is not string-typed\n 'StoreNotFound', // the store is not declared in the qpq config (misconfiguration)\n ],\n getPayload: (keyValueStoreName: string, item: KvsItemRecord, options?: KeyValueStoreUpsertOptions) => ({ keyValueStoreName, item, options }),\n});\n\n// Generic so callers can pin the item shape they are writing at the call site.\nexport function* askKeyValueStoreUpsert<KvsItem>(keyValueStoreName: string, item: KvsItem, options?: KeyValueStoreUpsertOptions): AskResponse<void> {\n return yield* askKeyValueStoreUpsertBase(keyValueStoreName, item as KvsItemRecord, options);\n}\n"]}
|
|
@@ -2,11 +2,12 @@ import { AskResponse } from '../../types/StorySession';
|
|
|
2
2
|
import { KeyValueStoreActionType } from './KeyValueStoreActionType';
|
|
3
3
|
import { KvsItemRecord } from './types';
|
|
4
4
|
export type KeyValueStoreUpsertManyOptions = {
|
|
5
|
+
ifNotExists?: boolean;
|
|
5
6
|
scope?: string;
|
|
6
7
|
};
|
|
7
8
|
export declare const askKeyValueStoreUpsertManyBase: import("../..").ActionRequesterFunction<KeyValueStoreActionType.UpsertMany, [keyValueStoreName: string, items: KvsItemRecord[], options?: KeyValueStoreUpsertManyOptions | undefined], {
|
|
8
9
|
keyValueStoreName: string;
|
|
9
10
|
items: KvsItemRecord[];
|
|
10
11
|
options: KeyValueStoreUpsertManyOptions | undefined;
|
|
11
|
-
}, void, "ResourceNotFound" | "ServiceUnavailable" | "InvalidScope" | "StoreNotFound" | "DuplicateKey">;
|
|
12
|
+
}, void, "Conflict" | "ResourceNotFound" | "ServiceUnavailable" | "InvalidScope" | "StoreNotFound" | "WriteContention" | "DuplicateKey">;
|
|
12
13
|
export declare function askKeyValueStoreUpsertMany<KvsItem>(keyValueStoreName: string, items: KvsItem[], options?: KeyValueStoreUpsertManyOptions): AskResponse<void>;
|
|
@@ -17,6 +17,13 @@ exports.askKeyValueStoreUpsertManyBase = (0, createActionRequester_1.createActio
|
|
|
17
17
|
// the later write silently wins), so without this check the outcome would
|
|
18
18
|
// depend on item position and never reproduce locally.
|
|
19
19
|
'DuplicateKey',
|
|
20
|
+
// A conditional (ifNotExists) batch lost to an existing item; none of the batch was
|
|
21
|
+
// written. Final: a slot is taken. Namespaced, not ErrorTypeEnum.Conflict, so retry
|
|
22
|
+
// logic can target the write race specifically without also catching domain-level conflicts.
|
|
23
|
+
'Conflict',
|
|
24
|
+
// Another write holds one of the batch's items right now; none of the batch was
|
|
25
|
+
// written. Transient: retry.
|
|
26
|
+
'WriteContention',
|
|
20
27
|
],
|
|
21
28
|
getPayload: (keyValueStoreName, items, options) => ({
|
|
22
29
|
keyValueStoreName,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"askKeyValueStoreUpsertMany.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/askKeyValueStoreUpsertMany.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"askKeyValueStoreUpsertMany.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/askKeyValueStoreUpsertMany.ts"],"names":[],"mappings":";;;AA+CA,gEAMC;AApDD,mFAAgF;AAChF,uEAAoE;AAgBvD,QAAA,8BAA8B,GAAG,IAAA,6CAAqB,GAAQ,CAAC;IAC1E,UAAU,EAAE,iDAAuB,CAAC,UAAU;IAC9C,UAAU,EAAE;QACV,oBAAoB,EAAE,uCAAuC;QAC7D,kBAAkB,EAAE,sCAAsC;QAC1D,cAAc,EAAE,sEAAsE;QACtF,eAAe,EAAE,iEAAiE;QAClF,yEAAyE;QACzE,4EAA4E;QAC5E,8EAA8E;QAC9E,0EAA0E;QAC1E,uDAAuD;QACvD,cAAc;QACd,oFAAoF;QACpF,oFAAoF;QACpF,6FAA6F;QAC7F,UAAU;QACV,gFAAgF;QAChF,6BAA6B;QAC7B,iBAAiB;KAClB;IACD,UAAU,EAAE,CAAC,iBAAyB,EAAE,KAAsB,EAAE,OAAwC,EAAE,EAAE,CAAC,CAAC;QAC5G,iBAAiB;QACjB,KAAK;QACL,OAAO;KACR,CAAC;CACH,CAAC,CAAC;AAEH,+EAA+E;AAC/E,QAAe,CAAC,CAAC,0BAA0B,CACzC,iBAAyB,EACzB,KAAgB,EAChB,OAAwC;IAExC,OAAO,KAAK,CAAC,CAAC,IAAA,sCAA8B,EAAC,iBAAiB,EAAE,KAAwB,EAAE,OAAO,CAAC,CAAC;AACrG,CAAC","sourcesContent":["import { AskResponse } from '../../types/StorySession';\nimport { createActionRequester } from '../../types/utils/createActionRequester';\nimport { KeyValueStoreActionType } from './KeyValueStoreActionType';\nimport { KvsItemRecord } from './types';\n\nexport type KeyValueStoreUpsertManyOptions = {\n // Conditional batch insert: only write when NO item in the batch has an\n // existing key, and write ALL of them or none. The batch becomes a\n // transaction on every runtime (DynamoDB TransactWriteItems, a SQLite\n // transaction locally), so a losing concurrent writer gets the namespaced\n // Conflict with nothing landed - the batch primitive for claiming a run of\n // consecutive slots in an append-only log.\n ifNotExists?: boolean;\n\n // Composed into each item's partition key value by the processor; requires a string-typed partition key.\n scope?: string;\n};\n\nexport const askKeyValueStoreUpsertManyBase = createActionRequester<void>()({\n actionType: KeyValueStoreActionType.UpsertMany,\n errorTypes: [\n 'ServiceUnavailable', // DynamoDB internal error / throttling\n 'ResourceNotFound', // the underlying table does not exist\n 'InvalidScope', // scope is malformed or the store's partition key is not string-typed\n 'StoreNotFound', // the store is not declared in the qpq config (misconfiguration)\n // Two items in one batch share a primary key. Rejected up front on every\n // runtime, before anything is written: DynamoDB rejects same-key duplicates\n // only when they land in the same 25-item BatchWriteItem chunk (across chunks\n // the later write silently wins), so without this check the outcome would\n // depend on item position and never reproduce locally.\n 'DuplicateKey',\n // A conditional (ifNotExists) batch lost to an existing item; none of the batch was\n // written. Final: a slot is taken. Namespaced, not ErrorTypeEnum.Conflict, so retry\n // logic can target the write race specifically without also catching domain-level conflicts.\n 'Conflict',\n // Another write holds one of the batch's items right now; none of the batch was\n // written. Transient: retry.\n 'WriteContention',\n ],\n getPayload: (keyValueStoreName: string, items: KvsItemRecord[], options?: KeyValueStoreUpsertManyOptions) => ({\n keyValueStoreName,\n items,\n options,\n }),\n});\n\n// Generic so callers can pin the item shape they are writing at the call site.\nexport function* askKeyValueStoreUpsertMany<KvsItem>(\n keyValueStoreName: string,\n items: KvsItem[],\n options?: KeyValueStoreUpsertManyOptions,\n): AskResponse<void> {\n return yield* askKeyValueStoreUpsertManyBase(keyValueStoreName, items as KvsItemRecord[], options);\n}\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare enum InvalidScheduleRecurrenceErrorCode {
|
|
2
|
+
UnknownRecurrence = "UnknownRecurrence",
|
|
3
|
+
IntervalDoesNotDivideEvenly = "IntervalDoesNotDivideEvenly",
|
|
4
|
+
ValueOutOfRange = "ValueOutOfRange"
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* A recurrence that cannot be scheduled.
|
|
8
|
+
*
|
|
9
|
+
* Thrown from `resolveScheduleFields`, which `defineRecurringSchedule` calls,
|
|
10
|
+
* so a bad schedule fails while the config is being evaluated - at synth AND
|
|
11
|
+
* at dev-server boot - rather than at deploy or, worse, silently at the wrong
|
|
12
|
+
* time of day.
|
|
13
|
+
*
|
|
14
|
+
* A named subclass with a code rather than a bare Error, so a caller can tell
|
|
15
|
+
* these apart from anything else that went wrong while loading config.
|
|
16
|
+
*/
|
|
17
|
+
export declare class InvalidScheduleRecurrenceError extends Error {
|
|
18
|
+
readonly code: InvalidScheduleRecurrenceErrorCode;
|
|
19
|
+
constructor(code: InvalidScheduleRecurrenceErrorCode, message: string);
|
|
20
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InvalidScheduleRecurrenceError = exports.InvalidScheduleRecurrenceErrorCode = void 0;
|
|
4
|
+
var InvalidScheduleRecurrenceErrorCode;
|
|
5
|
+
(function (InvalidScheduleRecurrenceErrorCode) {
|
|
6
|
+
InvalidScheduleRecurrenceErrorCode["UnknownRecurrence"] = "UnknownRecurrence";
|
|
7
|
+
InvalidScheduleRecurrenceErrorCode["IntervalDoesNotDivideEvenly"] = "IntervalDoesNotDivideEvenly";
|
|
8
|
+
InvalidScheduleRecurrenceErrorCode["ValueOutOfRange"] = "ValueOutOfRange";
|
|
9
|
+
})(InvalidScheduleRecurrenceErrorCode || (exports.InvalidScheduleRecurrenceErrorCode = InvalidScheduleRecurrenceErrorCode = {}));
|
|
10
|
+
/**
|
|
11
|
+
* A recurrence that cannot be scheduled.
|
|
12
|
+
*
|
|
13
|
+
* Thrown from `resolveScheduleFields`, which `defineRecurringSchedule` calls,
|
|
14
|
+
* so a bad schedule fails while the config is being evaluated - at synth AND
|
|
15
|
+
* at dev-server boot - rather than at deploy or, worse, silently at the wrong
|
|
16
|
+
* time of day.
|
|
17
|
+
*
|
|
18
|
+
* A named subclass with a code rather than a bare Error, so a caller can tell
|
|
19
|
+
* these apart from anything else that went wrong while loading config.
|
|
20
|
+
*/
|
|
21
|
+
class InvalidScheduleRecurrenceError extends Error {
|
|
22
|
+
constructor(code, message) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = 'InvalidScheduleRecurrenceError';
|
|
25
|
+
this.code = code;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.InvalidScheduleRecurrenceError = InvalidScheduleRecurrenceError;
|
|
29
|
+
//# sourceMappingURL=InvalidScheduleRecurrenceError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InvalidScheduleRecurrenceError.js","sourceRoot":"","sources":["../../../../../src/config/settings/schedule/InvalidScheduleRecurrenceError.ts"],"names":[],"mappings":";;;AAAA,IAAY,kCAIX;AAJD,WAAY,kCAAkC;IAC5C,6EAAuC,CAAA;IACvC,iGAA2D,CAAA;IAC3D,yEAAmC,CAAA;AACrC,CAAC,EAJW,kCAAkC,kDAAlC,kCAAkC,QAI7C;AAED;;;;;;;;;;GAUG;AACH,MAAa,8BAA+B,SAAQ,KAAK;IAGvD,YAAY,IAAwC,EAAE,OAAe;QACnE,KAAK,CAAC,OAAO,CAAC,CAAC;QAEf,IAAI,CAAC,IAAI,GAAG,gCAAgC,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AATD,wEASC","sourcesContent":["export enum InvalidScheduleRecurrenceErrorCode {\n UnknownRecurrence = 'UnknownRecurrence',\n IntervalDoesNotDivideEvenly = 'IntervalDoesNotDivideEvenly',\n ValueOutOfRange = 'ValueOutOfRange',\n}\n\n/**\n * A recurrence that cannot be scheduled.\n *\n * Thrown from `resolveScheduleFields`, which `defineRecurringSchedule` calls,\n * so a bad schedule fails while the config is being evaluated - at synth AND\n * at dev-server boot - rather than at deploy or, worse, silently at the wrong\n * time of day.\n *\n * A named subclass with a code rather than a bare Error, so a caller can tell\n * these apart from anything else that went wrong while loading config.\n */\nexport class InvalidScheduleRecurrenceError extends Error {\n readonly code: InvalidScheduleRecurrenceErrorCode;\n\n constructor(code: InvalidScheduleRecurrenceErrorCode, message: string) {\n super(message);\n\n this.name = 'InvalidScheduleRecurrenceError';\n this.code = code;\n }\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { QpqFunctionRuntime } from '../../../types';
|
|
2
|
+
import { QPQConfigAdvancedScheduleSettings } from './types/QPQConfigAdvancedScheduleSettings';
|
|
3
|
+
import { ScheduleQPQConfigSetting } from './types/ScheduleQPQConfigSetting';
|
|
4
|
+
import { ScheduleRecurrence } from './types/ScheduleRecurrence';
|
|
5
|
+
/**
|
|
6
|
+
* Run a story on a recurring timetable.
|
|
7
|
+
*
|
|
8
|
+
* The recurrence is declared as intent, not as a cron string. Deployed, it is
|
|
9
|
+
* rendered to an AWS EventBridge cron expression; locally, the dev server's
|
|
10
|
+
* ticker matches the same declaration against the clock. Neither behaviour is
|
|
11
|
+
* a special case of the other, which is the point.
|
|
12
|
+
*
|
|
13
|
+
* **Every time is UTC.** The deployed scheduler evaluates in UTC, so there is
|
|
14
|
+
* no local-timezone option: a schedule that reads `{ dailyAtUtc: { hour: 17 } }`
|
|
15
|
+
* is 3am in Brisbane, and saying so in the config is better than a comment
|
|
16
|
+
* that drifts out of date twice a year.
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* // every ten minutes
|
|
20
|
+
* defineRecurringSchedule({ everyMinutes: 10 }, '/entry/schedule/onPoll::onPoll')
|
|
21
|
+
*
|
|
22
|
+
* // 5pm UTC daily
|
|
23
|
+
* defineRecurringSchedule({ dailyAtUtc: { hour: 17, minute: 0 } }, '/entry/schedule/onNightly::onNightly')
|
|
24
|
+
*
|
|
25
|
+
* // 2am UTC every Monday
|
|
26
|
+
* defineRecurringSchedule(
|
|
27
|
+
* { weeklyAtUtc: { day: DayOfWeek.Monday, hour: 2, minute: 0 } },
|
|
28
|
+
* '/entry/schedule/onDigest::onDigest',
|
|
29
|
+
* )
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* An interval that cannot be scheduled evenly (`{ everyMinutes: 7 }`) throws
|
|
33
|
+
* an `InvalidScheduleRecurrenceError` here, while the config is being
|
|
34
|
+
* evaluated, so it fails at synth and at dev-server boot rather than at some
|
|
35
|
+
* unlucky hour in production.
|
|
36
|
+
*/
|
|
37
|
+
export declare const defineRecurringSchedule: (recurrence: ScheduleRecurrence, runtime: QpqFunctionRuntime, options?: QPQConfigAdvancedScheduleSettings) => ScheduleQPQConfigSetting;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defineRecurringSchedule = void 0;
|
|
4
|
+
const QPQConfig_1 = require("../../QPQConfig");
|
|
5
|
+
const crossModuleUtils_1 = require("../../utils/crossModuleUtils");
|
|
6
|
+
const qpqFunctionRuntimeUtils_1 = require("../../utils/qpqFunctionRuntimeUtils");
|
|
7
|
+
const ScheduleTypeEnum_1 = require("./types/ScheduleTypeEnum");
|
|
8
|
+
const resolveScheduleFields_1 = require("./resolveScheduleFields");
|
|
9
|
+
/**
|
|
10
|
+
* Run a story on a recurring timetable.
|
|
11
|
+
*
|
|
12
|
+
* The recurrence is declared as intent, not as a cron string. Deployed, it is
|
|
13
|
+
* rendered to an AWS EventBridge cron expression; locally, the dev server's
|
|
14
|
+
* ticker matches the same declaration against the clock. Neither behaviour is
|
|
15
|
+
* a special case of the other, which is the point.
|
|
16
|
+
*
|
|
17
|
+
* **Every time is UTC.** The deployed scheduler evaluates in UTC, so there is
|
|
18
|
+
* no local-timezone option: a schedule that reads `{ dailyAtUtc: { hour: 17 } }`
|
|
19
|
+
* is 3am in Brisbane, and saying so in the config is better than a comment
|
|
20
|
+
* that drifts out of date twice a year.
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* // every ten minutes
|
|
24
|
+
* defineRecurringSchedule({ everyMinutes: 10 }, '/entry/schedule/onPoll::onPoll')
|
|
25
|
+
*
|
|
26
|
+
* // 5pm UTC daily
|
|
27
|
+
* defineRecurringSchedule({ dailyAtUtc: { hour: 17, minute: 0 } }, '/entry/schedule/onNightly::onNightly')
|
|
28
|
+
*
|
|
29
|
+
* // 2am UTC every Monday
|
|
30
|
+
* defineRecurringSchedule(
|
|
31
|
+
* { weeklyAtUtc: { day: DayOfWeek.Monday, hour: 2, minute: 0 } },
|
|
32
|
+
* '/entry/schedule/onDigest::onDigest',
|
|
33
|
+
* )
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* An interval that cannot be scheduled evenly (`{ everyMinutes: 7 }`) throws
|
|
37
|
+
* an `InvalidScheduleRecurrenceError` here, while the config is being
|
|
38
|
+
* evaluated, so it fails at synth and at dev-server boot rather than at some
|
|
39
|
+
* unlucky hour in production.
|
|
40
|
+
*/
|
|
41
|
+
const defineRecurringSchedule = (recurrence, runtime, options) => {
|
|
42
|
+
// Resolved and discarded: this is here to validate. Both renderers resolve
|
|
43
|
+
// it again for themselves, so storing the fields would be a second copy of
|
|
44
|
+
// the truth that could drift from the declaration beside it.
|
|
45
|
+
(0, resolveScheduleFields_1.resolveScheduleFields)(recurrence);
|
|
46
|
+
return {
|
|
47
|
+
configSettingType: QPQConfig_1.QPQCoreConfigSettingType.schedule,
|
|
48
|
+
uniqueKey: (0, qpqFunctionRuntimeUtils_1.getStoryNameFromQpqFunctionRuntime)(runtime),
|
|
49
|
+
scheduleType: ScheduleTypeEnum_1.ScheduleTypeEnum.Recurring,
|
|
50
|
+
runtime,
|
|
51
|
+
recurrence,
|
|
52
|
+
metadata: (options === null || options === void 0 ? void 0 : options.metadata) || {},
|
|
53
|
+
maxConcurrentExecutions: options === null || options === void 0 ? void 0 : options.maxConcurrentExecutions,
|
|
54
|
+
owner: (0, crossModuleUtils_1.convertCrossModuleOwnerToGenericResourceNameOverride)(options === null || options === void 0 ? void 0 : options.owner),
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
exports.defineRecurringSchedule = defineRecurringSchedule;
|
|
58
|
+
//# sourceMappingURL=defineRecurringSchedule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defineRecurringSchedule.js","sourceRoot":"","sources":["../../../../../src/config/settings/schedule/defineRecurringSchedule.ts"],"names":[],"mappings":";;;AACA,+CAA2D;AAC3D,mEAAoG;AACpG,iFAAyF;AAIzF,+DAA4D;AAC5D,mEAAgE;AAEhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACI,MAAM,uBAAuB,GAAG,CACrC,UAA8B,EAC9B,OAA2B,EAC3B,OAA2C,EACjB,EAAE;IAC5B,2EAA2E;IAC3E,2EAA2E;IAC3E,6DAA6D;IAC7D,IAAA,6CAAqB,EAAC,UAAU,CAAC,CAAC;IAElC,OAAO;QACL,iBAAiB,EAAE,oCAAwB,CAAC,QAAQ;QACpD,SAAS,EAAE,IAAA,4DAAkC,EAAC,OAAO,CAAC;QAEtD,YAAY,EAAE,mCAAgB,CAAC,SAAS;QAExC,OAAO;QAEP,UAAU;QAEV,QAAQ,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAQ,KAAI,EAAE;QAEjC,uBAAuB,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,uBAAuB;QAEzD,KAAK,EAAE,IAAA,uEAAoD,EAAC,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,KAAK,CAAC;KAC5E,CAAC;AACJ,CAAC,CAAC;AA1BW,QAAA,uBAAuB,2BA0BlC","sourcesContent":["import { QpqFunctionRuntime } from '../../../types';\nimport { QPQCoreConfigSettingType } from '../../QPQConfig';\nimport { convertCrossModuleOwnerToGenericResourceNameOverride } from '../../utils/crossModuleUtils';\nimport { getStoryNameFromQpqFunctionRuntime } from '../../utils/qpqFunctionRuntimeUtils';\nimport { QPQConfigAdvancedScheduleSettings } from './types/QPQConfigAdvancedScheduleSettings';\nimport { ScheduleQPQConfigSetting } from './types/ScheduleQPQConfigSetting';\nimport { ScheduleRecurrence } from './types/ScheduleRecurrence';\nimport { ScheduleTypeEnum } from './types/ScheduleTypeEnum';\nimport { resolveScheduleFields } from './resolveScheduleFields';\n\n/**\n * Run a story on a recurring timetable.\n *\n * The recurrence is declared as intent, not as a cron string. Deployed, it is\n * rendered to an AWS EventBridge cron expression; locally, the dev server's\n * ticker matches the same declaration against the clock. Neither behaviour is\n * a special case of the other, which is the point.\n *\n * **Every time is UTC.** The deployed scheduler evaluates in UTC, so there is\n * no local-timezone option: a schedule that reads `{ dailyAtUtc: { hour: 17 } }`\n * is 3am in Brisbane, and saying so in the config is better than a comment\n * that drifts out of date twice a year.\n *\n * ```ts\n * // every ten minutes\n * defineRecurringSchedule({ everyMinutes: 10 }, '/entry/schedule/onPoll::onPoll')\n *\n * // 5pm UTC daily\n * defineRecurringSchedule({ dailyAtUtc: { hour: 17, minute: 0 } }, '/entry/schedule/onNightly::onNightly')\n *\n * // 2am UTC every Monday\n * defineRecurringSchedule(\n * { weeklyAtUtc: { day: DayOfWeek.Monday, hour: 2, minute: 0 } },\n * '/entry/schedule/onDigest::onDigest',\n * )\n * ```\n *\n * An interval that cannot be scheduled evenly (`{ everyMinutes: 7 }`) throws\n * an `InvalidScheduleRecurrenceError` here, while the config is being\n * evaluated, so it fails at synth and at dev-server boot rather than at some\n * unlucky hour in production.\n */\nexport const defineRecurringSchedule = (\n recurrence: ScheduleRecurrence,\n runtime: QpqFunctionRuntime,\n options?: QPQConfigAdvancedScheduleSettings,\n): ScheduleQPQConfigSetting => {\n // Resolved and discarded: this is here to validate. Both renderers resolve\n // it again for themselves, so storing the fields would be a second copy of\n // the truth that could drift from the declaration beside it.\n resolveScheduleFields(recurrence);\n\n return {\n configSettingType: QPQCoreConfigSettingType.schedule,\n uniqueKey: getStoryNameFromQpqFunctionRuntime(runtime),\n\n scheduleType: ScheduleTypeEnum.Recurring,\n\n runtime,\n\n recurrence,\n\n metadata: options?.metadata || {},\n\n maxConcurrentExecutions: options?.maxConcurrentExecutions,\n\n owner: convertCrossModuleOwnerToGenericResourceNameOverride(options?.owner),\n };\n};\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./defineRecurringSchedule"), exports);
|
|
18
|
+
__exportStar(require("./InvalidScheduleRecurrenceError"), exports);
|
|
19
|
+
__exportStar(require("./resolveScheduleFields"), exports);
|
|
20
|
+
__exportStar(require("./types"), exports);
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/config/settings/schedule/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4DAA0C;AAC1C,mEAAiD;AACjD,0DAAwC;AACxC,0CAAwB","sourcesContent":["export * from './defineRecurringSchedule';\nexport * from './InvalidScheduleRecurrenceError';\nexport * from './resolveScheduleFields';\nexport * from './types';\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ScheduleFields } from './types/ScheduleFields';
|
|
2
|
+
import { ScheduleRecurrence } from './types/ScheduleRecurrence';
|
|
3
|
+
/**
|
|
4
|
+
* Turn a declared recurrence into the concrete field values both renderers
|
|
5
|
+
* consume, validating it on the way through.
|
|
6
|
+
*
|
|
7
|
+
* The single interpreter of ScheduleRecurrence. quidproquo-deploy-awscdk and
|
|
8
|
+
* quidproquo-dev-server both start from what this returns, which is what makes
|
|
9
|
+
* "it fires locally exactly when it would fire deployed" a property of one
|
|
10
|
+
* function rather than of two implementations agreeing.
|
|
11
|
+
*/
|
|
12
|
+
export declare const resolveScheduleFields: (recurrence: ScheduleRecurrence) => ScheduleFields;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveScheduleFields = void 0;
|
|
4
|
+
const DayOfWeek_1 = require("./types/DayOfWeek");
|
|
5
|
+
const InvalidScheduleRecurrenceError_1 = require("./InvalidScheduleRecurrenceError");
|
|
6
|
+
const assertInRange = (label, value, min, max) => {
|
|
7
|
+
if (!Number.isInteger(value) || value < min || value > max) {
|
|
8
|
+
throw new InvalidScheduleRecurrenceError_1.InvalidScheduleRecurrenceError(InvalidScheduleRecurrenceError_1.InvalidScheduleRecurrenceErrorCode.ValueOutOfRange, `Schedule ${label} must be a whole number between ${min} and ${max}, got ${value}.`);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Reject an interval that does not divide its unit evenly.
|
|
13
|
+
*
|
|
14
|
+
* Not pedantry. AWS renders these as `0/n`, which restarts at the top of every
|
|
15
|
+
* hour (or day), so `everyMinutes: 7` fires at :00 :07 ... :56 and then leaves
|
|
16
|
+
* a four minute gap. The dev server matching the same field list would agree
|
|
17
|
+
* with AWS, but neither would be what the author asked for, and the gap only
|
|
18
|
+
* shows up in production at an awkward hour. An interval that divides evenly
|
|
19
|
+
* means every consumer of these fields is saying the same thing.
|
|
20
|
+
*/
|
|
21
|
+
const assertDividesEvenly = (label, value, unit) => {
|
|
22
|
+
assertInRange(label, value, 1, unit);
|
|
23
|
+
if (unit % value !== 0) {
|
|
24
|
+
throw new InvalidScheduleRecurrenceError_1.InvalidScheduleRecurrenceError(InvalidScheduleRecurrenceError_1.InvalidScheduleRecurrenceErrorCode.IntervalDoesNotDivideEvenly, `Schedule ${label} must divide ${unit} evenly (got ${value}), or the interval would restart mid-cycle and leave an uneven gap. Valid values: ${everyDivisorOf(unit).join(', ')}.`);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const everyDivisorOf = (unit) => Array.from({ length: unit }, (_, i) => i + 1).filter((n) => unit % n === 0);
|
|
28
|
+
// 0, n, 2n ... up to but not including the unit.
|
|
29
|
+
const stepsOf = (interval, unit) => Array.from({ length: unit / interval }, (_, i) => i * interval);
|
|
30
|
+
const EVERY_VALUE = {
|
|
31
|
+
minutes: [],
|
|
32
|
+
hours: null,
|
|
33
|
+
daysOfMonth: null,
|
|
34
|
+
months: null,
|
|
35
|
+
daysOfWeek: null,
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Turn a declared recurrence into the concrete field values both renderers
|
|
39
|
+
* consume, validating it on the way through.
|
|
40
|
+
*
|
|
41
|
+
* The single interpreter of ScheduleRecurrence. quidproquo-deploy-awscdk and
|
|
42
|
+
* quidproquo-dev-server both start from what this returns, which is what makes
|
|
43
|
+
* "it fires locally exactly when it would fire deployed" a property of one
|
|
44
|
+
* function rather than of two implementations agreeing.
|
|
45
|
+
*/
|
|
46
|
+
const resolveScheduleFields = (recurrence) => {
|
|
47
|
+
var _a;
|
|
48
|
+
if ('everyMinutes' in recurrence) {
|
|
49
|
+
assertDividesEvenly('everyMinutes', recurrence.everyMinutes, 60);
|
|
50
|
+
return Object.assign(Object.assign({}, EVERY_VALUE), { minutes: stepsOf(recurrence.everyMinutes, 60) });
|
|
51
|
+
}
|
|
52
|
+
if ('everyHours' in recurrence) {
|
|
53
|
+
assertDividesEvenly('everyHours', recurrence.everyHours, 24);
|
|
54
|
+
const atMinute = (_a = recurrence.atMinute) !== null && _a !== void 0 ? _a : 0;
|
|
55
|
+
assertInRange('atMinute', atMinute, 0, 59);
|
|
56
|
+
return Object.assign(Object.assign({}, EVERY_VALUE), { minutes: [atMinute], hours: stepsOf(recurrence.everyHours, 24) });
|
|
57
|
+
}
|
|
58
|
+
if ('dailyAtUtc' in recurrence) {
|
|
59
|
+
const { hour, minute } = recurrence.dailyAtUtc;
|
|
60
|
+
assertInRange('hour', hour, 0, 23);
|
|
61
|
+
assertInRange('minute', minute, 0, 59);
|
|
62
|
+
return Object.assign(Object.assign({}, EVERY_VALUE), { minutes: [minute], hours: [hour] });
|
|
63
|
+
}
|
|
64
|
+
if ('weeklyAtUtc' in recurrence) {
|
|
65
|
+
const { day, hour, minute } = recurrence.weeklyAtUtc;
|
|
66
|
+
assertInRange('day', day, DayOfWeek_1.DayOfWeek.Sunday, DayOfWeek_1.DayOfWeek.Saturday);
|
|
67
|
+
assertInRange('hour', hour, 0, 23);
|
|
68
|
+
assertInRange('minute', minute, 0, 59);
|
|
69
|
+
return Object.assign(Object.assign({}, EVERY_VALUE), { minutes: [minute], hours: [hour], daysOfWeek: [day] });
|
|
70
|
+
}
|
|
71
|
+
if ('monthlyAtUtc' in recurrence) {
|
|
72
|
+
const { dayOfMonth, hour, minute } = recurrence.monthlyAtUtc;
|
|
73
|
+
// 29-31 are allowed and simply do not occur in every month, which is the
|
|
74
|
+
// same behaviour AWS has. Worth knowing rather than worth refusing.
|
|
75
|
+
assertInRange('dayOfMonth', dayOfMonth, 1, 31);
|
|
76
|
+
assertInRange('hour', hour, 0, 23);
|
|
77
|
+
assertInRange('minute', minute, 0, 59);
|
|
78
|
+
return Object.assign(Object.assign({}, EVERY_VALUE), { minutes: [minute], hours: [hour], daysOfMonth: [dayOfMonth] });
|
|
79
|
+
}
|
|
80
|
+
throw new InvalidScheduleRecurrenceError_1.InvalidScheduleRecurrenceError(InvalidScheduleRecurrenceError_1.InvalidScheduleRecurrenceErrorCode.UnknownRecurrence, `Unrecognised schedule recurrence: ${JSON.stringify(recurrence)}.`);
|
|
81
|
+
};
|
|
82
|
+
exports.resolveScheduleFields = resolveScheduleFields;
|
|
83
|
+
//# sourceMappingURL=resolveScheduleFields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveScheduleFields.js","sourceRoot":"","sources":["../../../../../src/config/settings/schedule/resolveScheduleFields.ts"],"names":[],"mappings":"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","sourcesContent":["import { DayOfWeek } from './types/DayOfWeek';\nimport { ScheduleFields } from './types/ScheduleFields';\nimport { ScheduleRecurrence } from './types/ScheduleRecurrence';\nimport { InvalidScheduleRecurrenceError, InvalidScheduleRecurrenceErrorCode } from './InvalidScheduleRecurrenceError';\n\nconst assertInRange = (label: string, value: number, min: number, max: number): void => {\n if (!Number.isInteger(value) || value < min || value > max) {\n throw new InvalidScheduleRecurrenceError(\n InvalidScheduleRecurrenceErrorCode.ValueOutOfRange,\n `Schedule ${label} must be a whole number between ${min} and ${max}, got ${value}.`,\n );\n }\n};\n\n/**\n * Reject an interval that does not divide its unit evenly.\n *\n * Not pedantry. AWS renders these as `0/n`, which restarts at the top of every\n * hour (or day), so `everyMinutes: 7` fires at :00 :07 ... :56 and then leaves\n * a four minute gap. The dev server matching the same field list would agree\n * with AWS, but neither would be what the author asked for, and the gap only\n * shows up in production at an awkward hour. An interval that divides evenly\n * means every consumer of these fields is saying the same thing.\n */\nconst assertDividesEvenly = (label: string, value: number, unit: number): void => {\n assertInRange(label, value, 1, unit);\n\n if (unit % value !== 0) {\n throw new InvalidScheduleRecurrenceError(\n InvalidScheduleRecurrenceErrorCode.IntervalDoesNotDivideEvenly,\n `Schedule ${label} must divide ${unit} evenly (got ${value}), or the interval would restart mid-cycle and leave an uneven gap. Valid values: ${everyDivisorOf(unit).join(', ')}.`,\n );\n }\n};\n\nconst everyDivisorOf = (unit: number): number[] => Array.from({ length: unit }, (_, i) => i + 1).filter((n) => unit % n === 0);\n\n// 0, n, 2n ... up to but not including the unit.\nconst stepsOf = (interval: number, unit: number): number[] => Array.from({ length: unit / interval }, (_, i) => i * interval);\n\nconst EVERY_VALUE: ScheduleFields = {\n minutes: [],\n hours: null,\n daysOfMonth: null,\n months: null,\n daysOfWeek: null,\n};\n\n/**\n * Turn a declared recurrence into the concrete field values both renderers\n * consume, validating it on the way through.\n *\n * The single interpreter of ScheduleRecurrence. quidproquo-deploy-awscdk and\n * quidproquo-dev-server both start from what this returns, which is what makes\n * \"it fires locally exactly when it would fire deployed\" a property of one\n * function rather than of two implementations agreeing.\n */\nexport const resolveScheduleFields = (recurrence: ScheduleRecurrence): ScheduleFields => {\n if ('everyMinutes' in recurrence) {\n assertDividesEvenly('everyMinutes', recurrence.everyMinutes, 60);\n\n return { ...EVERY_VALUE, minutes: stepsOf(recurrence.everyMinutes, 60) };\n }\n\n if ('everyHours' in recurrence) {\n assertDividesEvenly('everyHours', recurrence.everyHours, 24);\n\n const atMinute = recurrence.atMinute ?? 0;\n assertInRange('atMinute', atMinute, 0, 59);\n\n return { ...EVERY_VALUE, minutes: [atMinute], hours: stepsOf(recurrence.everyHours, 24) };\n }\n\n if ('dailyAtUtc' in recurrence) {\n const { hour, minute } = recurrence.dailyAtUtc;\n assertInRange('hour', hour, 0, 23);\n assertInRange('minute', minute, 0, 59);\n\n return { ...EVERY_VALUE, minutes: [minute], hours: [hour] };\n }\n\n if ('weeklyAtUtc' in recurrence) {\n const { day, hour, minute } = recurrence.weeklyAtUtc;\n assertInRange('day', day, DayOfWeek.Sunday, DayOfWeek.Saturday);\n assertInRange('hour', hour, 0, 23);\n assertInRange('minute', minute, 0, 59);\n\n return { ...EVERY_VALUE, minutes: [minute], hours: [hour], daysOfWeek: [day] };\n }\n\n if ('monthlyAtUtc' in recurrence) {\n const { dayOfMonth, hour, minute } = recurrence.monthlyAtUtc;\n\n // 29-31 are allowed and simply do not occur in every month, which is the\n // same behaviour AWS has. Worth knowing rather than worth refusing.\n assertInRange('dayOfMonth', dayOfMonth, 1, 31);\n assertInRange('hour', hour, 0, 23);\n assertInRange('minute', minute, 0, 59);\n\n return { ...EVERY_VALUE, minutes: [minute], hours: [hour], daysOfMonth: [dayOfMonth] };\n }\n\n throw new InvalidScheduleRecurrenceError(\n InvalidScheduleRecurrenceErrorCode.UnknownRecurrence,\n `Unrecognised schedule recurrence: ${JSON.stringify(recurrence)}.`,\n );\n};\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A day of the week, numbered the way JavaScript numbers them
|
|
3
|
+
* (`Date.getUTCDay()`), because the canonical schedule fields are matched
|
|
4
|
+
* against a real clock in the dev server.
|
|
5
|
+
*
|
|
6
|
+
* AWS numbers day-of-week 1-7 for SUN-SAT, so the cron renderer adds one. That
|
|
7
|
+
* offset lives in the renderer, which is the only place that should know
|
|
8
|
+
* anything about the AWS dialect.
|
|
9
|
+
*/
|
|
10
|
+
export declare enum DayOfWeek {
|
|
11
|
+
Sunday = 0,
|
|
12
|
+
Monday = 1,
|
|
13
|
+
Tuesday = 2,
|
|
14
|
+
Wednesday = 3,
|
|
15
|
+
Thursday = 4,
|
|
16
|
+
Friday = 5,
|
|
17
|
+
Saturday = 6
|
|
18
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DayOfWeek = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* A day of the week, numbered the way JavaScript numbers them
|
|
6
|
+
* (`Date.getUTCDay()`), because the canonical schedule fields are matched
|
|
7
|
+
* against a real clock in the dev server.
|
|
8
|
+
*
|
|
9
|
+
* AWS numbers day-of-week 1-7 for SUN-SAT, so the cron renderer adds one. That
|
|
10
|
+
* offset lives in the renderer, which is the only place that should know
|
|
11
|
+
* anything about the AWS dialect.
|
|
12
|
+
*/
|
|
13
|
+
var DayOfWeek;
|
|
14
|
+
(function (DayOfWeek) {
|
|
15
|
+
DayOfWeek[DayOfWeek["Sunday"] = 0] = "Sunday";
|
|
16
|
+
DayOfWeek[DayOfWeek["Monday"] = 1] = "Monday";
|
|
17
|
+
DayOfWeek[DayOfWeek["Tuesday"] = 2] = "Tuesday";
|
|
18
|
+
DayOfWeek[DayOfWeek["Wednesday"] = 3] = "Wednesday";
|
|
19
|
+
DayOfWeek[DayOfWeek["Thursday"] = 4] = "Thursday";
|
|
20
|
+
DayOfWeek[DayOfWeek["Friday"] = 5] = "Friday";
|
|
21
|
+
DayOfWeek[DayOfWeek["Saturday"] = 6] = "Saturday";
|
|
22
|
+
})(DayOfWeek || (exports.DayOfWeek = DayOfWeek = {}));
|
|
23
|
+
//# sourceMappingURL=DayOfWeek.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DayOfWeek.js","sourceRoot":"","sources":["../../../../../../src/config/settings/schedule/types/DayOfWeek.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;GAQG;AACH,IAAY,SAQX;AARD,WAAY,SAAS;IACnB,6CAAU,CAAA;IACV,6CAAU,CAAA;IACV,+CAAW,CAAA;IACX,mDAAa,CAAA;IACb,iDAAY,CAAA;IACZ,6CAAU,CAAA;IACV,iDAAY,CAAA;AACd,CAAC,EARW,SAAS,yBAAT,SAAS,QAQpB","sourcesContent":["/**\n * A day of the week, numbered the way JavaScript numbers them\n * (`Date.getUTCDay()`), because the canonical schedule fields are matched\n * against a real clock in the dev server.\n *\n * AWS numbers day-of-week 1-7 for SUN-SAT, so the cron renderer adds one. That\n * offset lives in the renderer, which is the only place that should know\n * anything about the AWS dialect.\n */\nexport enum DayOfWeek {\n Sunday = 0,\n Monday = 1,\n Tuesday = 2,\n Wednesday = 3,\n Thursday = 4,\n Friday = 5,\n Saturday = 6,\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CrossModuleOwner } from '../../../../types';
|
|
2
|
+
import { QPQConfigAdvancedSettings } from '../../../QPQConfig';
|
|
3
|
+
export interface QPQConfigAdvancedScheduleSettings extends QPQConfigAdvancedSettings {
|
|
4
|
+
metadata?: Record<string, any>;
|
|
5
|
+
owner?: CrossModuleOwner<'recurringSchedule'>;
|
|
6
|
+
maxConcurrentExecutions?: number;
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QPQConfigAdvancedScheduleSettings.js","sourceRoot":"","sources":["../../../../../../src/config/settings/schedule/types/QPQConfigAdvancedScheduleSettings.ts"],"names":[],"mappings":"","sourcesContent":["import { CrossModuleOwner } from '../../../../types';\nimport { QPQConfigAdvancedSettings } from '../../../QPQConfig';\n\nexport interface QPQConfigAdvancedScheduleSettings extends QPQConfigAdvancedSettings {\n metadata?: Record<string, any>;\n owner?: CrossModuleOwner<'recurringSchedule'>;\n\n // Cap (and guarantee) on this schedule's concurrent executions: never\n // throttled below it, never scales above it. Free, but carved out of the\n // deploy account's shared concurrency pool.\n maxConcurrentExecutions?: number;\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Nullable } from '../../../../types';
|
|
2
|
+
/**
|
|
3
|
+
* A resolved recurrence, as concrete field values.
|
|
4
|
+
*
|
|
5
|
+
* The one shape both renderers consume: quidproquo-deploy-awscdk turns it into
|
|
6
|
+
* an EventBridge cron expression, the dev server asks whether it matches a
|
|
7
|
+
* given UTC minute. Adding a ScheduleRecurrence variant means teaching
|
|
8
|
+
* resolveScheduleFields about it and nothing else.
|
|
9
|
+
*
|
|
10
|
+
* `null` means "every", which is not the same as listing every value: it is
|
|
11
|
+
* what lets the cron renderer emit `*`, and what tells it which of
|
|
12
|
+
* day-of-month / day-of-week should become AWS's `?`.
|
|
13
|
+
*
|
|
14
|
+
* `minutes` is never null, because a schedule that fires every minute of every
|
|
15
|
+
* hour still fires ON a minute.
|
|
16
|
+
*/
|
|
17
|
+
export type ScheduleFields = {
|
|
18
|
+
minutes: number[];
|
|
19
|
+
hours: Nullable<number[]>;
|
|
20
|
+
daysOfMonth: Nullable<number[]>;
|
|
21
|
+
months: Nullable<number[]>;
|
|
22
|
+
daysOfWeek: Nullable<number[]>;
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScheduleFields.js","sourceRoot":"","sources":["../../../../../../src/config/settings/schedule/types/ScheduleFields.ts"],"names":[],"mappings":"","sourcesContent":["import { Nullable } from '../../../../types';\n\n/**\n * A resolved recurrence, as concrete field values.\n *\n * The one shape both renderers consume: quidproquo-deploy-awscdk turns it into\n * an EventBridge cron expression, the dev server asks whether it matches a\n * given UTC minute. Adding a ScheduleRecurrence variant means teaching\n * resolveScheduleFields about it and nothing else.\n *\n * `null` means \"every\", which is not the same as listing every value: it is\n * what lets the cron renderer emit `*`, and what tells it which of\n * day-of-month / day-of-week should become AWS's `?`.\n *\n * `minutes` is never null, because a schedule that fires every minute of every\n * hour still fires ON a minute.\n */\nexport type ScheduleFields = {\n // 0-59\n minutes: number[];\n\n // 0-23\n hours: Nullable<number[]>;\n\n // 1-31\n daysOfMonth: Nullable<number[]>;\n\n // 1-12\n months: Nullable<number[]>;\n\n // 0-6, Sunday first (see DayOfWeek)\n daysOfWeek: Nullable<number[]>;\n};\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CrossModuleOwner, QpqFunctionRuntime } from '../../../../types';
|
|
2
|
+
import { QPQConfigSetting } from '../../../QPQConfig';
|
|
3
|
+
import { ScheduleRecurrence } from './ScheduleRecurrence';
|
|
4
|
+
import { ScheduleTypeEnum } from './ScheduleTypeEnum';
|
|
5
|
+
export interface ScheduleQPQConfigSetting extends QPQConfigSetting {
|
|
6
|
+
scheduleType: ScheduleTypeEnum;
|
|
7
|
+
runtime: QpqFunctionRuntime;
|
|
8
|
+
recurrence: ScheduleRecurrence;
|
|
9
|
+
metadata: Record<string, any>;
|
|
10
|
+
maxConcurrentExecutions?: number;
|
|
11
|
+
owner?: CrossModuleOwner;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScheduleQPQConfigSetting.js","sourceRoot":"","sources":["../../../../../../src/config/settings/schedule/types/ScheduleQPQConfigSetting.ts"],"names":[],"mappings":"","sourcesContent":["import { CrossModuleOwner, QpqFunctionRuntime } from '../../../../types';\nimport { QPQConfigSetting } from '../../../QPQConfig';\nimport { ScheduleRecurrence } from './ScheduleRecurrence';\nimport { ScheduleTypeEnum } from './ScheduleTypeEnum';\n\nexport interface ScheduleQPQConfigSetting extends QPQConfigSetting {\n scheduleType: ScheduleTypeEnum;\n\n runtime: QpqFunctionRuntime;\n\n // Platform-neutral. The AWS cron string is rendered from this at deploy\n // time; it is deliberately not stored.\n recurrence: ScheduleRecurrence;\n\n metadata: Record<string, any>;\n\n maxConcurrentExecutions?: number;\n\n owner?: CrossModuleOwner;\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { DayOfWeek } from './DayOfWeek';
|
|
2
|
+
/**
|
|
3
|
+
* How often a recurring schedule runs, declared as intent rather than as a
|
|
4
|
+
* cron string.
|
|
5
|
+
*
|
|
6
|
+
* Deliberately platform-neutral: an AWS EventBridge cron expression is one
|
|
7
|
+
* RENDERING of this, produced in quidproquo-deploy-awscdk, and the dev server
|
|
8
|
+
* matches the same declaration against the clock. Nothing here knows what a
|
|
9
|
+
* `?` is.
|
|
10
|
+
*
|
|
11
|
+
* Every time is UTC. There is no local-timezone variant on purpose: the
|
|
12
|
+
* deployed scheduler evaluates in UTC, so offering one would be a promise the
|
|
13
|
+
* platform cannot keep.
|
|
14
|
+
*
|
|
15
|
+
* Deliberately absent, so the next reader does not take either for an
|
|
16
|
+
* oversight:
|
|
17
|
+
*
|
|
18
|
+
* - **A year.** A recurrence pinned to a year is not recurring, it is a
|
|
19
|
+
* schedule with an expiry, and it fails by going quiet rather than by
|
|
20
|
+
* erroring. A one-shot at a date wants its own ScheduleTypeEnum member and
|
|
21
|
+
* its own define, not a field every existing caller has to leave as "any".
|
|
22
|
+
* - **A month.** ScheduleFields carries `months` and both renderers honour it,
|
|
23
|
+
* but nothing here sets it yet - `yearlyAtUtc` is the obvious next variant
|
|
24
|
+
* and lands as a resolveScheduleFields change alone. Left out until
|
|
25
|
+
* something asks for it.
|
|
26
|
+
*
|
|
27
|
+
* This union is a closed set, which is the cost of dropping the raw cron
|
|
28
|
+
* string: a consumer who needs something it cannot say has no local workaround
|
|
29
|
+
* and has to wait for a release. Adding a variant is additive and cheap, so
|
|
30
|
+
* add one rather than making anybody wait long.
|
|
31
|
+
*/
|
|
32
|
+
export type ScheduleRecurrence = {
|
|
33
|
+
everyMinutes: number;
|
|
34
|
+
} | {
|
|
35
|
+
everyHours: number;
|
|
36
|
+
atMinute?: number;
|
|
37
|
+
} | {
|
|
38
|
+
dailyAtUtc: {
|
|
39
|
+
hour: number;
|
|
40
|
+
minute: number;
|
|
41
|
+
};
|
|
42
|
+
} | {
|
|
43
|
+
weeklyAtUtc: {
|
|
44
|
+
day: DayOfWeek;
|
|
45
|
+
hour: number;
|
|
46
|
+
minute: number;
|
|
47
|
+
};
|
|
48
|
+
} | {
|
|
49
|
+
monthlyAtUtc: {
|
|
50
|
+
dayOfMonth: number;
|
|
51
|
+
hour: number;
|
|
52
|
+
minute: number;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScheduleRecurrence.js","sourceRoot":"","sources":["../../../../../../src/config/settings/schedule/types/ScheduleRecurrence.ts"],"names":[],"mappings":"","sourcesContent":["import { DayOfWeek } from './DayOfWeek';\n\n/**\n * How often a recurring schedule runs, declared as intent rather than as a\n * cron string.\n *\n * Deliberately platform-neutral: an AWS EventBridge cron expression is one\n * RENDERING of this, produced in quidproquo-deploy-awscdk, and the dev server\n * matches the same declaration against the clock. Nothing here knows what a\n * `?` is.\n *\n * Every time is UTC. There is no local-timezone variant on purpose: the\n * deployed scheduler evaluates in UTC, so offering one would be a promise the\n * platform cannot keep.\n *\n * Deliberately absent, so the next reader does not take either for an\n * oversight:\n *\n * - **A year.** A recurrence pinned to a year is not recurring, it is a\n * schedule with an expiry, and it fails by going quiet rather than by\n * erroring. A one-shot at a date wants its own ScheduleTypeEnum member and\n * its own define, not a field every existing caller has to leave as \"any\".\n * - **A month.** ScheduleFields carries `months` and both renderers honour it,\n * but nothing here sets it yet - `yearlyAtUtc` is the obvious next variant\n * and lands as a resolveScheduleFields change alone. Left out until\n * something asks for it.\n *\n * This union is a closed set, which is the cost of dropping the raw cron\n * string: a consumer who needs something it cannot say has no local workaround\n * and has to wait for a release. Adding a variant is additive and cheap, so\n * add one rather than making anybody wait long.\n */\nexport type ScheduleRecurrence =\n // Must divide 60 - see resolveScheduleFields for why an awkward interval is\n // rejected rather than approximated.\n | { everyMinutes: number }\n\n // Must divide 24. `atMinute` defaults to 0.\n | { everyHours: number; atMinute?: number }\n | { dailyAtUtc: { hour: number; minute: number } }\n | { weeklyAtUtc: { day: DayOfWeek; hour: number; minute: number } }\n | { monthlyAtUtc: { dayOfMonth: number; hour: number; minute: number } };\n"]}
|