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.
Files changed (109) hide show
  1. package/lib/commonjs/actions/keyValueStore/askKeyValueStoreUpsert.d.ts +1 -1
  2. package/lib/commonjs/actions/keyValueStore/askKeyValueStoreUpsert.js +6 -2
  3. package/lib/commonjs/actions/keyValueStore/askKeyValueStoreUpsert.js.map +1 -1
  4. package/lib/commonjs/actions/keyValueStore/askKeyValueStoreUpsertMany.d.ts +2 -1
  5. package/lib/commonjs/actions/keyValueStore/askKeyValueStoreUpsertMany.js +7 -0
  6. package/lib/commonjs/actions/keyValueStore/askKeyValueStoreUpsertMany.js.map +1 -1
  7. package/lib/commonjs/config/settings/schedule/InvalidScheduleRecurrenceError.d.ts +20 -0
  8. package/lib/commonjs/config/settings/schedule/InvalidScheduleRecurrenceError.js +29 -0
  9. package/lib/commonjs/config/settings/schedule/InvalidScheduleRecurrenceError.js.map +1 -0
  10. package/lib/commonjs/config/settings/schedule/defineRecurringSchedule.d.ts +37 -0
  11. package/lib/commonjs/config/settings/schedule/defineRecurringSchedule.js +58 -0
  12. package/lib/commonjs/config/settings/schedule/defineRecurringSchedule.js.map +1 -0
  13. package/lib/commonjs/config/settings/schedule/index.d.ts +4 -0
  14. package/lib/commonjs/config/settings/schedule/index.js +21 -0
  15. package/lib/commonjs/config/settings/schedule/index.js.map +1 -0
  16. package/lib/commonjs/config/settings/schedule/resolveScheduleFields.d.ts +12 -0
  17. package/lib/commonjs/config/settings/schedule/resolveScheduleFields.js +83 -0
  18. package/lib/commonjs/config/settings/schedule/resolveScheduleFields.js.map +1 -0
  19. package/lib/commonjs/config/settings/schedule/types/DayOfWeek.d.ts +18 -0
  20. package/lib/commonjs/config/settings/schedule/types/DayOfWeek.js +23 -0
  21. package/lib/commonjs/config/settings/schedule/types/DayOfWeek.js.map +1 -0
  22. package/lib/commonjs/config/settings/schedule/types/QPQConfigAdvancedScheduleSettings.d.ts +7 -0
  23. package/lib/commonjs/config/settings/schedule/types/QPQConfigAdvancedScheduleSettings.js +3 -0
  24. package/lib/commonjs/config/settings/schedule/types/QPQConfigAdvancedScheduleSettings.js.map +1 -0
  25. package/lib/commonjs/config/settings/schedule/types/ScheduleFields.d.ts +23 -0
  26. package/lib/commonjs/config/settings/schedule/types/ScheduleFields.js +3 -0
  27. package/lib/commonjs/config/settings/schedule/types/ScheduleFields.js.map +1 -0
  28. package/lib/commonjs/config/settings/schedule/types/ScheduleQPQConfigSetting.d.ts +12 -0
  29. package/lib/commonjs/config/settings/schedule/types/ScheduleQPQConfigSetting.js +3 -0
  30. package/lib/commonjs/config/settings/schedule/types/ScheduleQPQConfigSetting.js.map +1 -0
  31. package/lib/commonjs/config/settings/schedule/types/ScheduleRecurrence.d.ts +54 -0
  32. package/lib/commonjs/config/settings/schedule/types/ScheduleRecurrence.js +3 -0
  33. package/lib/commonjs/config/settings/schedule/types/ScheduleRecurrence.js.map +1 -0
  34. package/lib/commonjs/config/settings/schedule/types/ScheduleTypeEnum.d.ts +3 -0
  35. package/lib/commonjs/config/settings/schedule/types/ScheduleTypeEnum.js +8 -0
  36. package/lib/commonjs/config/settings/schedule/types/ScheduleTypeEnum.js.map +1 -0
  37. package/lib/commonjs/config/settings/schedule/types/index.d.ts +6 -0
  38. package/lib/commonjs/config/settings/schedule/types/index.js +23 -0
  39. package/lib/commonjs/config/settings/schedule/types/index.js.map +1 -0
  40. package/lib/commonjs/config/settings/userDirectory.d.ts +0 -1
  41. package/lib/commonjs/config/settings/userDirectory.js.map +1 -1
  42. package/lib/commonjs/stories/kvs/askKeyValueStoreUpsertWithRetry.js +4 -1
  43. package/lib/commonjs/stories/kvs/askKeyValueStoreUpsertWithRetry.js.map +1 -1
  44. package/lib/commonjs/types/QpqFunctionRuntime.d.ts +2 -4
  45. package/lib/commonjs/types/QpqFunctionRuntime.js.map +1 -1
  46. package/lib/commonjs/types/QpqPureFunction.d.ts +10 -0
  47. package/lib/commonjs/types/QpqPureFunction.js +3 -0
  48. package/lib/commonjs/types/QpqPureFunction.js.map +1 -0
  49. package/lib/commonjs/types/index.d.ts +1 -0
  50. package/lib/commonjs/types/index.js +1 -0
  51. package/lib/commonjs/types/index.js.map +1 -1
  52. package/lib/esm/actions/keyValueStore/askKeyValueStoreUpsert.d.ts +1 -1
  53. package/lib/esm/actions/keyValueStore/askKeyValueStoreUpsert.js +6 -2
  54. package/lib/esm/actions/keyValueStore/askKeyValueStoreUpsert.js.map +1 -1
  55. package/lib/esm/actions/keyValueStore/askKeyValueStoreUpsertMany.d.ts +2 -1
  56. package/lib/esm/actions/keyValueStore/askKeyValueStoreUpsertMany.js +7 -0
  57. package/lib/esm/actions/keyValueStore/askKeyValueStoreUpsertMany.js.map +1 -1
  58. package/lib/esm/config/settings/schedule/InvalidScheduleRecurrenceError.d.ts +20 -0
  59. package/lib/esm/config/settings/schedule/InvalidScheduleRecurrenceError.js +26 -0
  60. package/lib/esm/config/settings/schedule/InvalidScheduleRecurrenceError.js.map +1 -0
  61. package/lib/esm/config/settings/schedule/defineRecurringSchedule.d.ts +37 -0
  62. package/lib/esm/config/settings/schedule/defineRecurringSchedule.js +54 -0
  63. package/lib/esm/config/settings/schedule/defineRecurringSchedule.js.map +1 -0
  64. package/lib/esm/config/settings/schedule/index.d.ts +4 -0
  65. package/lib/esm/config/settings/schedule/index.js +5 -0
  66. package/lib/esm/config/settings/schedule/index.js.map +1 -0
  67. package/lib/esm/config/settings/schedule/resolveScheduleFields.d.ts +12 -0
  68. package/lib/esm/config/settings/schedule/resolveScheduleFields.js +78 -0
  69. package/lib/esm/config/settings/schedule/resolveScheduleFields.js.map +1 -0
  70. package/lib/esm/config/settings/schedule/types/DayOfWeek.d.ts +18 -0
  71. package/lib/esm/config/settings/schedule/types/DayOfWeek.js +20 -0
  72. package/lib/esm/config/settings/schedule/types/DayOfWeek.js.map +1 -0
  73. package/lib/esm/config/settings/schedule/types/QPQConfigAdvancedScheduleSettings.d.ts +7 -0
  74. package/lib/esm/config/settings/schedule/types/QPQConfigAdvancedScheduleSettings.js +2 -0
  75. package/lib/esm/config/settings/schedule/types/QPQConfigAdvancedScheduleSettings.js.map +1 -0
  76. package/lib/esm/config/settings/schedule/types/ScheduleFields.d.ts +23 -0
  77. package/lib/esm/config/settings/schedule/types/ScheduleFields.js +2 -0
  78. package/lib/esm/config/settings/schedule/types/ScheduleFields.js.map +1 -0
  79. package/lib/esm/config/settings/schedule/types/ScheduleQPQConfigSetting.d.ts +12 -0
  80. package/lib/esm/config/settings/schedule/types/ScheduleQPQConfigSetting.js +2 -0
  81. package/lib/esm/config/settings/schedule/types/ScheduleQPQConfigSetting.js.map +1 -0
  82. package/lib/esm/config/settings/schedule/types/ScheduleRecurrence.d.ts +54 -0
  83. package/lib/esm/config/settings/schedule/types/ScheduleRecurrence.js +2 -0
  84. package/lib/esm/config/settings/schedule/types/ScheduleRecurrence.js.map +1 -0
  85. package/lib/esm/config/settings/schedule/types/ScheduleTypeEnum.d.ts +3 -0
  86. package/lib/esm/config/settings/schedule/types/ScheduleTypeEnum.js +5 -0
  87. package/lib/esm/config/settings/schedule/types/ScheduleTypeEnum.js.map +1 -0
  88. package/lib/esm/config/settings/schedule/types/index.d.ts +6 -0
  89. package/lib/esm/config/settings/schedule/types/index.js +7 -0
  90. package/lib/esm/config/settings/schedule/types/index.js.map +1 -0
  91. package/lib/esm/config/settings/userDirectory.d.ts +0 -1
  92. package/lib/esm/config/settings/userDirectory.js.map +1 -1
  93. package/lib/esm/stories/kvs/askKeyValueStoreUpsertWithRetry.js +4 -1
  94. package/lib/esm/stories/kvs/askKeyValueStoreUpsertWithRetry.js.map +1 -1
  95. package/lib/esm/types/QpqFunctionRuntime.d.ts +2 -4
  96. package/lib/esm/types/QpqFunctionRuntime.js.map +1 -1
  97. package/lib/esm/types/QpqPureFunction.d.ts +10 -0
  98. package/lib/esm/types/QpqPureFunction.js +2 -0
  99. package/lib/esm/types/QpqPureFunction.js.map +1 -0
  100. package/lib/esm/types/index.d.ts +1 -0
  101. package/lib/esm/types/index.js +1 -0
  102. package/lib/esm/types/index.js.map +1 -1
  103. package/package.json +2 -2
  104. package/lib/commonjs/config/settings/schedule.d.ts +0 -79
  105. package/lib/commonjs/config/settings/schedule.js +0 -85
  106. package/lib/commonjs/config/settings/schedule.js.map +0 -1
  107. package/lib/esm/config/settings/schedule.d.ts +0 -79
  108. package/lib/esm/config/settings/schedule.js +0 -81
  109. 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. Namespaced
13
- // not ErrorTypeEnum.Conflict so retry logic can target the write race
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":";;;AAkCA,wDAEC;AAnCD,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,2EAA2E;QAC3E,wEAAwE;QACxE,6DAA6D;QAC7D,UAAU;QACV,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. Namespaced —\n // not ErrorTypeEnum.Conflict so retry logic can target the write race\n // specifically without also catching domain-level conflicts.\n 'Conflict',\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"]}
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":";;;AAgCA,gEAMC;AArCD,mFAAgF;AAChF,uEAAoE;AAQvD,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;KACf;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 // 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 ],\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"]}
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,4 @@
1
+ export * from './defineRecurringSchedule';
2
+ export * from './InvalidScheduleRecurrenceError';
3
+ export * from './resolveScheduleFields';
4
+ export * from './types';
@@ -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,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=QPQConfigAdvancedScheduleSettings.js.map
@@ -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,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=ScheduleFields.js.map
@@ -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,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=ScheduleQPQConfigSetting.js.map
@@ -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,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=ScheduleRecurrence.js.map
@@ -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"]}
@@ -0,0 +1,3 @@
1
+ export declare enum ScheduleTypeEnum {
2
+ Recurring = "Recurring"
3
+ }