quidproquo-core 0.1.12 → 0.1.14

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 (87) hide show
  1. package/lib/commonjs/actions/keyValueStore/KeyValueStoreActionType.d.ts +2 -1
  2. package/lib/commonjs/actions/keyValueStore/KeyValueStoreActionType.js +1 -0
  3. package/lib/commonjs/actions/keyValueStore/KeyValueStoreActionType.js.map +1 -1
  4. package/lib/commonjs/actions/keyValueStore/KeyValueStoreQueryActionTypes.d.ts +1 -0
  5. package/lib/commonjs/actions/keyValueStore/KeyValueStoreQueryActionTypes.js.map +1 -1
  6. package/lib/commonjs/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.d.ts +32 -0
  7. package/lib/commonjs/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.js +47 -0
  8. package/lib/commonjs/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.js.map +1 -0
  9. package/lib/commonjs/actions/keyValueStore/KeyValueStoreScanAllScopesActionTypes.d.ts +23 -0
  10. package/lib/commonjs/actions/keyValueStore/KeyValueStoreScanAllScopesActionTypes.js +3 -0
  11. package/lib/commonjs/actions/keyValueStore/KeyValueStoreScanAllScopesActionTypes.js.map +1 -0
  12. package/lib/commonjs/actions/keyValueStore/index.d.ts +2 -0
  13. package/lib/commonjs/actions/keyValueStore/index.js +2 -0
  14. package/lib/commonjs/actions/keyValueStore/index.js.map +1 -1
  15. package/lib/commonjs/actions/keyValueStore/types/KvsQueryOperation.d.ts +2 -2
  16. package/lib/commonjs/actions/keyValueStore/types/KvsQueryOperation.js.map +1 -1
  17. package/lib/commonjs/config/settings/keyValueStore.d.ts +38 -1
  18. package/lib/commonjs/config/settings/keyValueStore.js +1 -0
  19. package/lib/commonjs/config/settings/keyValueStore.js.map +1 -1
  20. package/lib/commonjs/config/settings/queue.js +14 -3
  21. package/lib/commonjs/config/settings/queue.js.map +1 -1
  22. package/lib/commonjs/logic/actionLogic.js +10 -0
  23. package/lib/commonjs/logic/actionLogic.js.map +1 -1
  24. package/lib/commonjs/logic/index.d.ts +1 -0
  25. package/lib/commonjs/logic/index.js +1 -0
  26. package/lib/commonjs/logic/index.js.map +1 -1
  27. package/lib/commonjs/logic/scope/scopedKvsValue.d.ts +4 -0
  28. package/lib/commonjs/logic/scope/scopedKvsValue.js +19 -1
  29. package/lib/commonjs/logic/scope/scopedKvsValue.js.map +1 -1
  30. package/lib/commonjs/logic/transientErrorNames.d.ts +20 -0
  31. package/lib/commonjs/logic/transientErrorNames.js +52 -0
  32. package/lib/commonjs/logic/transientErrorNames.js.map +1 -0
  33. package/lib/commonjs/qpqCoreUtils.js +7 -0
  34. package/lib/commonjs/qpqCoreUtils.js.map +1 -1
  35. package/lib/commonjs/types/KvsStreamEvent.d.ts +14 -0
  36. package/lib/commonjs/types/KvsStreamEvent.js +16 -0
  37. package/lib/commonjs/types/KvsStreamEvent.js.map +1 -0
  38. package/lib/commonjs/types/StorySession.d.ts +1 -0
  39. package/lib/commonjs/types/StorySession.js +1 -0
  40. package/lib/commonjs/types/StorySession.js.map +1 -1
  41. package/lib/commonjs/types/index.d.ts +1 -0
  42. package/lib/commonjs/types/index.js +1 -0
  43. package/lib/commonjs/types/index.js.map +1 -1
  44. package/lib/esm/actions/keyValueStore/KeyValueStoreActionType.d.ts +2 -1
  45. package/lib/esm/actions/keyValueStore/KeyValueStoreActionType.js +1 -0
  46. package/lib/esm/actions/keyValueStore/KeyValueStoreActionType.js.map +1 -1
  47. package/lib/esm/actions/keyValueStore/KeyValueStoreQueryActionTypes.d.ts +1 -0
  48. package/lib/esm/actions/keyValueStore/KeyValueStoreQueryActionTypes.js.map +1 -1
  49. package/lib/esm/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.d.ts +32 -0
  50. package/lib/esm/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.js +43 -0
  51. package/lib/esm/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.js.map +1 -0
  52. package/lib/esm/actions/keyValueStore/KeyValueStoreScanAllScopesActionTypes.d.ts +23 -0
  53. package/lib/esm/actions/keyValueStore/KeyValueStoreScanAllScopesActionTypes.js +2 -0
  54. package/lib/esm/actions/keyValueStore/KeyValueStoreScanAllScopesActionTypes.js.map +1 -0
  55. package/lib/esm/actions/keyValueStore/index.d.ts +2 -0
  56. package/lib/esm/actions/keyValueStore/index.js +2 -0
  57. package/lib/esm/actions/keyValueStore/index.js.map +1 -1
  58. package/lib/esm/actions/keyValueStore/types/KvsQueryOperation.d.ts +2 -2
  59. package/lib/esm/actions/keyValueStore/types/KvsQueryOperation.js.map +1 -1
  60. package/lib/esm/config/settings/keyValueStore.d.ts +38 -1
  61. package/lib/esm/config/settings/keyValueStore.js +1 -0
  62. package/lib/esm/config/settings/keyValueStore.js.map +1 -1
  63. package/lib/esm/config/settings/queue.js +12 -1
  64. package/lib/esm/config/settings/queue.js.map +1 -1
  65. package/lib/esm/logic/actionLogic.js +10 -0
  66. package/lib/esm/logic/actionLogic.js.map +1 -1
  67. package/lib/esm/logic/index.d.ts +1 -0
  68. package/lib/esm/logic/index.js +1 -0
  69. package/lib/esm/logic/index.js.map +1 -1
  70. package/lib/esm/logic/scope/scopedKvsValue.d.ts +4 -0
  71. package/lib/esm/logic/scope/scopedKvsValue.js +17 -0
  72. package/lib/esm/logic/scope/scopedKvsValue.js.map +1 -1
  73. package/lib/esm/logic/transientErrorNames.d.ts +20 -0
  74. package/lib/esm/logic/transientErrorNames.js +48 -0
  75. package/lib/esm/logic/transientErrorNames.js.map +1 -0
  76. package/lib/esm/qpqCoreUtils.js +7 -0
  77. package/lib/esm/qpqCoreUtils.js.map +1 -1
  78. package/lib/esm/types/KvsStreamEvent.d.ts +14 -0
  79. package/lib/esm/types/KvsStreamEvent.js +13 -0
  80. package/lib/esm/types/KvsStreamEvent.js.map +1 -0
  81. package/lib/esm/types/StorySession.d.ts +1 -0
  82. package/lib/esm/types/StorySession.js +1 -0
  83. package/lib/esm/types/StorySession.js.map +1 -1
  84. package/lib/esm/types/index.d.ts +1 -0
  85. package/lib/esm/types/index.js +1 -0
  86. package/lib/esm/types/index.js.map +1 -1
  87. package/package.json +2 -2
@@ -5,5 +5,6 @@ export declare enum KeyValueStoreActionType {
5
5
  Delete = "@quidproquo-core/KeyValueStore/Delete",
6
6
  Update = "@quidproquo-core/KeyValueStore/Update",
7
7
  Query = "@quidproquo-core/KeyValueStore/Query",
8
- Scan = "@quidproquo-core/KeyValueStore/Scan"
8
+ Scan = "@quidproquo-core/KeyValueStore/Scan",
9
+ ScanAllScopes = "@quidproquo-core/KeyValueStore/ScanAllScopes"
9
10
  }
@@ -10,5 +10,6 @@ var KeyValueStoreActionType;
10
10
  KeyValueStoreActionType["Update"] = "@quidproquo-core/KeyValueStore/Update";
11
11
  KeyValueStoreActionType["Query"] = "@quidproquo-core/KeyValueStore/Query";
12
12
  KeyValueStoreActionType["Scan"] = "@quidproquo-core/KeyValueStore/Scan";
13
+ KeyValueStoreActionType["ScanAllScopes"] = "@quidproquo-core/KeyValueStore/ScanAllScopes";
13
14
  })(KeyValueStoreActionType || (exports.KeyValueStoreActionType = KeyValueStoreActionType = {}));
14
15
  //# sourceMappingURL=KeyValueStoreActionType.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"KeyValueStoreActionType.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/KeyValueStoreActionType.ts"],"names":[],"mappings":";;;AAAA,IAAY,uBAQX;AARD,WAAY,uBAAuB;IACjC,2EAAgD,CAAA;IAChD,qEAA0C,CAAA;IAC1C,2EAAgD,CAAA;IAChD,2EAAgD,CAAA;IAChD,2EAAgD,CAAA;IAChD,yEAA8C,CAAA;IAC9C,uEAA4C,CAAA;AAC9C,CAAC,EARW,uBAAuB,uCAAvB,uBAAuB,QAQlC","sourcesContent":["export enum KeyValueStoreActionType {\n Upsert = '@quidproquo-core/KeyValueStore/Upsert',\n Get = '@quidproquo-core/KeyValueStore/Get',\n GetAll = '@quidproquo-core/KeyValueStore/GetAll',\n Delete = '@quidproquo-core/KeyValueStore/Delete',\n Update = '@quidproquo-core/KeyValueStore/Update',\n Query = '@quidproquo-core/KeyValueStore/Query',\n Scan = '@quidproquo-core/KeyValueStore/Scan',\n}\n"]}
1
+ {"version":3,"file":"KeyValueStoreActionType.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/KeyValueStoreActionType.ts"],"names":[],"mappings":";;;AAAA,IAAY,uBASX;AATD,WAAY,uBAAuB;IACjC,2EAAgD,CAAA;IAChD,qEAA0C,CAAA;IAC1C,2EAAgD,CAAA;IAChD,2EAAgD,CAAA;IAChD,2EAAgD,CAAA;IAChD,yEAA8C,CAAA;IAC9C,uEAA4C,CAAA;IAC5C,yFAA8D,CAAA;AAChE,CAAC,EATW,uBAAuB,uCAAvB,uBAAuB,QASlC","sourcesContent":["export enum KeyValueStoreActionType {\n Upsert = '@quidproquo-core/KeyValueStore/Upsert',\n Get = '@quidproquo-core/KeyValueStore/Get',\n GetAll = '@quidproquo-core/KeyValueStore/GetAll',\n Delete = '@quidproquo-core/KeyValueStore/Delete',\n Update = '@quidproquo-core/KeyValueStore/Update',\n Query = '@quidproquo-core/KeyValueStore/Query',\n Scan = '@quidproquo-core/KeyValueStore/Scan',\n ScanAllScopes = '@quidproquo-core/KeyValueStore/ScanAllScopes',\n}\n"]}
@@ -9,6 +9,7 @@ export interface KeyValueStoreQueryOptions {
9
9
  nextPageKey?: string;
10
10
  filter?: KvsQueryOperation;
11
11
  scope?: string;
12
+ consistentRead?: boolean;
12
13
  }
13
14
  export interface KeyValueStoreQueryActionPayload {
14
15
  keyValueStoreName: string;
@@ -1 +1 @@
1
- {"version":3,"file":"KeyValueStoreQueryActionTypes.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/KeyValueStoreQueryActionTypes.ts"],"names":[],"mappings":"","sourcesContent":["import { QpqPagedData } from '../../types';\nimport { Action, ActionProcessor, ActionRequester } from '../../types/Action';\nimport { KeyValueStoreActionType } from './KeyValueStoreActionType';\nimport { KvsQueryOperation } from './types';\n\n// Options Type\nexport interface KeyValueStoreQueryOptions {\n // Accepted but not implemented: no processor applies a TTL to query results.\n ttlInSeconds?: number;\n sortAscending?: boolean;\n limit?: number;\n nextPageKey?: string;\n filter?: KvsQueryOperation;\n // Composed into partition-key conditions by the processor; requires a string-typed partition key.\n scope?: string;\n}\n\n// Payload\nexport interface KeyValueStoreQueryActionPayload {\n keyValueStoreName: string;\n\n keyCondition: KvsQueryOperation;\n\n options?: KeyValueStoreQueryOptions;\n}\n\n// Action\nexport interface KeyValueStoreQueryAction extends Action<KeyValueStoreQueryActionPayload> {\n type: KeyValueStoreActionType.Query;\n payload: KeyValueStoreQueryActionPayload;\n}\n\n// Function Types\nexport type KeyValueStoreQueryActionProcessor<KvsItem> = ActionProcessor<KeyValueStoreQueryAction, QpqPagedData<KvsItem>>;\nexport type KeyValueStoreQueryActionRequester<KvsItem> = ActionRequester<KeyValueStoreQueryAction, QpqPagedData<KvsItem>>;\n"]}
1
+ {"version":3,"file":"KeyValueStoreQueryActionTypes.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/KeyValueStoreQueryActionTypes.ts"],"names":[],"mappings":"","sourcesContent":["import { QpqPagedData } from '../../types';\nimport { Action, ActionProcessor, ActionRequester } from '../../types/Action';\nimport { KeyValueStoreActionType } from './KeyValueStoreActionType';\nimport { KvsQueryOperation } from './types';\n\n// Options Type\nexport interface KeyValueStoreQueryOptions {\n // Accepted but not implemented: no processor applies a TTL to query results.\n ttlInSeconds?: number;\n sortAscending?: boolean;\n limit?: number;\n nextPageKey?: string;\n filter?: KvsQueryOperation;\n // Composed into partition-key conditions by the processor; requires a string-typed partition key.\n scope?: string;\n // Read the writer's own most recent writes, at the cost of a slower and (on DynamoDB) doubly-charged\n // read.\n //\n // Default reads are EVENTUALLY consistent, which is right for a viewer and wrong for a caller that just\n // wrote and is now reading back to decide something. That case is not hypothetical: a flow run appends\n // its terminal event and immediately re-lists the log to fold the outcome, and under load the terminal\n // event is intermittently not yet visible — so a run that succeeded reads back as having no outcome at\n // all, with nothing recorded to explain it.\n //\n // Use it for read-your-own-writes and for any fold used as a COORDINATION primitive (deciding \"am I\n // last\", \"did this finish\"). Do not use it for ordinary reads.\n consistentRead?: boolean;\n}\n\n// Payload\nexport interface KeyValueStoreQueryActionPayload {\n keyValueStoreName: string;\n\n keyCondition: KvsQueryOperation;\n\n options?: KeyValueStoreQueryOptions;\n}\n\n// Action\nexport interface KeyValueStoreQueryAction extends Action<KeyValueStoreQueryActionPayload> {\n type: KeyValueStoreActionType.Query;\n payload: KeyValueStoreQueryActionPayload;\n}\n\n// Function Types\nexport type KeyValueStoreQueryActionProcessor<KvsItem> = ActionProcessor<KeyValueStoreQueryAction, QpqPagedData<KvsItem>>;\nexport type KeyValueStoreQueryActionRequester<KvsItem> = ActionRequester<KeyValueStoreQueryAction, QpqPagedData<KvsItem>>;\n"]}
@@ -0,0 +1,32 @@
1
+ import { KeyValueStoreScanAllScopesActionRequester, KeyValueStoreScanAllScopesOptions } from './KeyValueStoreScanAllScopesActionTypes';
2
+ import { KvsQueryOperation } from './types';
3
+ export declare const KeyValueStoreScanAllScopesErrorTypeEnum: {
4
+ ResourceNotFound: string;
5
+ ServiceUnavailable: string;
6
+ StoreNotFound: string;
7
+ };
8
+ /**
9
+ * Read EVERY row in a store, across every scope, each paired with the scope it lives under.
10
+ *
11
+ * This deliberately crosses the tenant boundary that the rest of the KVS layer works hard to
12
+ * hold. An ordinary unscoped Scan cannot see scoped rows at all: the translator ANDs on a
13
+ * predicate that excludes them, precisely so one tenant's request can never read another's.
14
+ * That is the right default and nothing about it changes.
15
+ *
16
+ * The exception exists for MIGRATIONS, where the job is "rewrite every row" and any row you
17
+ * cannot see is data you silently leave behind. The alternative is asking the app to
18
+ * enumerate its own tenants and loop, which fails quietly the moment that list is incomplete.
19
+ *
20
+ * So: reach for this only from a migration or an equivalent whole-store operation, never on a
21
+ * request path. Nothing in the framework calls it, and a caller with a user's request in hand
22
+ * has no business using it — scope is what keeps tenants apart, and this steps over it.
23
+ *
24
+ * The scope comes back per row rather than being discarded, so a caller can write each row
25
+ * back where it came from.
26
+ *
27
+ * DRAIN THE PAGE KEY: loop until `nextPageKey` comes back undefined. Every backend pages,
28
+ * including the local one, deliberately so — a backend that returned everything in one call
29
+ * would let a caller stop after the first page, be right locally, and silently skip most of
30
+ * the data deployed. A local run has to be able to prove the loop.
31
+ */
32
+ export declare function askKeyValueStoreScanAllScopes<KvsItem>(keyValueStoreName: string, filterCondition?: KvsQueryOperation, nextPageKey?: string, options?: KeyValueStoreScanAllScopesOptions): KeyValueStoreScanAllScopesActionRequester<KvsItem>;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KeyValueStoreScanAllScopesErrorTypeEnum = void 0;
4
+ exports.askKeyValueStoreScanAllScopes = askKeyValueStoreScanAllScopes;
5
+ const types_1 = require("../../types");
6
+ const KeyValueStoreActionType_1 = require("./KeyValueStoreActionType");
7
+ exports.KeyValueStoreScanAllScopesErrorTypeEnum = (0, types_1.createErrorEnumForAction)(KeyValueStoreActionType_1.KeyValueStoreActionType.ScanAllScopes, [
8
+ 'ServiceUnavailable', // DynamoDB internal error / throttling
9
+ 'ResourceNotFound', // the underlying table does not exist
10
+ 'StoreNotFound', // the store is not declared in the qpq config (misconfiguration)
11
+ ]);
12
+ /**
13
+ * Read EVERY row in a store, across every scope, each paired with the scope it lives under.
14
+ *
15
+ * This deliberately crosses the tenant boundary that the rest of the KVS layer works hard to
16
+ * hold. An ordinary unscoped Scan cannot see scoped rows at all: the translator ANDs on a
17
+ * predicate that excludes them, precisely so one tenant's request can never read another's.
18
+ * That is the right default and nothing about it changes.
19
+ *
20
+ * The exception exists for MIGRATIONS, where the job is "rewrite every row" and any row you
21
+ * cannot see is data you silently leave behind. The alternative is asking the app to
22
+ * enumerate its own tenants and loop, which fails quietly the moment that list is incomplete.
23
+ *
24
+ * So: reach for this only from a migration or an equivalent whole-store operation, never on a
25
+ * request path. Nothing in the framework calls it, and a caller with a user's request in hand
26
+ * has no business using it — scope is what keeps tenants apart, and this steps over it.
27
+ *
28
+ * The scope comes back per row rather than being discarded, so a caller can write each row
29
+ * back where it came from.
30
+ *
31
+ * DRAIN THE PAGE KEY: loop until `nextPageKey` comes back undefined. Every backend pages,
32
+ * including the local one, deliberately so — a backend that returned everything in one call
33
+ * would let a caller stop after the first page, be right locally, and silently skip most of
34
+ * the data deployed. A local run has to be able to prove the loop.
35
+ */
36
+ function* askKeyValueStoreScanAllScopes(keyValueStoreName, filterCondition, nextPageKey, options) {
37
+ return yield {
38
+ type: KeyValueStoreActionType_1.KeyValueStoreActionType.ScanAllScopes,
39
+ payload: {
40
+ keyValueStoreName,
41
+ filterCondition,
42
+ nextPageKey,
43
+ options,
44
+ },
45
+ };
46
+ }
47
+ //# sourceMappingURL=KeyValueStoreScanAllScopesActionRequester.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyValueStoreScanAllScopesActionRequester.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.ts"],"names":[],"mappings":";;;AAmCA,sEAkBC;AArDD,uCAAuD;AACvD,uEAAoE;AAIvD,QAAA,uCAAuC,GAAG,IAAA,gCAAwB,EAAC,iDAAuB,CAAC,aAAa,EAAE;IACrH,oBAAoB,EAAE,uCAAuC;IAC7D,kBAAkB,EAAE,sCAAsC;IAC1D,eAAe,EAAE,iEAAiE;CACnF,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,QAAe,CAAC,CAAC,6BAA6B,CAC5C,iBAAyB,EACzB,eAAmC,EACnC,WAAoB,EACpB,OAA2C;IAE3C,OAAO,MAAM;QACX,IAAI,EAAE,iDAAuB,CAAC,aAAa;QAC3C,OAAO,EAAE;YACP,iBAAiB;YAEjB,eAAe;YAEf,WAAW;YAEX,OAAO;SACR;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { createErrorEnumForAction } from '../../types';\nimport { KeyValueStoreActionType } from './KeyValueStoreActionType';\nimport { KeyValueStoreScanAllScopesActionRequester, KeyValueStoreScanAllScopesOptions } from './KeyValueStoreScanAllScopesActionTypes';\nimport { KvsQueryOperation } from './types';\n\nexport const KeyValueStoreScanAllScopesErrorTypeEnum = createErrorEnumForAction(KeyValueStoreActionType.ScanAllScopes, [\n 'ServiceUnavailable', // DynamoDB internal error / throttling\n 'ResourceNotFound', // the underlying table does not exist\n 'StoreNotFound', // the store is not declared in the qpq config (misconfiguration)\n]);\n\n/**\n * Read EVERY row in a store, across every scope, each paired with the scope it lives under.\n *\n * This deliberately crosses the tenant boundary that the rest of the KVS layer works hard to\n * hold. An ordinary unscoped Scan cannot see scoped rows at all: the translator ANDs on a\n * predicate that excludes them, precisely so one tenant's request can never read another's.\n * That is the right default and nothing about it changes.\n *\n * The exception exists for MIGRATIONS, where the job is \"rewrite every row\" and any row you\n * cannot see is data you silently leave behind. The alternative is asking the app to\n * enumerate its own tenants and loop, which fails quietly the moment that list is incomplete.\n *\n * So: reach for this only from a migration or an equivalent whole-store operation, never on a\n * request path. Nothing in the framework calls it, and a caller with a user's request in hand\n * has no business using it — scope is what keeps tenants apart, and this steps over it.\n *\n * The scope comes back per row rather than being discarded, so a caller can write each row\n * back where it came from.\n *\n * DRAIN THE PAGE KEY: loop until `nextPageKey` comes back undefined. Every backend pages,\n * including the local one, deliberately so — a backend that returned everything in one call\n * would let a caller stop after the first page, be right locally, and silently skip most of\n * the data deployed. A local run has to be able to prove the loop.\n */\nexport function* askKeyValueStoreScanAllScopes<KvsItem>(\n keyValueStoreName: string,\n filterCondition?: KvsQueryOperation,\n nextPageKey?: string,\n options?: KeyValueStoreScanAllScopesOptions,\n): KeyValueStoreScanAllScopesActionRequester<KvsItem> {\n return yield {\n type: KeyValueStoreActionType.ScanAllScopes,\n payload: {\n keyValueStoreName,\n\n filterCondition,\n\n nextPageKey,\n\n options,\n },\n };\n}\n"]}
@@ -0,0 +1,23 @@
1
+ import { QpqPagedData } from '../../types';
2
+ import { Action, ActionProcessor, ActionRequester } from '../../types/Action';
3
+ import { KeyValueStoreActionType } from './KeyValueStoreActionType';
4
+ import { KvsQueryOperation } from './types';
5
+ export type KvsScopedItem<KvsItem> = {
6
+ scope?: string;
7
+ item: KvsItem;
8
+ };
9
+ export interface KeyValueStoreScanAllScopesOptions {
10
+ limit?: number;
11
+ }
12
+ export interface KeyValueStoreScanAllScopesActionPayload {
13
+ keyValueStoreName: string;
14
+ filterCondition?: KvsQueryOperation;
15
+ nextPageKey?: string;
16
+ options?: KeyValueStoreScanAllScopesOptions;
17
+ }
18
+ export interface KeyValueStoreScanAllScopesAction extends Action<KeyValueStoreScanAllScopesActionPayload> {
19
+ type: KeyValueStoreActionType.ScanAllScopes;
20
+ payload: KeyValueStoreScanAllScopesActionPayload;
21
+ }
22
+ export type KeyValueStoreScanAllScopesActionProcessor<KvsItem> = ActionProcessor<KeyValueStoreScanAllScopesAction, QpqPagedData<KvsScopedItem<KvsItem>>>;
23
+ export type KeyValueStoreScanAllScopesActionRequester<KvsItem> = ActionRequester<KeyValueStoreScanAllScopesAction, QpqPagedData<KvsScopedItem<KvsItem>>>;
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=KeyValueStoreScanAllScopesActionTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeyValueStoreScanAllScopesActionTypes.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/KeyValueStoreScanAllScopesActionTypes.ts"],"names":[],"mappings":"","sourcesContent":["import { QpqPagedData } from '../../types';\nimport { Action, ActionProcessor, ActionRequester } from '../../types/Action';\nimport { KeyValueStoreActionType } from './KeyValueStoreActionType';\nimport { KvsQueryOperation } from './types';\n\n// One row and the scope it lives under. `scope` is absent for an unscoped row.\n//\n// The scope is reported rather than dropped BECAUSE this crosses scopes: a caller that reads\n// every tenant's rows in one pass has to know which tenant each belongs to, or anything it\n// writes back lands in the wrong partition. Handing back bare items would make silent\n// cross-tenant corruption the easy mistake.\nexport type KvsScopedItem<KvsItem> = {\n scope?: string;\n item: KvsItem;\n};\n\n// Options Type\nexport interface KeyValueStoreScanAllScopesOptions {\n limit?: number;\n}\n\n// Payload\nexport interface KeyValueStoreScanAllScopesActionPayload {\n keyValueStoreName: string;\n\n filterCondition?: KvsQueryOperation;\n\n nextPageKey?: string;\n\n options?: KeyValueStoreScanAllScopesOptions;\n}\n\n// Action\nexport interface KeyValueStoreScanAllScopesAction extends Action<KeyValueStoreScanAllScopesActionPayload> {\n type: KeyValueStoreActionType.ScanAllScopes;\n payload: KeyValueStoreScanAllScopesActionPayload;\n}\n\n// Function Types\nexport type KeyValueStoreScanAllScopesActionProcessor<KvsItem> = ActionProcessor<\n KeyValueStoreScanAllScopesAction,\n QpqPagedData<KvsScopedItem<KvsItem>>\n>;\nexport type KeyValueStoreScanAllScopesActionRequester<KvsItem> = ActionRequester<\n KeyValueStoreScanAllScopesAction,\n QpqPagedData<KvsScopedItem<KvsItem>>\n>;\n"]}
@@ -1,3 +1,5 @@
1
+ export * from './KeyValueStoreScanAllScopesActionTypes';
2
+ export * from './KeyValueStoreScanAllScopesActionRequester';
1
3
  export * from './KeyValueStoreActionType';
2
4
  export * from './KeyValueStoreDeleteActionRequester';
3
5
  export * from './KeyValueStoreDeleteActionTypes';
@@ -14,6 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./KeyValueStoreScanAllScopesActionTypes"), exports);
18
+ __exportStar(require("./KeyValueStoreScanAllScopesActionRequester"), exports);
17
19
  __exportStar(require("./KeyValueStoreActionType"), exports);
18
20
  __exportStar(require("./KeyValueStoreDeleteActionRequester"), exports);
19
21
  __exportStar(require("./KeyValueStoreDeleteActionTypes"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4DAA0C;AAC1C,uEAAqD;AACrD,mEAAiD;AACjD,oEAAkD;AAClD,gEAA8C;AAC9C,uEAAqD;AACrD,mEAAiD;AACjD,sEAAoD;AACpD,kEAAgD;AAChD,qEAAmD;AACnD,iEAA+C;AAC/C,uEAAqD;AACrD,mEAAiD;AACjD,uEAAqD;AACrD,mEAAiD;AACjD,0CAAwB;AACxB,0CAAwB","sourcesContent":["export * from './KeyValueStoreActionType';\nexport * from './KeyValueStoreDeleteActionRequester';\nexport * from './KeyValueStoreDeleteActionTypes';\nexport * from './KeyValueStoreGetActionRequester';\nexport * from './KeyValueStoreGetActionTypes';\nexport * from './KeyValueStoreGetAllActionRequester';\nexport * from './KeyValueStoreGetAllActionTypes';\nexport * from './KeyValueStoreQueryActionRequester';\nexport * from './KeyValueStoreQueryActionTypes';\nexport * from './KeyValueStoreScanActionRequester';\nexport * from './KeyValueStoreScanActionTypes';\nexport * from './KeyValueStoreUpdateActionRequester';\nexport * from './KeyValueStoreUpdateActionTypes';\nexport * from './KeyValueStoreUpsertActionRequester';\nexport * from './KeyValueStoreUpsertActionTypes';\nexport * from './types';\nexport * from './utils';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0EAAwD;AACxD,8EAA4D;AAC5D,4DAA0C;AAC1C,uEAAqD;AACrD,mEAAiD;AACjD,oEAAkD;AAClD,gEAA8C;AAC9C,uEAAqD;AACrD,mEAAiD;AACjD,sEAAoD;AACpD,kEAAgD;AAChD,qEAAmD;AACnD,iEAA+C;AAC/C,uEAAqD;AACrD,mEAAiD;AACjD,uEAAqD;AACrD,mEAAiD;AACjD,0CAAwB;AACxB,0CAAwB","sourcesContent":["export * from './KeyValueStoreScanAllScopesActionTypes';\nexport * from './KeyValueStoreScanAllScopesActionRequester';\nexport * from './KeyValueStoreActionType';\nexport * from './KeyValueStoreDeleteActionRequester';\nexport * from './KeyValueStoreDeleteActionTypes';\nexport * from './KeyValueStoreGetActionRequester';\nexport * from './KeyValueStoreGetActionTypes';\nexport * from './KeyValueStoreGetAllActionRequester';\nexport * from './KeyValueStoreGetAllActionTypes';\nexport * from './KeyValueStoreQueryActionRequester';\nexport * from './KeyValueStoreQueryActionTypes';\nexport * from './KeyValueStoreScanActionRequester';\nexport * from './KeyValueStoreScanActionTypes';\nexport * from './KeyValueStoreUpdateActionRequester';\nexport * from './KeyValueStoreUpdateActionTypes';\nexport * from './KeyValueStoreUpsertActionRequester';\nexport * from './KeyValueStoreUpsertActionTypes';\nexport * from './types';\nexport * from './utils';\n"]}
@@ -1,9 +1,9 @@
1
1
  import { KvsLogicalOperatorType, KvsQueryOperationType } from './KvsQueryOperationType';
2
2
  export type KvsCoreDataType = string | number;
3
3
  export type KvsBasicDataType = KvsCoreDataType | boolean;
4
- export type KvsListDataType = KvsBasicDataType[];
4
+ export type KvsListDataType = KvsAdvancedDataType[];
5
5
  export interface KvsObjectDataType {
6
- [key: string]: KvsAdvancedDataType;
6
+ [key: string]: KvsAdvancedDataType | undefined;
7
7
  }
8
8
  export type KvsAdvancedDataType = KvsBasicDataType | KvsListDataType | KvsObjectDataType;
9
9
  export type KvsQueryCondition = {
@@ -1 +1 @@
1
- {"version":3,"file":"KvsQueryOperation.js","sourceRoot":"","sources":["../../../../../src/actions/keyValueStore/types/KvsQueryOperation.ts"],"names":[],"mappings":"","sourcesContent":["import { KvsLogicalOperatorType, KvsQueryOperationType } from './KvsQueryOperationType';\n\nexport type KvsCoreDataType = string | number;\nexport type KvsBasicDataType = KvsCoreDataType | boolean;\nexport type KvsListDataType = KvsBasicDataType[];\n\n// Use an interface for the recursive data type\nexport interface KvsObjectDataType {\n [key: string]: KvsAdvancedDataType;\n}\n\nexport type KvsAdvancedDataType = KvsBasicDataType | KvsListDataType | KvsObjectDataType;\n\n// Type for individual query conditions\nexport type KvsQueryCondition = {\n key: string;\n operation: KvsQueryOperationType;\n valueA?: KvsAdvancedDataType;\n valueB?: KvsBasicDataType;\n};\n\n// Type for logical operators\nexport type KvsLogicalOperator = {\n operation: KvsLogicalOperatorType;\n conditions: (KvsQueryCondition | KvsLogicalOperator)[];\n};\n\n// Type for the overall query operation\nexport type KvsQueryOperation = KvsQueryCondition | KvsLogicalOperator;\n"]}
1
+ {"version":3,"file":"KvsQueryOperation.js","sourceRoot":"","sources":["../../../../../src/actions/keyValueStore/types/KvsQueryOperation.ts"],"names":[],"mappings":"","sourcesContent":["import { KvsLogicalOperatorType, KvsQueryOperationType } from './KvsQueryOperationType';\n\nexport type KvsCoreDataType = string | number;\nexport type KvsBasicDataType = KvsCoreDataType | boolean;\n\nexport type KvsListDataType = KvsAdvancedDataType[];\n\n// Use an interface for the recursive data type. Members may be undefined so that a\n// domain model with OPTIONAL fields can be written directly; the marshaller drops\n// undefined members, since DynamoDB has no representation for them.\nexport interface KvsObjectDataType {\n [key: string]: KvsAdvancedDataType | undefined;\n}\n\nexport type KvsAdvancedDataType = KvsBasicDataType | KvsListDataType | KvsObjectDataType;\n\n// Type for individual query conditions\nexport type KvsQueryCondition = {\n key: string;\n operation: KvsQueryOperationType;\n valueA?: KvsAdvancedDataType;\n valueB?: KvsBasicDataType;\n};\n\n// Type for logical operators\nexport type KvsLogicalOperator = {\n operation: KvsLogicalOperatorType;\n conditions: (KvsQueryCondition | KvsLogicalOperator)[];\n};\n\n// Type for the overall query operation\nexport type KvsQueryOperation = KvsQueryCondition | KvsLogicalOperator;\n"]}
@@ -1,4 +1,4 @@
1
- import { CrossModuleOwner, KeyOf } from '../../types';
1
+ import { CrossModuleOwner, KeyOf, QpqFunctionRuntime } from '../../types';
2
2
  import { QPQConfigAdvancedSettings, QPQConfigSetting } from '../QPQConfig';
3
3
  export type KvsKeyType = 'string' | 'number' | 'binary';
4
4
  export type KvsKey<T extends object = any> = {
@@ -16,6 +16,41 @@ type CompositeCompositeKvsIndex<T extends object = any> = {
16
16
  sortKey?: CompositeKvsKey<T>;
17
17
  };
18
18
  export type CompositeKvsIndex<T extends object = any> = KeyOf<T> | CompositeCompositeKvsIndex<T>;
19
+ /**
20
+ * Change data capture on a store: the table gets a stream (NEW_AND_OLD_IMAGES) and
21
+ * `runtime` is invoked with the changes.
22
+ *
23
+ * Records arrive in order PER PARTITION KEY, and different partition keys are processed
24
+ * concurrently. A stream handler therefore gets one-writer-per-item serialisation for
25
+ * free, which is what makes a stream the natural place to maintain a projection of a table
26
+ * you only ever append to — no queue, no extra write on the append path, and no trigger to
27
+ * lose, because the record exists precisely because the write happened.
28
+ *
29
+ * Mirrors defineStorageDrive's `onEvent`.
30
+ */
31
+ export type KvsStreamSettings = {
32
+ runtime: QpqFunctionRuntime;
33
+ /**
34
+ * Collapse each delivered batch down to ONE record per partition key, keeping the latest.
35
+ *
36
+ * Off by default: a generic consumer (audit trail, change notifications) needs to see
37
+ * every change, and the framework should never silently drop records.
38
+ *
39
+ * On for a PROJECTION, where the payload that matters is "this key changed" and the
40
+ * handler re-derives from source anyway. Without it, the batch is fanned back out into
41
+ * one story run per record and the batching bought nothing: 500 appends to one document
42
+ * would mean 500 re-derivations, each reading a log that grows as it goes. With it, that
43
+ * batch is a single re-derivation.
44
+ */
45
+ coalesceByPartitionKey?: boolean;
46
+ /** Records per invocation. DynamoDB streams allow up to 1000; the mapping default is 100. */
47
+ batchSize?: number;
48
+ /**
49
+ * How long to wait accumulating records before invoking, 0 to 300 seconds. Trades
50
+ * projection latency for far fewer invocations, and gives coalescing more to collapse.
51
+ */
52
+ maximumBatchingWindowInSeconds?: number;
53
+ };
19
54
  export interface QPQConfigAdvancedKeyValueStoreSettings<T extends object = any> extends QPQConfigAdvancedSettings {
20
55
  indexes?: CompositeKvsIndex<T>[];
21
56
  global?: boolean;
@@ -23,6 +58,7 @@ export interface QPQConfigAdvancedKeyValueStoreSettings<T extends object = any>
23
58
  ttlAttribute?: string;
24
59
  disablePointInTimeRecovery?: boolean;
25
60
  encryption?: boolean;
61
+ onStream?: KvsStreamSettings;
26
62
  }
27
63
  export interface KeyValueStoreQPQConfigSetting<T extends object = any> extends QPQConfigSetting {
28
64
  keyValueStoreName: string;
@@ -33,6 +69,7 @@ export interface KeyValueStoreQPQConfigSetting<T extends object = any> extends Q
33
69
  ttlAttribute?: string;
34
70
  disablePointInTimeRecovery: boolean;
35
71
  encryption: boolean;
72
+ onStream?: KvsStreamSettings;
36
73
  }
37
74
  export declare const defineKeyValueStore: <T extends object = any>(keyValueStoreName: string, partitionKey: CompositeKvsKey<T>, sortKeys?: CompositeKvsKey<T>[], options?: QPQConfigAdvancedKeyValueStoreSettings<T>) => KeyValueStoreQPQConfigSetting<T>;
38
75
  export {};
@@ -43,6 +43,7 @@ const defineKeyValueStore = (keyValueStoreName, partitionKey, sortKeys = [], opt
43
43
  ttlAttribute: options === null || options === void 0 ? void 0 : options.ttlAttribute,
44
44
  disablePointInTimeRecovery: (_c = options === null || options === void 0 ? void 0 : options.disablePointInTimeRecovery) !== null && _c !== void 0 ? _c : false,
45
45
  encryption: (_d = options === null || options === void 0 ? void 0 : options.encryption) !== null && _d !== void 0 ? _d : false,
46
+ onStream: options === null || options === void 0 ? void 0 : options.onStream,
46
47
  });
47
48
  };
48
49
  exports.defineKeyValueStore = defineKeyValueStore;
@@ -1 +1 @@
1
- {"version":3,"file":"keyValueStore.js","sourceRoot":"","sources":["../../../../src/config/settings/keyValueStore.ts"],"names":[],"mappings":";;;AACA,4CAAqG;AAmB9F,MAAM,MAAM,GAAG,CAAyB,GAAa,EAAE,OAAmB,QAAQ,EAAa,EAAE,CAAC,CAAC;IACxG,GAAG;IACH,IAAI;CACL,CAAC,CAAC;AAHU,QAAA,MAAM,UAGhB;AAWH,MAAM,8BAA8B,GAAG,CAAyB,eAAmC,EAAa,EAAE;IAChH,oBAAoB;IACpB,IAAI,OAAO,eAAe,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,IAAA,cAAM,EAAI,eAA2B,EAAE,QAAQ,CAAC,CAAC;IAC1D,CAAC;IAED,sBAAsB;IACtB,OAAO,eAA4B,CAAC;AACtC,CAAC,CAAC;AAEF,MAAM,8CAA8C,GAAG,CACrD,iBAAuC,EACa,EAAE;IACtD,OAAO,OAAO,iBAAiB,KAAK,QAAQ,CAAC;AAC/C,CAAC,CAAC;AAEF,MAAM,kCAAkC,GAAG,CAAyB,iBAAuC,EAAe,EAAE;IAC1H,IAAI,CAAC,8CAA8C,CAAI,iBAAiB,CAAC,EAAE,CAAC;QAC1E,OAAO;YACL,YAAY,EAAE,IAAA,cAAM,EAAI,iBAAiB,EAAE,QAAQ,CAAC;SACrD,CAAC;IACJ,CAAC;IAED,OAAO;QACL,YAAY,EAAE,8BAA8B,CAAI,iBAAiB,CAAC,YAAY,CAAC;QAC/E,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAI,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;KAC9G,CAAC;AACJ,CAAC,CAAC;AAkCK,MAAM,mBAAmB,GAAG,CACjC,iBAAyB,EAEzB,YAAgC,EAChC,WAAiC,EAAE,EAEnC,OAAmD,EACjB,EAAE;;IAAC,OAAA,CAAC;QACtC,iBAAiB,EAAE,oCAAwB,CAAC,aAAa;QACzD,SAAS,EAAE,iBAAiB;QAE5B,iBAAiB;QAEjB,YAAY,EAAE,8BAA8B,CAAI,YAAY,CAAC;QAC7D,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAA,8BAAiC,CAAA,CAAC;QAEzD,OAAO,EAAE,CAAC,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,mCAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA,kCAAqC,CAAA,CAAC;QAE5E,MAAM,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,mCAAI,KAAK;QAEhC,KAAK,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,KAAK;QAErB,YAAY,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,YAAY;QAEnC,0BAA0B,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,0BAA0B,mCAAI,KAAK;QAExE,UAAU,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,UAAU,mCAAI,KAAK;KACzC,CAAC,CAAA;CAAA,CAAC;AA3BU,QAAA,mBAAmB,uBA2B7B","sourcesContent":["import { CrossModuleOwner, KeyOf } from '../../types';\nimport { QPQConfigAdvancedSettings, QPQConfigSetting, QPQCoreConfigSettingType } from '../QPQConfig';\n\n// TODO: When we put validation on the QPQConfig, we need to make sure\n// Key Length and Data Type: DynamoDB has limitations on key length and data type.\n// Your KvsKeyType only supports 'string', 'number', and 'binary' types. Make sure\n// that you're validating these in convertKvsKeyToDynamodbAttribute function and\n// also ensure that key length is within DynamoDB's specified limits.\n\nexport type KvsKeyType = 'string' | 'number' | 'binary';\nexport type KvsKey<T extends object = any> = {\n key: KeyOf<T>;\n type: KvsKeyType;\n};\n\nexport type KvsIndex<T extends object = any> = {\n partitionKey: KvsKey<T>;\n sortKey?: KvsKey<T>;\n};\n\nexport const kvsKey = <T extends object = any>(key: KeyOf<T>, type: KvsKeyType = 'string'): KvsKey<T> => ({\n key,\n type,\n});\n\ntype CompositeKvsKey<T extends object = any> = KvsKey<T> | KeyOf<T>;\n\ntype CompositeCompositeKvsIndex<T extends object = any> = {\n partitionKey: CompositeKvsKey<T>;\n sortKey?: CompositeKvsKey<T>;\n};\n\nexport type CompositeKvsIndex<T extends object = any> = KeyOf<T> | CompositeCompositeKvsIndex<T>;\n\nconst convertCompositeKvsKeyToKvsKey = <T extends object = any>(compositeKvsKey: CompositeKvsKey<T>): KvsKey<T> => {\n // Must be a keyof T\n if (typeof compositeKvsKey === 'string') {\n return kvsKey<T>(compositeKvsKey as KeyOf<T>, 'string');\n }\n\n // must be a KvsKey<T>\n return compositeKvsKey as KvsKey<T>;\n};\n\nconst isCompositeKvsIndexACompositeCompositeKvsIndex = <T extends object = any>(\n compositeKvsIndex: CompositeKvsIndex<T>,\n): compositeKvsIndex is CompositeCompositeKvsIndex<T> => {\n return typeof compositeKvsIndex !== 'string';\n};\n\nconst convertCompositeKvsIndexToKvsIndex = <T extends object = any>(compositeKvsIndex: CompositeKvsIndex<T>): KvsIndex<T> => {\n if (!isCompositeKvsIndexACompositeCompositeKvsIndex<T>(compositeKvsIndex)) {\n return {\n partitionKey: kvsKey<T>(compositeKvsIndex, 'string'),\n };\n }\n\n return {\n partitionKey: convertCompositeKvsKeyToKvsKey<T>(compositeKvsIndex.partitionKey),\n sortKey: compositeKvsIndex.sortKey ? convertCompositeKvsKeyToKvsKey<T>(compositeKvsIndex.sortKey) : undefined,\n };\n};\n\nexport interface QPQConfigAdvancedKeyValueStoreSettings<T extends object = any> extends QPQConfigAdvancedSettings {\n indexes?: CompositeKvsIndex<T>[];\n\n global?: boolean;\n\n owner?: CrossModuleOwner;\n\n ttlAttribute?: string;\n\n // Point-in-time recovery (35-day continuous backups) is on by default; set this to opt out.\n disablePointInTimeRecovery?: boolean;\n\n encryption?: boolean;\n}\n\nexport interface KeyValueStoreQPQConfigSetting<T extends object = any> extends QPQConfigSetting {\n keyValueStoreName: string;\n\n partitionKey: KvsKey<T>;\n sortKeys: KvsKey<T>[];\n\n indexes: KvsIndex<T>[];\n\n global: boolean;\n\n ttlAttribute?: string;\n\n disablePointInTimeRecovery: boolean;\n\n encryption: boolean;\n}\n\nexport const defineKeyValueStore = <T extends object = any>(\n keyValueStoreName: string,\n\n partitionKey: CompositeKvsKey<T>,\n sortKeys: CompositeKvsKey<T>[] = [],\n\n options?: QPQConfigAdvancedKeyValueStoreSettings<T>,\n): KeyValueStoreQPQConfigSetting<T> => ({\n configSettingType: QPQCoreConfigSettingType.keyValueStore,\n uniqueKey: keyValueStoreName,\n\n keyValueStoreName,\n\n partitionKey: convertCompositeKvsKeyToKvsKey<T>(partitionKey),\n sortKeys: sortKeys.map(convertCompositeKvsKeyToKvsKey<T>),\n\n indexes: (options?.indexes ?? []).map(convertCompositeKvsIndexToKvsIndex<T>),\n\n global: options?.global ?? false,\n\n owner: options?.owner,\n\n ttlAttribute: options?.ttlAttribute,\n\n disablePointInTimeRecovery: options?.disablePointInTimeRecovery ?? false,\n\n encryption: options?.encryption ?? false,\n});\n"]}
1
+ {"version":3,"file":"keyValueStore.js","sourceRoot":"","sources":["../../../../src/config/settings/keyValueStore.ts"],"names":[],"mappings":";;;AACA,4CAAqG;AAmB9F,MAAM,MAAM,GAAG,CAAyB,GAAa,EAAE,OAAmB,QAAQ,EAAa,EAAE,CAAC,CAAC;IACxG,GAAG;IACH,IAAI;CACL,CAAC,CAAC;AAHU,QAAA,MAAM,UAGhB;AAWH,MAAM,8BAA8B,GAAG,CAAyB,eAAmC,EAAa,EAAE;IAChH,oBAAoB;IACpB,IAAI,OAAO,eAAe,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,IAAA,cAAM,EAAI,eAA2B,EAAE,QAAQ,CAAC,CAAC;IAC1D,CAAC;IAED,sBAAsB;IACtB,OAAO,eAA4B,CAAC;AACtC,CAAC,CAAC;AAEF,MAAM,8CAA8C,GAAG,CACrD,iBAAuC,EACa,EAAE;IACtD,OAAO,OAAO,iBAAiB,KAAK,QAAQ,CAAC;AAC/C,CAAC,CAAC;AAEF,MAAM,kCAAkC,GAAG,CAAyB,iBAAuC,EAAe,EAAE;IAC1H,IAAI,CAAC,8CAA8C,CAAI,iBAAiB,CAAC,EAAE,CAAC;QAC1E,OAAO;YACL,YAAY,EAAE,IAAA,cAAM,EAAI,iBAAiB,EAAE,QAAQ,CAAC;SACrD,CAAC;IACJ,CAAC;IAED,OAAO;QACL,YAAY,EAAE,8BAA8B,CAAI,iBAAiB,CAAC,YAAY,CAAC;QAC/E,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAI,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;KAC9G,CAAC;AACJ,CAAC,CAAC;AA+EK,MAAM,mBAAmB,GAAG,CACjC,iBAAyB,EAEzB,YAAgC,EAChC,WAAiC,EAAE,EAEnC,OAAmD,EACjB,EAAE;;IAAC,OAAA,CAAC;QACtC,iBAAiB,EAAE,oCAAwB,CAAC,aAAa;QACzD,SAAS,EAAE,iBAAiB;QAE5B,iBAAiB;QAEjB,YAAY,EAAE,8BAA8B,CAAI,YAAY,CAAC;QAC7D,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAA,8BAAiC,CAAA,CAAC;QAEzD,OAAO,EAAE,CAAC,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,mCAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA,kCAAqC,CAAA,CAAC;QAE5E,MAAM,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,mCAAI,KAAK;QAEhC,KAAK,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,KAAK;QAErB,YAAY,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,YAAY;QAEnC,0BAA0B,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,0BAA0B,mCAAI,KAAK;QAExE,UAAU,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,UAAU,mCAAI,KAAK;QAExC,QAAQ,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAQ;KAC5B,CAAC,CAAA;CAAA,CAAC;AA7BU,QAAA,mBAAmB,uBA6B7B","sourcesContent":["import { CrossModuleOwner, KeyOf, QpqFunctionRuntime } from '../../types';\nimport { QPQConfigAdvancedSettings, QPQConfigSetting, QPQCoreConfigSettingType } from '../QPQConfig';\n\n// TODO: When we put validation on the QPQConfig, we need to make sure\n// Key Length and Data Type: DynamoDB has limitations on key length and data type.\n// Your KvsKeyType only supports 'string', 'number', and 'binary' types. Make sure\n// that you're validating these in convertKvsKeyToDynamodbAttribute function and\n// also ensure that key length is within DynamoDB's specified limits.\n\nexport type KvsKeyType = 'string' | 'number' | 'binary';\nexport type KvsKey<T extends object = any> = {\n key: KeyOf<T>;\n type: KvsKeyType;\n};\n\nexport type KvsIndex<T extends object = any> = {\n partitionKey: KvsKey<T>;\n sortKey?: KvsKey<T>;\n};\n\nexport const kvsKey = <T extends object = any>(key: KeyOf<T>, type: KvsKeyType = 'string'): KvsKey<T> => ({\n key,\n type,\n});\n\ntype CompositeKvsKey<T extends object = any> = KvsKey<T> | KeyOf<T>;\n\ntype CompositeCompositeKvsIndex<T extends object = any> = {\n partitionKey: CompositeKvsKey<T>;\n sortKey?: CompositeKvsKey<T>;\n};\n\nexport type CompositeKvsIndex<T extends object = any> = KeyOf<T> | CompositeCompositeKvsIndex<T>;\n\nconst convertCompositeKvsKeyToKvsKey = <T extends object = any>(compositeKvsKey: CompositeKvsKey<T>): KvsKey<T> => {\n // Must be a keyof T\n if (typeof compositeKvsKey === 'string') {\n return kvsKey<T>(compositeKvsKey as KeyOf<T>, 'string');\n }\n\n // must be a KvsKey<T>\n return compositeKvsKey as KvsKey<T>;\n};\n\nconst isCompositeKvsIndexACompositeCompositeKvsIndex = <T extends object = any>(\n compositeKvsIndex: CompositeKvsIndex<T>,\n): compositeKvsIndex is CompositeCompositeKvsIndex<T> => {\n return typeof compositeKvsIndex !== 'string';\n};\n\nconst convertCompositeKvsIndexToKvsIndex = <T extends object = any>(compositeKvsIndex: CompositeKvsIndex<T>): KvsIndex<T> => {\n if (!isCompositeKvsIndexACompositeCompositeKvsIndex<T>(compositeKvsIndex)) {\n return {\n partitionKey: kvsKey<T>(compositeKvsIndex, 'string'),\n };\n }\n\n return {\n partitionKey: convertCompositeKvsKeyToKvsKey<T>(compositeKvsIndex.partitionKey),\n sortKey: compositeKvsIndex.sortKey ? convertCompositeKvsKeyToKvsKey<T>(compositeKvsIndex.sortKey) : undefined,\n };\n};\n\n/**\n * Change data capture on a store: the table gets a stream (NEW_AND_OLD_IMAGES) and\n * `runtime` is invoked with the changes.\n *\n * Records arrive in order PER PARTITION KEY, and different partition keys are processed\n * concurrently. A stream handler therefore gets one-writer-per-item serialisation for\n * free, which is what makes a stream the natural place to maintain a projection of a table\n * you only ever append to — no queue, no extra write on the append path, and no trigger to\n * lose, because the record exists precisely because the write happened.\n *\n * Mirrors defineStorageDrive's `onEvent`.\n */\nexport type KvsStreamSettings = {\n runtime: QpqFunctionRuntime;\n\n /**\n * Collapse each delivered batch down to ONE record per partition key, keeping the latest.\n *\n * Off by default: a generic consumer (audit trail, change notifications) needs to see\n * every change, and the framework should never silently drop records.\n *\n * On for a PROJECTION, where the payload that matters is \"this key changed\" and the\n * handler re-derives from source anyway. Without it, the batch is fanned back out into\n * one story run per record and the batching bought nothing: 500 appends to one document\n * would mean 500 re-derivations, each reading a log that grows as it goes. With it, that\n * batch is a single re-derivation.\n */\n coalesceByPartitionKey?: boolean;\n\n /** Records per invocation. DynamoDB streams allow up to 1000; the mapping default is 100. */\n batchSize?: number;\n\n /**\n * How long to wait accumulating records before invoking, 0 to 300 seconds. Trades\n * projection latency for far fewer invocations, and gives coalescing more to collapse.\n */\n maximumBatchingWindowInSeconds?: number;\n};\n\nexport interface QPQConfigAdvancedKeyValueStoreSettings<T extends object = any> extends QPQConfigAdvancedSettings {\n indexes?: CompositeKvsIndex<T>[];\n\n global?: boolean;\n\n owner?: CrossModuleOwner;\n\n ttlAttribute?: string;\n\n // Point-in-time recovery (35-day continuous backups) is on by default; set this to opt out.\n disablePointInTimeRecovery?: boolean;\n\n encryption?: boolean;\n\n // Turn on change data capture — see KvsStreamSettings. Absent means no stream is\n // created at all.\n onStream?: KvsStreamSettings;\n}\n\nexport interface KeyValueStoreQPQConfigSetting<T extends object = any> extends QPQConfigSetting {\n keyValueStoreName: string;\n\n partitionKey: KvsKey<T>;\n sortKeys: KvsKey<T>[];\n\n indexes: KvsIndex<T>[];\n\n global: boolean;\n\n ttlAttribute?: string;\n\n disablePointInTimeRecovery: boolean;\n\n encryption: boolean;\n\n onStream?: KvsStreamSettings;\n}\n\nexport const defineKeyValueStore = <T extends object = any>(\n keyValueStoreName: string,\n\n partitionKey: CompositeKvsKey<T>,\n sortKeys: CompositeKvsKey<T>[] = [],\n\n options?: QPQConfigAdvancedKeyValueStoreSettings<T>,\n): KeyValueStoreQPQConfigSetting<T> => ({\n configSettingType: QPQCoreConfigSettingType.keyValueStore,\n uniqueKey: keyValueStoreName,\n\n keyValueStoreName,\n\n partitionKey: convertCompositeKvsKeyToKvsKey<T>(partitionKey),\n sortKeys: sortKeys.map(convertCompositeKvsKeyToKvsKey<T>),\n\n indexes: (options?.indexes ?? []).map(convertCompositeKvsIndexToKvsIndex<T>),\n\n global: options?.global ?? false,\n\n owner: options?.owner,\n\n ttlAttribute: options?.ttlAttribute,\n\n disablePointInTimeRecovery: options?.disablePointInTimeRecovery ?? false,\n\n encryption: options?.encryption ?? false,\n\n onStream: options?.onStream,\n});\n"]}
@@ -3,17 +3,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.defineQueue = void 0;
4
4
  const QPQConfig_1 = require("../QPQConfig");
5
5
  const defineQueue = (name, processors, options) => {
6
- var _a;
6
+ var _a, _b;
7
7
  return {
8
8
  configSettingType: QPQConfig_1.QPQCoreConfigSettingType.queue,
9
9
  uniqueKey: name,
10
10
  name,
11
11
  batchSize: (options === null || options === void 0 ? void 0 : options.batchSize) || 0,
12
- batchWindowInSeconds: (options === null || options === void 0 ? void 0 : options.batchWindowInSeconds) || 5,
12
+ // Default 0 invoke as soon as a message arrives which is also AWS's own default for
13
+ // MaximumBatchingWindowInSeconds.
14
+ //
15
+ // It used to default to 5 with a `||`, which was wrong twice over. A window only ever reaches AWS for a
16
+ // queue that sets `batchSize > 0` (the CDK construct omits both together otherwise), and in practice
17
+ // those queues all use batchSize 1 — where a batch is FULL on the first message, so a window can never
18
+ // gather a second one and can only delay the first. And `||` meant 0 was falsy, so a caller who noticed
19
+ // and passed 0 still got 5: the setting could not be turned off at all.
20
+ //
21
+ // A queue that genuinely wants to accumulate should ask for a window explicitly, rather than every
22
+ // queue inheriting latency that most of them cannot use.
23
+ batchWindowInSeconds: (_a = options === null || options === void 0 ? void 0 : options.batchWindowInSeconds) !== null && _a !== void 0 ? _a : 0,
13
24
  concurrency: (options === null || options === void 0 ? void 0 : options.concurrency) || 1,
14
25
  maxTries: (options === null || options === void 0 ? void 0 : options.maxTries) || 1,
15
26
  ttRetryInSeconds: Math.min((options === null || options === void 0 ? void 0 : options.ttRetryInSeconds) || 15 * 60, 15 * 60),
16
- hasDeadLetterQueue: (_a = options === null || options === void 0 ? void 0 : options.hasDeadLetterQueue) !== null && _a !== void 0 ? _a : true,
27
+ hasDeadLetterQueue: (_b = options === null || options === void 0 ? void 0 : options.hasDeadLetterQueue) !== null && _b !== void 0 ? _b : true,
17
28
  qpqQueueProcessors: processors,
18
29
  eventBusSubscriptions: (options === null || options === void 0 ? void 0 : options.eventBusSubscriptions) || [],
19
30
  maxConcurrentExecutions: options === null || options === void 0 ? void 0 : options.maxConcurrentExecutions,
@@ -1 +1 @@
1
- {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../../../src/config/settings/queue.ts"],"names":[],"mappings":";;;AACA,4CAAqG;AAuC9F,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,UAA8B,EAAE,OAAwC,EAAyB,EAAE;;IAC3I,OAAO;QACL,iBAAiB,EAAE,oCAAwB,CAAC,KAAK;QACjD,SAAS,EAAE,IAAI;QAEf,IAAI;QAEJ,SAAS,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,SAAS,KAAI,CAAC;QAClC,oBAAoB,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,oBAAoB,KAAI,CAAC;QAExD,WAAW,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,WAAW,KAAI,CAAC;QAEtC,QAAQ,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAQ,KAAI,CAAC;QAChC,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,gBAAgB,KAAI,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;QAEzE,kBAAkB,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,kBAAkB,mCAAI,IAAI;QAEvD,kBAAkB,EAAE,UAAU;QAE9B,qBAAqB,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,qBAAqB,KAAI,EAAE;QAE3D,uBAAuB,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,uBAAuB;QAEzD,MAAM,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,KAAI,KAAK;KACjC,CAAC;AACJ,CAAC,CAAC;AAzBW,QAAA,WAAW,eAyBtB","sourcesContent":["import { QpqFunctionRuntime } from '../../types';\nimport { QPQConfigAdvancedSettings, QPQConfigSetting, QPQCoreConfigSettingType } from '../QPQConfig';\n\nexport interface QpqQueueProcessors {\n [type: string]: QpqFunctionRuntime;\n}\n\nexport interface QPQConfigAdvancedQueueSettings extends QPQConfigAdvancedSettings {\n batchSize?: number;\n batchWindowInSeconds?: number;\n concurrency?: number;\n maxTries?: number;\n ttRetryInSeconds?: number;\n hasDeadLetterQueue?: boolean;\n eventBusSubscriptions?: string[];\n maxConcurrentExecutions?: number;\n isFifo?: boolean;\n}\n\nexport interface QueueQPQConfigSetting extends QPQConfigSetting {\n name: string;\n\n batchSize: number;\n batchWindowInSeconds: number;\n\n concurrency: number;\n\n maxTries: number;\n ttRetryInSeconds: number;\n\n hasDeadLetterQueue: boolean;\n qpqQueueProcessors: QpqQueueProcessors;\n\n eventBusSubscriptions: string[];\n\n maxConcurrentExecutions?: number;\n\n isFifo: boolean;\n}\n\nexport const defineQueue = (name: string, processors: QpqQueueProcessors, options?: QPQConfigAdvancedQueueSettings): QueueQPQConfigSetting => {\n return {\n configSettingType: QPQCoreConfigSettingType.queue,\n uniqueKey: name,\n\n name,\n\n batchSize: options?.batchSize || 0,\n batchWindowInSeconds: options?.batchWindowInSeconds || 5,\n\n concurrency: options?.concurrency || 1,\n\n maxTries: options?.maxTries || 1,\n ttRetryInSeconds: Math.min(options?.ttRetryInSeconds || 15 * 60, 15 * 60),\n\n hasDeadLetterQueue: options?.hasDeadLetterQueue ?? true,\n\n qpqQueueProcessors: processors,\n\n eventBusSubscriptions: options?.eventBusSubscriptions || [],\n\n maxConcurrentExecutions: options?.maxConcurrentExecutions,\n\n isFifo: options?.isFifo || false,\n };\n};\n"]}
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../../../src/config/settings/queue.ts"],"names":[],"mappings":";;;AACA,4CAAqG;AAuC9F,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,UAA8B,EAAE,OAAwC,EAAyB,EAAE;;IAC3I,OAAO;QACL,iBAAiB,EAAE,oCAAwB,CAAC,KAAK;QACjD,SAAS,EAAE,IAAI;QAEf,IAAI;QAEJ,SAAS,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,SAAS,KAAI,CAAC;QAClC,wFAAwF;QACxF,kCAAkC;QAClC,EAAE;QACF,wGAAwG;QACxG,qGAAqG;QACrG,uGAAuG;QACvG,wGAAwG;QACxG,wEAAwE;QACxE,EAAE;QACF,mGAAmG;QACnG,yDAAyD;QACzD,oBAAoB,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,oBAAoB,mCAAI,CAAC;QAExD,WAAW,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,WAAW,KAAI,CAAC;QAEtC,QAAQ,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAQ,KAAI,CAAC;QAChC,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,gBAAgB,KAAI,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;QAEzE,kBAAkB,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,kBAAkB,mCAAI,IAAI;QAEvD,kBAAkB,EAAE,UAAU;QAE9B,qBAAqB,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,qBAAqB,KAAI,EAAE;QAE3D,uBAAuB,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,uBAAuB;QAEzD,MAAM,EAAE,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,KAAI,KAAK;KACjC,CAAC;AACJ,CAAC,CAAC;AApCW,QAAA,WAAW,eAoCtB","sourcesContent":["import { QpqFunctionRuntime } from '../../types';\nimport { QPQConfigAdvancedSettings, QPQConfigSetting, QPQCoreConfigSettingType } from '../QPQConfig';\n\nexport interface QpqQueueProcessors {\n [type: string]: QpqFunctionRuntime;\n}\n\nexport interface QPQConfigAdvancedQueueSettings extends QPQConfigAdvancedSettings {\n batchSize?: number;\n batchWindowInSeconds?: number;\n concurrency?: number;\n maxTries?: number;\n ttRetryInSeconds?: number;\n hasDeadLetterQueue?: boolean;\n eventBusSubscriptions?: string[];\n maxConcurrentExecutions?: number;\n isFifo?: boolean;\n}\n\nexport interface QueueQPQConfigSetting extends QPQConfigSetting {\n name: string;\n\n batchSize: number;\n batchWindowInSeconds: number;\n\n concurrency: number;\n\n maxTries: number;\n ttRetryInSeconds: number;\n\n hasDeadLetterQueue: boolean;\n qpqQueueProcessors: QpqQueueProcessors;\n\n eventBusSubscriptions: string[];\n\n maxConcurrentExecutions?: number;\n\n isFifo: boolean;\n}\n\nexport const defineQueue = (name: string, processors: QpqQueueProcessors, options?: QPQConfigAdvancedQueueSettings): QueueQPQConfigSetting => {\n return {\n configSettingType: QPQCoreConfigSettingType.queue,\n uniqueKey: name,\n\n name,\n\n batchSize: options?.batchSize || 0,\n // Default 0 — invoke as soon as a message arrives — which is also AWS's own default for\n // MaximumBatchingWindowInSeconds.\n //\n // It used to default to 5 with a `||`, which was wrong twice over. A window only ever reaches AWS for a\n // queue that sets `batchSize > 0` (the CDK construct omits both together otherwise), and in practice\n // those queues all use batchSize 1 — where a batch is FULL on the first message, so a window can never\n // gather a second one and can only delay the first. And `||` meant 0 was falsy, so a caller who noticed\n // and passed 0 still got 5: the setting could not be turned off at all.\n //\n // A queue that genuinely wants to accumulate should ask for a window explicitly, rather than every\n // queue inheriting latency that most of them cannot use.\n batchWindowInSeconds: options?.batchWindowInSeconds ?? 0,\n\n concurrency: options?.concurrency || 1,\n\n maxTries: options?.maxTries || 1,\n ttRetryInSeconds: Math.min(options?.ttRetryInSeconds || 15 * 60, 15 * 60),\n\n hasDeadLetterQueue: options?.hasDeadLetterQueue ?? true,\n\n qpqQueueProcessors: processors,\n\n eventBusSubscriptions: options?.eventBusSubscriptions || [],\n\n maxConcurrentExecutions: options?.maxConcurrentExecutions,\n\n isFifo: options?.isFifo || false,\n };\n};\n"]}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.actionResultErrorFromCaughtError = exports.resolveActionResultError = exports.resolveActionResult = exports.isErroredActionResult = exports.actionResult = exports.actionResultError = exports.getUnsuccessfulEitherActionResult = exports.getSuccessfulEitherActionResult = void 0;
4
4
  const ErrorTypeEnum_1 = require("../types/ErrorTypeEnum");
5
+ const transientErrorNames_1 = require("./transientErrorNames");
5
6
  const getSuccessfulEitherActionResult = (result) => ({
6
7
  success: true,
7
8
  result,
@@ -51,6 +52,15 @@ const actionResultErrorFromCaughtError = (error, errorMap) => {
51
52
  return handler(error);
52
53
  }
53
54
  const unmappedKey = errorCode !== null && errorCode !== void 0 ? errorCode : errorName;
55
+ // TODO: This is really shit... this does not belong here.
56
+ //
57
+ // Classify throttles and transient service failures BEFORE the generic fallback, so a caller can tell
58
+ // "the service is busy" from "this request is wrong" without matching on error text. Without this they
59
+ // are indistinguishable GenericErrors, and a caller that records failures durably writes a permanent
60
+ // failure for something that would have succeeded on the next attempt.
61
+ if ((0, transientErrorNames_1.isTransientErrorName)(errorCode) || (0, transientErrorNames_1.isTransientErrorName)(errorName)) {
62
+ return (0, exports.actionResultError)(ErrorTypeEnum_1.ErrorTypeEnum.OutOfResources, `A transient error occurred, retry [${unmappedKey}].`);
63
+ }
54
64
  console.log(`Error: ${unmappedKey}`);
55
65
  return (0, exports.actionResultError)(ErrorTypeEnum_1.ErrorTypeEnum.GenericError, `An unexpected error occurred [${unmappedKey}].`);
56
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"actionLogic.js","sourceRoot":"","sources":["../../../src/logic/actionLogic.ts"],"names":[],"mappings":";;;AACA,0DAAuD;AAGhD,MAAM,+BAA+B,GAAG,CAAI,MAAS,EAAyB,EAAE,CAAC,CAAC;IACvF,OAAO,EAAE,IAAI;IACb,MAAM;CACP,CAAC,CAAC;AAHU,QAAA,+BAA+B,mCAGzC;AAEI,MAAM,iCAAiC,GAAG,CAAC,KAAe,EAA2B,EAAE,CAAC,CAAC;IAC9F,OAAO,EAAE,KAAK;IACd,KAAK,EAAE,KAAK;CACb,CAAC,CAAC;AAHU,QAAA,iCAAiC,qCAG3C;AAEI,MAAM,iBAAiB,GAAG,CAAC,SAAiC,EAAE,SAAiB,EAAE,UAAmB,EAA8B,EAAE;IACzI,OAAO,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC;AAC3D,CAAC,CAAC;AAFW,QAAA,iBAAiB,qBAE5B;AAEK,MAAM,YAAY,GAAG,CAAI,MAAS,EAA4B,EAAE;IACrE,OAAO,CAAC,MAAM,CAAC,CAAC;AAClB,CAAC,CAAC;AAFW,QAAA,YAAY,gBAEvB;AAEK,MAAM,qBAAqB,GAAG,CAAI,YAAsC,EAAW,EAAE;IAC1F,OAAO,CAAC,YAAY,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AAC5C,CAAC,CAAC;AAFW,QAAA,qBAAqB,yBAEhC;AAEK,MAAM,mBAAmB,GAAG,CAAI,YAAsC,EAAE,EAAE;IAC/E,yEAAyE;IACzE,qCAAqC;IACrC,OAAO,YAAY,CAAC,CAAC,CAAE,CAAC;AAC1B,CAAC,CAAC;AAJW,QAAA,mBAAmB,uBAI9B;AAEK,MAAM,wBAAwB,GAAG,CAAI,YAAsC,EAAY,EAAE;IAC9F,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO;YACL,SAAS,EAAE,wCAAwC,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,GAAG;YAClF,SAAS,EAAE,6BAAa,CAAC,YAAY;SACtC,CAAC;IACJ,CAAC;IAED,OAAO,YAAY,CAAC,CAAC,CAAa,CAAC;AACrC,CAAC,CAAC;AATW,QAAA,wBAAwB,4BASnC;AAIK,MAAM,gCAAgC,GAAG,CAAC,KAAc,EAAE,QAAkB,EAAwC,EAAE;IAC3H,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAI,KAAa,CAAC,IAA0B,CAAC;QAC5D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC;QAE7B,0EAA0E;QAC1E,8DAA8D;QAC9D,MAAM,OAAO,GAAG,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC1E,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;QAED,MAAM,WAAW,GAAG,SAAS,aAAT,SAAS,cAAT,SAAS,GAAI,SAAS,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,UAAU,WAAW,EAAE,CAAC,CAAC;QACrC,OAAO,IAAA,yBAAiB,EAAC,6BAAa,CAAC,YAAY,EAAE,iCAAiC,WAAW,IAAI,CAAC,CAAC;IACzG,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;IAExC,OAAO,IAAA,yBAAiB,EAAC,6BAAa,CAAC,YAAY,EAAE,4BAA4B,CAAC,CAAC;AACrF,CAAC,CAAC;AApBW,QAAA,gCAAgC,oCAoB3C","sourcesContent":["import { ActionProcessorResult, EitherActionResult } from '../types/Action';\nimport { ErrorTypeEnum } from '../types/ErrorTypeEnum';\nimport { QPQError } from '../types/ErrorTypeEnum';\n\nexport const getSuccessfulEitherActionResult = <T>(result: T): EitherActionResult<T> => ({\n success: true,\n result,\n});\n\nexport const getUnsuccessfulEitherActionResult = (error: QPQError): EitherActionResult<any> => ({\n success: false,\n error: error,\n});\n\nexport const actionResultError = (errorType: ErrorTypeEnum | string, errorText: string, errorStack?: string): ActionProcessorResult<any> => {\n return [undefined, { errorType, errorText, errorStack }];\n};\n\nexport const actionResult = <T>(result: T): ActionProcessorResult<T> => {\n return [result];\n};\n\nexport const isErroredActionResult = <T>(actionResult: ActionProcessorResult<T>): boolean => {\n return !actionResult || !!actionResult[1];\n};\n\nexport const resolveActionResult = <T>(actionResult: ActionProcessorResult<T>) => {\n // We say that it has to have a value, otherwise don't call this function\n // use isErroredActionResult to check\n return actionResult[0]!;\n};\n\nexport const resolveActionResultError = <T>(actionResult: ActionProcessorResult<T>): QPQError => {\n if (!actionResult) {\n return {\n errorText: `Action processor returned no result [${JSON.stringify(actionResult)}]`,\n errorType: ErrorTypeEnum.GenericError,\n };\n }\n\n return actionResult[1] as QPQError;\n};\n\ntype ErrorMap = { [key: string]: (error: Error) => ActionProcessorResult<any> };\n\nexport const actionResultErrorFromCaughtError = (error: unknown, errorMap: ErrorMap): ReturnType<typeof actionResultError> => {\n if (error instanceof Error) {\n const errorCode = (error as any).code as string | undefined;\n const errorName = error.name;\n\n // Prefer the OS/runtime code (node fs: EACCES, ENOENT...), then fall back\n // to the error name (AWS SDK: AccessDenied, NoSuchBucket...).\n const handler = (errorCode && errorMap[errorCode]) || errorMap[errorName];\n if (handler) {\n return handler(error);\n }\n\n const unmappedKey = errorCode ?? errorName;\n console.log(`Error: ${unmappedKey}`);\n return actionResultError(ErrorTypeEnum.GenericError, `An unexpected error occurred [${unmappedKey}].`);\n }\n\n console.log('Caught non-error:', error);\n\n return actionResultError(ErrorTypeEnum.GenericError, 'An unknown error occurred.');\n};\n"]}
1
+ {"version":3,"file":"actionLogic.js","sourceRoot":"","sources":["../../../src/logic/actionLogic.ts"],"names":[],"mappings":"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","sourcesContent":["import { ActionProcessorResult, EitherActionResult } from '../types/Action';\nimport { ErrorTypeEnum } from '../types/ErrorTypeEnum';\nimport { QPQError } from '../types/ErrorTypeEnum';\nimport { isTransientErrorName } from './transientErrorNames';\n\nexport const getSuccessfulEitherActionResult = <T>(result: T): EitherActionResult<T> => ({\n success: true,\n result,\n});\n\nexport const getUnsuccessfulEitherActionResult = (error: QPQError): EitherActionResult<any> => ({\n success: false,\n error: error,\n});\n\nexport const actionResultError = (errorType: ErrorTypeEnum | string, errorText: string, errorStack?: string): ActionProcessorResult<any> => {\n return [undefined, { errorType, errorText, errorStack }];\n};\n\nexport const actionResult = <T>(result: T): ActionProcessorResult<T> => {\n return [result];\n};\n\nexport const isErroredActionResult = <T>(actionResult: ActionProcessorResult<T>): boolean => {\n return !actionResult || !!actionResult[1];\n};\n\nexport const resolveActionResult = <T>(actionResult: ActionProcessorResult<T>) => {\n // We say that it has to have a value, otherwise don't call this function\n // use isErroredActionResult to check\n return actionResult[0]!;\n};\n\nexport const resolveActionResultError = <T>(actionResult: ActionProcessorResult<T>): QPQError => {\n if (!actionResult) {\n return {\n errorText: `Action processor returned no result [${JSON.stringify(actionResult)}]`,\n errorType: ErrorTypeEnum.GenericError,\n };\n }\n\n return actionResult[1] as QPQError;\n};\n\ntype ErrorMap = { [key: string]: (error: Error) => ActionProcessorResult<any> };\n\nexport const actionResultErrorFromCaughtError = (error: unknown, errorMap: ErrorMap): ReturnType<typeof actionResultError> => {\n if (error instanceof Error) {\n const errorCode = (error as any).code as string | undefined;\n const errorName = error.name;\n\n // Prefer the OS/runtime code (node fs: EACCES, ENOENT...), then fall back\n // to the error name (AWS SDK: AccessDenied, NoSuchBucket...).\n const handler = (errorCode && errorMap[errorCode]) || errorMap[errorName];\n if (handler) {\n return handler(error);\n }\n\n const unmappedKey = errorCode ?? errorName;\n\n // TODO: This is really shit... this does not belong here.\n //\n // Classify throttles and transient service failures BEFORE the generic fallback, so a caller can tell\n // \"the service is busy\" from \"this request is wrong\" without matching on error text. Without this they\n // are indistinguishable GenericErrors, and a caller that records failures durably writes a permanent\n // failure for something that would have succeeded on the next attempt.\n if (isTransientErrorName(errorCode) || isTransientErrorName(errorName)) {\n return actionResultError(ErrorTypeEnum.OutOfResources, `A transient error occurred, retry [${unmappedKey}].`);\n }\n\n console.log(`Error: ${unmappedKey}`);\n return actionResultError(ErrorTypeEnum.GenericError, `An unexpected error occurred [${unmappedKey}].`);\n }\n\n console.log('Caught non-error:', error);\n\n return actionResultError(ErrorTypeEnum.GenericError, 'An unknown error occurred.');\n};\n"]}
@@ -7,3 +7,4 @@ export * from './lookup';
7
7
  export * from './scope';
8
8
  export * from './stateEffects';
9
9
  export * from './stream';
10
+ export * from './transientErrorNames';
@@ -23,4 +23,5 @@ __exportStar(require("./lookup"), exports);
23
23
  __exportStar(require("./scope"), exports);
24
24
  __exportStar(require("./stateEffects"), exports);
25
25
  __exportStar(require("./stream"), exports);
26
+ __exportStar(require("./transientErrorNames"), exports);
26
27
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/logic/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,qDAAmC;AACnC,4CAA0B;AAC1B,qDAAmC;AACnC,0CAAwB;AACxB,+CAA6B;AAC7B,2CAAyB;AACzB,0CAAwB;AACxB,iDAA+B;AAC/B,2CAAyB","sourcesContent":["export * from './actionValidators';\nexport * from './context';\nexport * from './decomposedString';\nexport * from './error';\nexport * from './logHistory';\nexport * from './lookup';\nexport * from './scope';\nexport * from './stateEffects';\nexport * from './stream';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/logic/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,qDAAmC;AACnC,4CAA0B;AAC1B,qDAAmC;AACnC,0CAAwB;AACxB,+CAA6B;AAC7B,2CAAyB;AACzB,0CAAwB;AACxB,iDAA+B;AAC/B,2CAAyB;AACzB,wDAAsC","sourcesContent":["export * from './actionValidators';\nexport * from './context';\nexport * from './decomposedString';\nexport * from './error';\nexport * from './logHistory';\nexport * from './lookup';\nexport * from './scope';\nexport * from './stateEffects';\nexport * from './stream';\nexport * from './transientErrorNames';\n"]}
@@ -1,6 +1,10 @@
1
1
  import { KvsCoreDataType, KvsQueryCondition } from '../../actions/keyValueStore/types';
2
2
  export declare const KVS_SCOPE_DELIMITER = "@@QPQSCOPE@@";
3
3
  export declare function composeScopedKvsValue(scope: string | undefined, rawValue: KvsCoreDataType): KvsCoreDataType;
4
+ export declare const decomposeScopedKvsValue: (composedValue: string) => {
5
+ scope?: string;
6
+ rawValue: string;
7
+ };
4
8
  export declare function validateRawPkValueForScopeOrThrow(rawValue: KvsCoreDataType): void;
5
9
  export declare function stripScopedKvsValue(scope: string | undefined, storedValue: KvsCoreDataType): KvsCoreDataType;
6
10
  export declare function validateScopeSupportedForPartitionKeyType(partitionKeyType: string): void;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.KVS_SCOPE_DELIMITER = void 0;
3
+ exports.decomposeScopedKvsValue = exports.KVS_SCOPE_DELIMITER = void 0;
4
4
  exports.composeScopedKvsValue = composeScopedKvsValue;
5
5
  exports.validateRawPkValueForScopeOrThrow = validateRawPkValueForScopeOrThrow;
6
6
  exports.stripScopedKvsValue = stripScopedKvsValue;
@@ -31,6 +31,24 @@ function composeScopedKvsValue(scope, rawValue) {
31
31
  validateRawPkValueForScopeOrThrow(rawValue);
32
32
  return `${scope}${exports.KVS_SCOPE_DELIMITER}${rawValue}`;
33
33
  }
34
+ // The inverse of composeScopedKvsValue: split a stored partition key value back into the
35
+ // scope it was written under and the caller's raw key.
36
+ //
37
+ // Needed by anything reading a RAW stored key rather than going through a scoped request.
38
+ // Change data capture is the case that forced it: a stream record carries the composed pk,
39
+ // and the consumer has to re-enter the same scope before it can act on the item. An
40
+ // unscoped value has no delimiter and comes back with `scope: undefined`.
41
+ const decomposeScopedKvsValue = (composedValue) => {
42
+ const at = composedValue.indexOf(exports.KVS_SCOPE_DELIMITER);
43
+ if (at < 0) {
44
+ return { rawValue: composedValue };
45
+ }
46
+ return {
47
+ scope: composedValue.slice(0, at),
48
+ rawValue: composedValue.slice(at + exports.KVS_SCOPE_DELIMITER.length),
49
+ };
50
+ };
51
+ exports.decomposeScopedKvsValue = decomposeScopedKvsValue;
34
52
  // The delimiter is reserved in scoped partition-key values: a raw value
35
53
  // carrying it would strip ambiguously and could collide with (or forge)
36
54
  // another scope's composed rows. File-partitioned backends call this directly
@@ -1 +1 @@
1
- {"version":3,"file":"scopedKvsValue.js","sourceRoot":"","sources":["../../../../src/logic/scope/scopedKvsValue.ts"],"names":[],"mappings":";;;AAeA,sDAcC;AAOD,8EAOC;AAKD,kDAOC;AAKD,8FAOC;AAMD,0EAMC;AAID,4EAQC;AA3FD,6DAA8G;AAC9G,2DAA+E;AAC/E,iEAA8D;AAE9D,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,0EAA0E;AAC1E,6EAA6E;AAC7E,SAAS;AACI,QAAA,mBAAmB,GAAG,cAAc,CAAC;AAElD,gGAAgG;AAChG,8EAA8E;AAC9E,4EAA4E;AAC5E,SAAgB,qBAAqB,CAAC,KAAyB,EAAE,QAAyB;IACxF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAA,2CAAoB,EAAC,KAAK,CAAC,CAAC;IAE5B,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,qCAAiB,CAAC,yCAAqB,CAAC,gBAAgB,EAAE,yDAAyD,CAAC,CAAC;IACjI,CAAC;IAED,iCAAiC,CAAC,QAAQ,CAAC,CAAC;IAE5C,OAAO,GAAG,KAAK,GAAG,2BAAmB,GAAG,QAAQ,EAAE,CAAC;AACrD,CAAC;AAED,wEAAwE;AACxE,wEAAwE;AACxE,8EAA8E;AAC9E,yEAAyE;AACzE,eAAe;AACf,SAAgB,iCAAiC,CAAC,QAAyB;IACzE,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,2BAAmB,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,qCAAiB,CACzB,yCAAqB,CAAC,iBAAiB,EACvC,8DAA8D,2BAAmB,IAAI,CACtF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,8EAA8E;AAC9E,0DAA0D;AAC1D,SAAgB,mBAAmB,CAAC,KAAyB,EAAE,WAA4B;IACzF,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QAC3D,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,MAAM,MAAM,GAAG,GAAG,KAAK,GAAG,2BAAmB,EAAE,CAAC;IAChD,OAAO,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;AACzF,CAAC;AAED,gFAAgF;AAChF,+EAA+E;AAC/E,gEAAgE;AAChE,SAAgB,yCAAyC,CAAC,gBAAwB;IAChF,IAAI,gBAAgB,KAAK,QAAQ,EAAE,CAAC;QAClC,MAAM,IAAI,qCAAiB,CACzB,yCAAqB,CAAC,gBAAgB,EACtC,uEAAuE,gBAAgB,KAAK,CAC7F,CAAC;IACJ,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,gFAAgF;AAChF,6EAA6E;AAC7E,+BAA+B;AAC/B,SAAgB,+BAA+B,CAAC,eAAuB;IACrE,OAAO;QACL,GAAG,EAAE,eAAe;QACpB,SAAS,EAAE,6BAAqB,CAAC,WAAW;QAC5C,MAAM,EAAE,2BAAmB;KAC5B,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,4DAA4D;AAC5D,SAAgB,gCAAgC,CAAC,eAAuB,EAAE,KAAa;IACrF,IAAA,2CAAoB,EAAC,KAAK,CAAC,CAAC;IAE5B,OAAO;QACL,GAAG,EAAE,eAAe;QACpB,SAAS,EAAE,6BAAqB,CAAC,UAAU;QAC3C,MAAM,EAAE,GAAG,KAAK,GAAG,2BAAmB,EAAE;KACzC,CAAC;AACJ,CAAC","sourcesContent":["import { KvsCoreDataType, KvsQueryCondition, KvsQueryOperationType } from '../../actions/keyValueStore/types';\nimport { InvalidScopeError, InvalidScopeErrorCode } from './InvalidScopeError';\nimport { validateScopeSegment } from './validateScopeSegment';\n\n// The delimiter must never occur in legitimate partition key data, because raw\n// values carrying it are rejected outright (see below). '::' is unusable: it is\n// qpq's own function-runtime separator ('src/path::method') and appears inside\n// correlation ids, which the log service stores as partition keys. So the\n// delimiter is a loud qpq-namespaced sentinel instead of a short punctuation\n// token.\nexport const KVS_SCOPE_DELIMITER = '@@QPQSCOPE@@';\n\n// Compose a scope into a partition key value: `${scope}${KVS_SCOPE_DELIMITER}${rawValue}`. Only\n// string keys can carry a scope - a number/binary pk has nowhere to embed the\n// prefix, so scoping such a store is rejected rather than silently coerced.\nexport function composeScopedKvsValue(scope: string | undefined, rawValue: KvsCoreDataType): KvsCoreDataType {\n if (scope === undefined) {\n return rawValue;\n }\n\n validateScopeSegment(scope);\n\n if (typeof rawValue !== 'string') {\n throw new InvalidScopeError(InvalidScopeErrorCode.unsafeCharacters, 'Scope is only supported on string partition key values.');\n }\n\n validateRawPkValueForScopeOrThrow(rawValue);\n\n return `${scope}${KVS_SCOPE_DELIMITER}${rawValue}`;\n}\n\n// The delimiter is reserved in scoped partition-key values: a raw value\n// carrying it would strip ambiguously and could collide with (or forge)\n// another scope's composed rows. File-partitioned backends call this directly\n// for parity - they store values raw, but a value prod rejects must fail\n// locally too.\nexport function validateRawPkValueForScopeOrThrow(rawValue: KvsCoreDataType): void {\n if (typeof rawValue === 'string' && rawValue.includes(KVS_SCOPE_DELIMITER)) {\n throw new InvalidScopeError(\n InvalidScopeErrorCode.reservedDelimiter,\n `Partition key values must not contain the scope delimiter '${KVS_SCOPE_DELIMITER}'.`,\n );\n }\n}\n\n// Undo composeScopedKvsValue on a value read back from storage, so callers\n// never see the composed form. A stored value that doesn't carry the expected\n// prefix is returned unchanged (it was written unscoped).\nexport function stripScopedKvsValue(scope: string | undefined, storedValue: KvsCoreDataType): KvsCoreDataType {\n if (scope === undefined || typeof storedValue !== 'string') {\n return storedValue;\n }\n\n const prefix = `${scope}${KVS_SCOPE_DELIMITER}`;\n return storedValue.startsWith(prefix) ? storedValue.slice(prefix.length) : storedValue;\n}\n\n// Scope composes into the partition key's stored string value, so a store whose\n// partition key is number/binary-typed cannot be scoped at all - reject before\n// any read/write happens rather than silently matching nothing.\nexport function validateScopeSupportedForPartitionKeyType(partitionKeyType: string): void {\n if (partitionKeyType !== 'string') {\n throw new InvalidScopeError(\n InvalidScopeErrorCode.unsafeCharacters,\n `Scope is only supported on stores with a string partition key (got '${partitionKeyType}').`,\n );\n }\n}\n\n// The inverse guard for UNSCOPED Scan / GetAll on a value-composed backend:\n// exclude rows whose pk carries the scope delimiter, so unscoped listings never\n// leak other scopes' (composed) rows. File-partitioned backends get this for\n// free from the file boundary.\nexport function buildKvsScopeExclusionCondition(pkAttributeName: string): KvsQueryCondition {\n return {\n key: pkAttributeName,\n operation: KvsQueryOperationType.NotContains,\n valueA: KVS_SCOPE_DELIMITER,\n };\n}\n\n// A begins-with predicate on the partition key attribute, for enforcing scope\n// on operations that have no key condition (Scan / GetAll).\nexport function buildKvsScopeBeginsWithCondition(pkAttributeName: string, scope: string): KvsQueryCondition {\n validateScopeSegment(scope);\n\n return {\n key: pkAttributeName,\n operation: KvsQueryOperationType.BeginsWith,\n valueA: `${scope}${KVS_SCOPE_DELIMITER}`,\n };\n}\n"]}
1
+ {"version":3,"file":"scopedKvsValue.js","sourceRoot":"","sources":["../../../../src/logic/scope/scopedKvsValue.ts"],"names":[],"mappings":"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","sourcesContent":["import { KvsCoreDataType, KvsQueryCondition, KvsQueryOperationType } from '../../actions/keyValueStore/types';\nimport { InvalidScopeError, InvalidScopeErrorCode } from './InvalidScopeError';\nimport { validateScopeSegment } from './validateScopeSegment';\n\n// The delimiter must never occur in legitimate partition key data, because raw\n// values carrying it are rejected outright (see below). '::' is unusable: it is\n// qpq's own function-runtime separator ('src/path::method') and appears inside\n// correlation ids, which the log service stores as partition keys. So the\n// delimiter is a loud qpq-namespaced sentinel instead of a short punctuation\n// token.\nexport const KVS_SCOPE_DELIMITER = '@@QPQSCOPE@@';\n\n// Compose a scope into a partition key value: `${scope}${KVS_SCOPE_DELIMITER}${rawValue}`. Only\n// string keys can carry a scope - a number/binary pk has nowhere to embed the\n// prefix, so scoping such a store is rejected rather than silently coerced.\nexport function composeScopedKvsValue(scope: string | undefined, rawValue: KvsCoreDataType): KvsCoreDataType {\n if (scope === undefined) {\n return rawValue;\n }\n\n validateScopeSegment(scope);\n\n if (typeof rawValue !== 'string') {\n throw new InvalidScopeError(InvalidScopeErrorCode.unsafeCharacters, 'Scope is only supported on string partition key values.');\n }\n\n validateRawPkValueForScopeOrThrow(rawValue);\n\n return `${scope}${KVS_SCOPE_DELIMITER}${rawValue}`;\n}\n\n// The inverse of composeScopedKvsValue: split a stored partition key value back into the\n// scope it was written under and the caller's raw key.\n//\n// Needed by anything reading a RAW stored key rather than going through a scoped request.\n// Change data capture is the case that forced it: a stream record carries the composed pk,\n// and the consumer has to re-enter the same scope before it can act on the item. An\n// unscoped value has no delimiter and comes back with `scope: undefined`.\nexport const decomposeScopedKvsValue = (composedValue: string): { scope?: string; rawValue: string } => {\n const at = composedValue.indexOf(KVS_SCOPE_DELIMITER);\n\n if (at < 0) {\n return { rawValue: composedValue };\n }\n\n return {\n scope: composedValue.slice(0, at),\n rawValue: composedValue.slice(at + KVS_SCOPE_DELIMITER.length),\n };\n};\n\n// The delimiter is reserved in scoped partition-key values: a raw value\n// carrying it would strip ambiguously and could collide with (or forge)\n// another scope's composed rows. File-partitioned backends call this directly\n// for parity - they store values raw, but a value prod rejects must fail\n// locally too.\nexport function validateRawPkValueForScopeOrThrow(rawValue: KvsCoreDataType): void {\n if (typeof rawValue === 'string' && rawValue.includes(KVS_SCOPE_DELIMITER)) {\n throw new InvalidScopeError(\n InvalidScopeErrorCode.reservedDelimiter,\n `Partition key values must not contain the scope delimiter '${KVS_SCOPE_DELIMITER}'.`,\n );\n }\n}\n\n// Undo composeScopedKvsValue on a value read back from storage, so callers\n// never see the composed form. A stored value that doesn't carry the expected\n// prefix is returned unchanged (it was written unscoped).\nexport function stripScopedKvsValue(scope: string | undefined, storedValue: KvsCoreDataType): KvsCoreDataType {\n if (scope === undefined || typeof storedValue !== 'string') {\n return storedValue;\n }\n\n const prefix = `${scope}${KVS_SCOPE_DELIMITER}`;\n return storedValue.startsWith(prefix) ? storedValue.slice(prefix.length) : storedValue;\n}\n\n// Scope composes into the partition key's stored string value, so a store whose\n// partition key is number/binary-typed cannot be scoped at all - reject before\n// any read/write happens rather than silently matching nothing.\nexport function validateScopeSupportedForPartitionKeyType(partitionKeyType: string): void {\n if (partitionKeyType !== 'string') {\n throw new InvalidScopeError(\n InvalidScopeErrorCode.unsafeCharacters,\n `Scope is only supported on stores with a string partition key (got '${partitionKeyType}').`,\n );\n }\n}\n\n// The inverse guard for UNSCOPED Scan / GetAll on a value-composed backend:\n// exclude rows whose pk carries the scope delimiter, so unscoped listings never\n// leak other scopes' (composed) rows. File-partitioned backends get this for\n// free from the file boundary.\nexport function buildKvsScopeExclusionCondition(pkAttributeName: string): KvsQueryCondition {\n return {\n key: pkAttributeName,\n operation: KvsQueryOperationType.NotContains,\n valueA: KVS_SCOPE_DELIMITER,\n };\n}\n\n// A begins-with predicate on the partition key attribute, for enforcing scope\n// on operations that have no key condition (Scan / GetAll).\nexport function buildKvsScopeBeginsWithCondition(pkAttributeName: string, scope: string): KvsQueryCondition {\n validateScopeSegment(scope);\n\n return {\n key: pkAttributeName,\n operation: KvsQueryOperationType.BeginsWith,\n valueA: `${scope}${KVS_SCOPE_DELIMITER}`,\n };\n}\n"]}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * AWS / runtime error names that mean "try again shortly", not "this will never work".
3
+ *
4
+ * These are RATE and CAPACITY signals, not faults in the request. A throttled DynamoDB write or a
5
+ * throttled Lambda invoke is a statement about the last few seconds of traffic, and the identical call a
6
+ * moment later usually succeeds.
7
+ *
8
+ * They matter because the default classification is the opposite. An unmapped error name falls through to
9
+ * GenericError, which callers reasonably treat as terminal — so a caller with retry machinery available
10
+ * will not use it, and a caller that records failures durably will record a permanent failure for
11
+ * something that was only ever momentary. That was observed in DocGen: a 300-record fanned-out batch put
12
+ * ~100 concurrent flows against DynamoDB, 64 records came back `ThrottlingException`, and every one was
13
+ * written down as a failed run rather than retried.
14
+ *
15
+ * Mapping them to OutOfResources gives callers a single typed thing to branch on, instead of matching
16
+ * substrings against error text.
17
+ */
18
+ export declare const TRANSIENT_ERROR_NAMES: ReadonlySet<string>;
19
+ /** Whether an error name or OS code names a transient, worth-retrying condition. */
20
+ export declare const isTransientErrorName: (name?: string) => boolean;