quidproquo-core 0.1.12 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/actions/keyValueStore/KeyValueStoreActionType.d.ts +2 -1
- package/lib/commonjs/actions/keyValueStore/KeyValueStoreActionType.js +1 -0
- package/lib/commonjs/actions/keyValueStore/KeyValueStoreActionType.js.map +1 -1
- package/lib/commonjs/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.d.ts +32 -0
- package/lib/commonjs/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.js +47 -0
- package/lib/commonjs/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.js.map +1 -0
- package/lib/commonjs/actions/keyValueStore/KeyValueStoreScanAllScopesActionTypes.d.ts +23 -0
- package/lib/commonjs/actions/keyValueStore/KeyValueStoreScanAllScopesActionTypes.js +3 -0
- package/lib/commonjs/actions/keyValueStore/KeyValueStoreScanAllScopesActionTypes.js.map +1 -0
- package/lib/commonjs/actions/keyValueStore/index.d.ts +2 -0
- package/lib/commonjs/actions/keyValueStore/index.js +2 -0
- package/lib/commonjs/actions/keyValueStore/index.js.map +1 -1
- package/lib/commonjs/actions/keyValueStore/types/KvsQueryOperation.d.ts +2 -2
- package/lib/commonjs/actions/keyValueStore/types/KvsQueryOperation.js.map +1 -1
- package/lib/commonjs/config/settings/keyValueStore.d.ts +38 -1
- package/lib/commonjs/config/settings/keyValueStore.js +1 -0
- package/lib/commonjs/config/settings/keyValueStore.js.map +1 -1
- package/lib/commonjs/logic/scope/scopedKvsValue.d.ts +4 -0
- package/lib/commonjs/logic/scope/scopedKvsValue.js +19 -1
- package/lib/commonjs/logic/scope/scopedKvsValue.js.map +1 -1
- package/lib/commonjs/types/KvsStreamEvent.d.ts +14 -0
- package/lib/commonjs/types/KvsStreamEvent.js +16 -0
- package/lib/commonjs/types/KvsStreamEvent.js.map +1 -0
- package/lib/commonjs/types/StorySession.d.ts +1 -0
- package/lib/commonjs/types/StorySession.js +1 -0
- package/lib/commonjs/types/StorySession.js.map +1 -1
- package/lib/commonjs/types/index.d.ts +1 -0
- package/lib/commonjs/types/index.js +1 -0
- package/lib/commonjs/types/index.js.map +1 -1
- package/lib/esm/actions/keyValueStore/KeyValueStoreActionType.d.ts +2 -1
- package/lib/esm/actions/keyValueStore/KeyValueStoreActionType.js +1 -0
- package/lib/esm/actions/keyValueStore/KeyValueStoreActionType.js.map +1 -1
- package/lib/esm/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.d.ts +32 -0
- package/lib/esm/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.js +43 -0
- package/lib/esm/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.js.map +1 -0
- package/lib/esm/actions/keyValueStore/KeyValueStoreScanAllScopesActionTypes.d.ts +23 -0
- package/lib/esm/actions/keyValueStore/KeyValueStoreScanAllScopesActionTypes.js +2 -0
- package/lib/esm/actions/keyValueStore/KeyValueStoreScanAllScopesActionTypes.js.map +1 -0
- package/lib/esm/actions/keyValueStore/index.d.ts +2 -0
- package/lib/esm/actions/keyValueStore/index.js +2 -0
- package/lib/esm/actions/keyValueStore/index.js.map +1 -1
- package/lib/esm/actions/keyValueStore/types/KvsQueryOperation.d.ts +2 -2
- package/lib/esm/actions/keyValueStore/types/KvsQueryOperation.js.map +1 -1
- package/lib/esm/config/settings/keyValueStore.d.ts +38 -1
- package/lib/esm/config/settings/keyValueStore.js +1 -0
- package/lib/esm/config/settings/keyValueStore.js.map +1 -1
- package/lib/esm/logic/scope/scopedKvsValue.d.ts +4 -0
- package/lib/esm/logic/scope/scopedKvsValue.js +17 -0
- package/lib/esm/logic/scope/scopedKvsValue.js.map +1 -1
- package/lib/esm/types/KvsStreamEvent.d.ts +14 -0
- package/lib/esm/types/KvsStreamEvent.js +13 -0
- package/lib/esm/types/KvsStreamEvent.js.map +1 -0
- package/lib/esm/types/StorySession.d.ts +1 -0
- package/lib/esm/types/StorySession.js +1 -0
- package/lib/esm/types/StorySession.js.map +1 -1
- package/lib/esm/types/index.d.ts +1 -0
- package/lib/esm/types/index.js +1 -0
- package/lib/esm/types/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -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,
|
|
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"]}
|
|
@@ -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 @@
|
|
|
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 =
|
|
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 =
|
|
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;
|
|
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"]}
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"scopedKvsValue.js","sourceRoot":"","sources":["../../../../src/logic/scope/scopedKvsValue.ts"],"names":[],"mappings":";;;AAeA,sDAcC;AA2BD,8EAOC;AAKD,kDAOC;AAKD,8FAOC;AAMD,0EAMC;AAID,4EAQC;AA/GD,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,yFAAyF;AACzF,uDAAuD;AACvD,EAAE;AACF,0FAA0F;AAC1F,2FAA2F;AAC3F,oFAAoF;AACpF,0EAA0E;AACnE,MAAM,uBAAuB,GAAG,CAAC,aAAqB,EAAwC,EAAE;IACrG,MAAM,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,2BAAmB,CAAC,CAAC;IAEtD,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;IACrC,CAAC;IAED,OAAO;QACL,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QACjC,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,GAAG,2BAAmB,CAAC,MAAM,CAAC;KAC/D,CAAC;AACJ,CAAC,CAAC;AAXW,QAAA,uBAAuB,2BAWlC;AAEF,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 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,14 @@
|
|
|
1
|
+
export declare enum KvsStreamEventType {
|
|
2
|
+
Insert = "Insert",
|
|
3
|
+
Modify = "Modify",
|
|
4
|
+
Remove = "Remove"
|
|
5
|
+
}
|
|
6
|
+
export type KvsStreamRecord<TItem extends object = any> = {
|
|
7
|
+
keyValueStoreName: string;
|
|
8
|
+
eventType: KvsStreamEventType;
|
|
9
|
+
scope?: string;
|
|
10
|
+
keys: Record<string, unknown>;
|
|
11
|
+
newImage?: TItem;
|
|
12
|
+
oldImage?: TItem;
|
|
13
|
+
};
|
|
14
|
+
export type KvsStreamEventResponse = void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KvsStreamEventType = void 0;
|
|
4
|
+
// One change to a key-value store item, delivered by the store's stream (see
|
|
5
|
+
// defineKeyValueStore's `onStream`).
|
|
6
|
+
//
|
|
7
|
+
// The images are plain objects, not the storage layer's wire format: the action processor
|
|
8
|
+
// unmarshals them, so a stream handler is ordinary story code and never sees a DynamoDB
|
|
9
|
+
// AttributeValue. That also keeps handlers portable to a non-DynamoDB store.
|
|
10
|
+
var KvsStreamEventType;
|
|
11
|
+
(function (KvsStreamEventType) {
|
|
12
|
+
KvsStreamEventType["Insert"] = "Insert";
|
|
13
|
+
KvsStreamEventType["Modify"] = "Modify";
|
|
14
|
+
KvsStreamEventType["Remove"] = "Remove";
|
|
15
|
+
})(KvsStreamEventType || (exports.KvsStreamEventType = KvsStreamEventType = {}));
|
|
16
|
+
//# sourceMappingURL=KvsStreamEvent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KvsStreamEvent.js","sourceRoot":"","sources":["../../../src/types/KvsStreamEvent.ts"],"names":[],"mappings":";;;AAAA,6EAA6E;AAC7E,qCAAqC;AACrC,EAAE;AACF,0FAA0F;AAC1F,wFAAwF;AACxF,6EAA6E;AAC7E,IAAY,kBAIX;AAJD,WAAY,kBAAkB;IAC5B,uCAAiB,CAAA;IACjB,uCAAiB,CAAA;IACjB,uCAAiB,CAAA;AACnB,CAAC,EAJW,kBAAkB,kCAAlB,kBAAkB,QAI7B","sourcesContent":["// One change to a key-value store item, delivered by the store's stream (see\n// defineKeyValueStore's `onStream`).\n//\n// The images are plain objects, not the storage layer's wire format: the action processor\n// unmarshals them, so a stream handler is ordinary story code and never sees a DynamoDB\n// AttributeValue. That also keeps handlers portable to a non-DynamoDB store.\nexport enum KvsStreamEventType {\n Insert = 'Insert',\n Modify = 'Modify',\n Remove = 'Remove',\n}\n\nexport type KvsStreamRecord<TItem extends object = any> = {\n keyValueStoreName: string;\n\n eventType: KvsStreamEventType;\n\n // The storage scope the item was written under; absent for an unscoped row.\n //\n // Scope is composed INTO the partition key value on the way to storage, so the raw stored\n // key carries it. Every other read path hides that (the scoped translator strips it before\n // a consumer ever sees it), and a stream record is the one place it would otherwise leak.\n // Handing it back as its own field keeps that promise: a consumer re-enters the scope by\n // reading this, instead of having to know the composition format and remember to undo it.\n // Forgetting to would mean acting on one tenant's data while pointed at another's.\n scope?: string;\n\n // The item's key attributes only — always present, including on Remove. RAW: the scope has\n // been stripped back out of the partition key, so these are the values the caller wrote.\n keys: Record<string, unknown>;\n\n // The item after the change, scope stripped like `keys`. Absent on Remove.\n newImage?: TItem;\n\n // The item before the change, scope stripped like `keys`. Absent on Insert.\n oldImage?: TItem;\n};\n\n// Handlers are invoked per record, mirroring how storage drive and queue events are\n// dispatched. Ordering is guaranteed per partition key; records for different partition\n// keys may be processed concurrently.\nexport type KvsStreamEventResponse = void;\n"]}
|
|
@@ -72,6 +72,7 @@ export declare enum QpqRuntimeType {
|
|
|
72
72
|
WEBSOCKET_EVENT = "WEBSOCKET_EVENT",
|
|
73
73
|
DEPLOY_EVENT = "DEPLOY_EVENT",
|
|
74
74
|
STORAGEDRIVE_EVENT = "STORAGEDRIVE_EVENT",
|
|
75
|
+
KVS_STREAM_EVENT = "KVS_STREAM_EVENT",
|
|
75
76
|
CLOUD_FLARE_DEPLOY = "CLOUD_FLARE_DEPLOY",
|
|
76
77
|
UNIT_TEST = "UNIT_TEST",
|
|
77
78
|
UI = "UI",
|
|
@@ -13,6 +13,7 @@ var QpqRuntimeType;
|
|
|
13
13
|
QpqRuntimeType["WEBSOCKET_EVENT"] = "WEBSOCKET_EVENT";
|
|
14
14
|
QpqRuntimeType["DEPLOY_EVENT"] = "DEPLOY_EVENT";
|
|
15
15
|
QpqRuntimeType["STORAGEDRIVE_EVENT"] = "STORAGEDRIVE_EVENT";
|
|
16
|
+
QpqRuntimeType["KVS_STREAM_EVENT"] = "KVS_STREAM_EVENT";
|
|
16
17
|
QpqRuntimeType["CLOUD_FLARE_DEPLOY"] = "CLOUD_FLARE_DEPLOY";
|
|
17
18
|
QpqRuntimeType["UNIT_TEST"] = "UNIT_TEST";
|
|
18
19
|
QpqRuntimeType["UI"] = "UI";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorySession.js","sourceRoot":"","sources":["../../../src/types/StorySession.ts"],"names":[],"mappings":";;;AA8GA,IAAY,
|
|
1
|
+
{"version":3,"file":"StorySession.js","sourceRoot":"","sources":["../../../src/types/StorySession.ts"],"names":[],"mappings":";;;AA8GA,IAAY,cAsBX;AAtBD,WAAY,cAAc;IACxB,6BAAW,CAAA;IACX,iDAA+B,CAAA;IAC/B,+EAA6D,CAAA;IAC7D,2DAAyC,CAAA;IACzC,6CAA2B,CAAA;IAC3B,+CAA6B,CAAA;IAC7B,+DAA6C,CAAA;IAC7C,qDAAmC,CAAA;IACnC,+CAA6B,CAAA;IAC7B,2DAAyC,CAAA;IACzC,uDAAqC,CAAA;IACrC,2DAAyC,CAAA;IAEzC,yCAAuB,CAAA;IACvB,2BAAS,CAAA;IAET,uDAAqC,CAAA;IAErC,2EAAyD,CAAA;IACzD,2EAAyD,CAAA;IACzD,2EAAyD,CAAA;AAC3D,CAAC,EAtBW,cAAc,8BAAd,cAAc,QAsBzB","sourcesContent":["import { Action } from './Action';\nimport { QPQError } from './ErrorTypeEnum';\nimport { QpqContext } from './QpqContextIdentifier';\nimport { QpqFunctionRuntime } from './QpqFunctionRuntime';\n\nexport interface DecodedAccessToken {\n userId: string;\n username: string;\n // Unix timestamp (number of seconds since January 1, 1970 UTC).\n exp: number;\n roles?: string[];\n\n userDirectory: string;\n wasValid: boolean;\n}\n\nexport interface StorySession {\n correlation?: string;\n depth: number;\n\n // This token is never transfer cross storys\n // can't access it via events or anything\n accessToken?: string;\n\n // Decoded access token + roles token\n decodedAccessToken?: DecodedAccessToken;\n\n // Context\n context: QpqContext<any>;\n\n // Service-local context, this is never transferred across a service boundary\n // (queue / event bus / service function), but does flow to in-process sub-runtimes.\n localContext?: QpqContext<any>;\n\n // Globals declared on the executing function's QpqFunctionRuntimeAdvanced.\n // Set per-runtime from qpqFunctionRuntimeInfo; never inherited from the caller.\n functionGlobals?: Record<string, unknown>;\n}\n\nexport type StorySessionUpdater = (newSession: Partial<StorySession>) => void;\n\nexport type AskResponse<T> = Generator<Action<any>, T, any>;\n\nexport type Story<TArgs extends any[], R> = (...args: TArgs) => AskResponse<R>;\nexport type StoryResolver = <S extends Story<any, any>>(story: S) => (...args: Parameters<S>) => Promise<AskResponseReturnType<ReturnType<S>>>;\n\n// function* test(msg: string): AskResponse<number> {\n// return parseInt(msg);\n// }\n\n// function DoThing<S extends Story<any, any>>(story: S): (...args: Parameters<S>) => AskResponseReturnType<ReturnType<S>> {\n// return (...args: Parameters<S>) => {\n// return (() => {}) as any;\n// };\n// }\n\n// const runTest = DoThing(test);\n// const num = runTest('a');\n\n/**\n * Utility Type: ExtractGeneratorReturnType\n *\n * This utility type is designed to extract the return type of a Generator without\n * conflating it with the types of values it may yield. Generators in TypeScript are\n * annotated as Generator<YieldType, ReturnType, NextType>, where YieldType is the\n * type of values yielded by the generator, ReturnType is the type the generator returns\n * when it's done, and NextType is the type of the value passed to the generator's `next` method.\n *\n * By leveraging TypeScript's conditional types and the `infer` keyword, ExtractGeneratorReturnType\n * can precisely capture and extract just the ReturnType of any given Generator, providing a clean,\n * type-safe way to understand what a generator ultimately returns upon completion.\n *\n * Usage of `infer` in Conditional Types:\n * The `infer` keyword is used here to declare a type variable R within the conditional type\n * expression. TypeScript will then infer the type R as the generator's ReturnType when the\n * condition `T extends Generator<any, infer R, any>` is met. This inference is based on the\n * actual type passed to ExtractGeneratorReturnType, allowing us to \"pull out\" the ReturnType\n * from within the Generator type.\n *\n * This approach is particularly useful because it directly targets the ReturnType without\n * having to manually sift through or exclude the types of values that might be yielded by\n * the generator, which can often be diverse and not relevant to the final return type we're\n * interested in capturing.\n *\n * The ExtractGeneratorReturnType utility type simplifies the extraction process and ensures\n * that types dependent on the return value of generators are accurately typed, enhancing\n * code readability and maintainability.\n */\n\nexport type ExtractGeneratorReturnType<T extends Generator> = T extends Generator<any, infer R, any> ? R : never;\n\n// Directly extracting the generator's return type for any given AskResponse.\n// By applying ExtractGeneratorReturnType to an AskResponse, we can determine the\n// precise type that the AskResponse generator returns, bypassing the complexities\n// of its yield types. This is particularly useful in scenarios where the return type\n// is distinct from the types of actions or values yielded by the generator, allowing\n// for clear type inference and usage in TypeScript codebases.\nexport type AskResponseReturnType<T extends AskResponse<any>> = ExtractGeneratorReturnType<T>;\n\n// Any generator function that returns an AskResponse\n// qpq runtimes are built on stories\nexport type qpqStory = <T = any>(...args: any[]) => AskResponse<T>;\n\nexport interface ActionHistory<P = any, R = any> {\n act: Action<P>;\n res: R;\n startedAt: string;\n finishedAt: string;\n}\n\nexport enum QpqRuntimeType {\n API = 'API',\n EXECUTE_STORY = 'EXECUTE_STORY',\n EXECUTE_IMPLEMENTATION_STORY = 'EXECUTE_IMPLEMENTATION_STORY',\n RECURRING_SCHEDULE = 'RECURRING_SCHEDULE',\n QUEUE_EVENT = 'QUEUE_EVENT',\n EVENT_SEO_OR = 'EVENT_SEO_OR',\n SERVICE_FUNCTION_EXE = 'SERVICE_FUNCTION_EXE',\n WEBSOCKET_EVENT = 'WEBSOCKET_EVENT',\n DEPLOY_EVENT = 'DEPLOY_EVENT',\n STORAGEDRIVE_EVENT = 'STORAGEDRIVE_EVENT',\n KVS_STREAM_EVENT = 'KVS_STREAM_EVENT',\n CLOUD_FLARE_DEPLOY = 'CLOUD_FLARE_DEPLOY',\n\n UNIT_TEST = 'UNIT_TEST',\n UI = 'UI',\n\n SEND_EMAIL_EVENT = 'SEND_EMAIL_EVENT',\n\n AUTH_DEFINE_AUTH_CHALLENGE = 'AUTH_DEFINE_AUTH_CHALLENGE',\n AUTH_CREATE_AUTH_CHALLENGE = 'AUTH_CREATE_AUTH_CHALLENGE',\n AUTH_VERIFY_AUTH_CHALLENGE = 'AUTH_VERIFY_AUTH_CHALLENGE',\n}\n\nexport interface qpqConsoleLog {\n t: string;\n a: any[];\n}\n\nexport interface StoryResult<TArgs extends Array<any>, TResult = any> {\n // Params to story\n input: TArgs;\n\n // Story Session Data\n session: StorySession;\n\n // History of actions\n history: ActionHistory[];\n\n // When the story started / finished\n startedAt: string;\n finishedAt: string;\n\n // correlationGuid from the calling story\n fromCorrelation?: string;\n\n // correlation for this story (correlates all actions together)\n correlation: string;\n\n // For logging, tags to help identify the story\n tags: string[];\n moduleName: string;\n\n // Result of the story result or error not both\n result?: TResult;\n error?: QPQError;\n\n // User specified runtime type\n runtimeType: QpqRuntimeType;\n\n // Impure Logs\n logs?: qpqConsoleLog[];\n\n // What was the function runtime that this was executed against\n qpqFunctionRuntimeInfo?: QpqFunctionRuntime;\n}\n\nexport type StoryResultMetadata = {\n correlation: string;\n fromCorrelation?: string;\n\n moduleName: string;\n runtimeType: QpqRuntimeType;\n\n startedAt: string;\n\n generic: string;\n\n error?: string;\n\n executionTimeMs: number;\n\n userInfo?: string;\n\n qpqFunctionRuntimeInfo?: QpqFunctionRuntime;\n};\n\nexport type StoryResultMetadataWithChildren = StoryResultMetadata & {\n children: StoryResultMetadataWithChildren[];\n};\n"]}
|
|
@@ -13,6 +13,7 @@ export * from './EventMessage';
|
|
|
13
13
|
export * from './ExcludeKeys';
|
|
14
14
|
export * from './FullyQualifiedResource';
|
|
15
15
|
export * from './KeyOf';
|
|
16
|
+
export * from './KvsStreamEvent';
|
|
16
17
|
export * from './LogLevelEnum';
|
|
17
18
|
export * from './Lookup';
|
|
18
19
|
export * from './NotifyErrorQueueEvent';
|
|
@@ -29,6 +29,7 @@ __exportStar(require("./EventMessage"), exports);
|
|
|
29
29
|
__exportStar(require("./ExcludeKeys"), exports);
|
|
30
30
|
__exportStar(require("./FullyQualifiedResource"), exports);
|
|
31
31
|
__exportStar(require("./KeyOf"), exports);
|
|
32
|
+
__exportStar(require("./KvsStreamEvent"), exports);
|
|
32
33
|
__exportStar(require("./LogLevelEnum"), exports);
|
|
33
34
|
__exportStar(require("./Lookup"), exports);
|
|
34
35
|
__exportStar(require("./NotifyErrorQueueEvent"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,kDAAgC;AAChC,oDAAkC;AAClC,wDAAsC;AACtC,uDAAqC;AACrC,qDAAmC;AACnC,6DAA2C;AAC3C,qDAAmC;AACnC,gDAA8B;AAC9B,wDAAsC;AACtC,kDAAgC;AAChC,iDAA+B;AAC/B,gDAA8B;AAC9B,2DAAyC;AACzC,0CAAwB;AACxB,iDAA+B;AAC/B,2CAAyB;AACzB,0DAAwC;AACxC,6CAA2B;AAC3B,kDAAgC;AAChC,yDAAuC;AACvC,sDAAoC;AACpC,uDAAqC;AACrC,mDAAiC;AACjC,8CAA4B;AAC5B,iDAA+B;AAC/B,+CAA6B;AAC7B,gDAA8B;AAC9B,mDAAiC;AACjC,iDAA+B;AAC/B,mDAAiC;AACjC,sDAAoC;AACpC,wDAAsC;AACtC,0CAAwB","sourcesContent":["export * from './Action';\nexport * from './authChallenge';\nexport * from './BoundLogicStory';\nexport * from './ConfigUrlDefinition';\nexport * from './CrossModuleMessage';\nexport * from './CrossModuleOwner';\nexport * from './CrossServiceResourceName';\nexport * from './DecomposedString';\nexport * from './DeployEvent';\nexport * from './DynamicModuleLoader';\nexport * from './ErrorTypeEnum';\nexport * from './EventMessage';\nexport * from './ExcludeKeys';\nexport * from './FullyQualifiedResource';\nexport * from './KeyOf';\nexport * from './LogLevelEnum';\nexport * from './Lookup';\nexport * from './NotifyErrorQueueEvent';\nexport * from './Nullable';\nexport * from './QPQBinaryData';\nexport * from './QpqContextIdentifier';\nexport * from './QpqExecutionTrace';\nexport * from './QpqFunctionRuntime';\nexport * from './QpqIsoDateTime';\nexport * from './QpqLogger';\nexport * from './QpqPagedData';\nexport * from './QueueEvent';\nexport * from './Readonlyify';\nexport * from './ScheduledEvent';\nexport * from './StorySession';\nexport * from './StreamRegistry';\nexport * from './UnboundLogicStory';\nexport * from './UnionToIntersection';\nexport * from './utils';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,kDAAgC;AAChC,oDAAkC;AAClC,wDAAsC;AACtC,uDAAqC;AACrC,qDAAmC;AACnC,6DAA2C;AAC3C,qDAAmC;AACnC,gDAA8B;AAC9B,wDAAsC;AACtC,kDAAgC;AAChC,iDAA+B;AAC/B,gDAA8B;AAC9B,2DAAyC;AACzC,0CAAwB;AACxB,mDAAiC;AACjC,iDAA+B;AAC/B,2CAAyB;AACzB,0DAAwC;AACxC,6CAA2B;AAC3B,kDAAgC;AAChC,yDAAuC;AACvC,sDAAoC;AACpC,uDAAqC;AACrC,mDAAiC;AACjC,8CAA4B;AAC5B,iDAA+B;AAC/B,+CAA6B;AAC7B,gDAA8B;AAC9B,mDAAiC;AACjC,iDAA+B;AAC/B,mDAAiC;AACjC,sDAAoC;AACpC,wDAAsC;AACtC,0CAAwB","sourcesContent":["export * from './Action';\nexport * from './authChallenge';\nexport * from './BoundLogicStory';\nexport * from './ConfigUrlDefinition';\nexport * from './CrossModuleMessage';\nexport * from './CrossModuleOwner';\nexport * from './CrossServiceResourceName';\nexport * from './DecomposedString';\nexport * from './DeployEvent';\nexport * from './DynamicModuleLoader';\nexport * from './ErrorTypeEnum';\nexport * from './EventMessage';\nexport * from './ExcludeKeys';\nexport * from './FullyQualifiedResource';\nexport * from './KeyOf';\nexport * from './KvsStreamEvent';\nexport * from './LogLevelEnum';\nexport * from './Lookup';\nexport * from './NotifyErrorQueueEvent';\nexport * from './Nullable';\nexport * from './QPQBinaryData';\nexport * from './QpqContextIdentifier';\nexport * from './QpqExecutionTrace';\nexport * from './QpqFunctionRuntime';\nexport * from './QpqIsoDateTime';\nexport * from './QpqLogger';\nexport * from './QpqPagedData';\nexport * from './QueueEvent';\nexport * from './Readonlyify';\nexport * from './ScheduledEvent';\nexport * from './StorySession';\nexport * from './StreamRegistry';\nexport * from './UnboundLogicStory';\nexport * from './UnionToIntersection';\nexport * from './utils';\n"]}
|
|
@@ -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
|
}
|
|
@@ -7,5 +7,6 @@ export var KeyValueStoreActionType;
|
|
|
7
7
|
KeyValueStoreActionType["Update"] = "@quidproquo-core/KeyValueStore/Update";
|
|
8
8
|
KeyValueStoreActionType["Query"] = "@quidproquo-core/KeyValueStore/Query";
|
|
9
9
|
KeyValueStoreActionType["Scan"] = "@quidproquo-core/KeyValueStore/Scan";
|
|
10
|
+
KeyValueStoreActionType["ScanAllScopes"] = "@quidproquo-core/KeyValueStore/ScanAllScopes";
|
|
10
11
|
})(KeyValueStoreActionType || (KeyValueStoreActionType = {}));
|
|
11
12
|
//# sourceMappingURL=KeyValueStoreActionType.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KeyValueStoreActionType.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/KeyValueStoreActionType.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,
|
|
1
|
+
{"version":3,"file":"KeyValueStoreActionType.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/KeyValueStoreActionType.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,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,KAAvB,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"]}
|
|
@@ -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,43 @@
|
|
|
1
|
+
import { createErrorEnumForAction } from '../../types';
|
|
2
|
+
import { KeyValueStoreActionType } from './KeyValueStoreActionType';
|
|
3
|
+
export const KeyValueStoreScanAllScopesErrorTypeEnum = createErrorEnumForAction(KeyValueStoreActionType.ScanAllScopes, [
|
|
4
|
+
'ServiceUnavailable', // DynamoDB internal error / throttling
|
|
5
|
+
'ResourceNotFound', // the underlying table does not exist
|
|
6
|
+
'StoreNotFound', // the store is not declared in the qpq config (misconfiguration)
|
|
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 function* askKeyValueStoreScanAllScopes(keyValueStoreName, filterCondition, nextPageKey, options) {
|
|
33
|
+
return yield {
|
|
34
|
+
type: KeyValueStoreActionType.ScanAllScopes,
|
|
35
|
+
payload: {
|
|
36
|
+
keyValueStoreName,
|
|
37
|
+
filterCondition,
|
|
38
|
+
nextPageKey,
|
|
39
|
+
options,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=KeyValueStoreScanAllScopesActionRequester.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyValueStoreScanAllScopesActionRequester.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/KeyValueStoreScanAllScopesActionRequester.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAIpE,MAAM,CAAC,MAAM,uCAAuC,GAAG,wBAAwB,CAAC,uBAAuB,CAAC,aAAa,EAAE;IACrH,oBAAoB,EAAE,uCAAuC;IAC7D,kBAAkB,EAAE,sCAAsC;IAC1D,eAAe,EAAE,iEAAiE;CACnF,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,SAAS,CAAC,CAAC,6BAA6B,CAC5C,iBAAyB,EACzB,eAAmC,EACnC,WAAoB,EACpB,OAA2C;IAE3C,OAAO,MAAM;QACX,IAAI,EAAE,uBAAuB,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 @@
|
|
|
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';
|
|
@@ -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';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/actions/keyValueStore/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,qCAAqC,CAAC;AACpD,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC","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,cAAc,yCAAyC,CAAC;AACxD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,qCAAqC,CAAC;AACpD,cAAc,iCAAiC,CAAC;AAChD,cAAc,oCAAoC,CAAC;AACnD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC","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 =
|
|
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 =
|
|
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 {};
|
|
@@ -37,5 +37,6 @@ export const defineKeyValueStore = (keyValueStoreName, partitionKey, sortKeys =
|
|
|
37
37
|
ttlAttribute: options?.ttlAttribute,
|
|
38
38
|
disablePointInTimeRecovery: options?.disablePointInTimeRecovery ?? false,
|
|
39
39
|
encryption: options?.encryption ?? false,
|
|
40
|
+
onStream: options?.onStream,
|
|
40
41
|
});
|
|
41
42
|
//# sourceMappingURL=keyValueStore.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyValueStore.js","sourceRoot":"","sources":["../../../../src/config/settings/keyValueStore.ts"],"names":[],"mappings":"AACA,OAAO,EAA+C,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAmBrG,MAAM,CAAC,MAAM,MAAM,GAAG,CAAyB,GAAa,EAAE,OAAmB,QAAQ,EAAa,EAAE,CAAC,CAAC;IACxG,GAAG;IACH,IAAI;CACL,CAAC,CAAC;AAWH,MAAM,8BAA8B,GAAG,CAAyB,eAAmC,EAAa,EAAE;IAChH,oBAAoB;IACpB,IAAI,OAAO,eAAe,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,MAAM,CAAI,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,MAAM,CAAI,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;
|
|
1
|
+
{"version":3,"file":"keyValueStore.js","sourceRoot":"","sources":["../../../../src/config/settings/keyValueStore.ts"],"names":[],"mappings":"AACA,OAAO,EAA+C,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAmBrG,MAAM,CAAC,MAAM,MAAM,GAAG,CAAyB,GAAa,EAAE,OAAmB,QAAQ,EAAa,EAAE,CAAC,CAAC;IACxG,GAAG;IACH,IAAI;CACL,CAAC,CAAC;AAWH,MAAM,8BAA8B,GAAG,CAAyB,eAAmC,EAAa,EAAE;IAChH,oBAAoB;IACpB,IAAI,OAAO,eAAe,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,MAAM,CAAI,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,MAAM,CAAI,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+EF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,iBAAyB,EAEzB,YAAgC,EAChC,WAAiC,EAAE,EAEnC,OAAmD,EACjB,EAAE,CAAC,CAAC;IACtC,iBAAiB,EAAE,wBAAwB,CAAC,aAAa;IACzD,SAAS,EAAE,iBAAiB;IAE5B,iBAAiB;IAEjB,YAAY,EAAE,8BAA8B,CAAI,YAAY,CAAC;IAC7D,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAA,8BAAiC,CAAA,CAAC;IAEzD,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAA,kCAAqC,CAAA,CAAC;IAE5E,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,KAAK;IAEhC,KAAK,EAAE,OAAO,EAAE,KAAK;IAErB,YAAY,EAAE,OAAO,EAAE,YAAY;IAEnC,0BAA0B,EAAE,OAAO,EAAE,0BAA0B,IAAI,KAAK;IAExE,UAAU,EAAE,OAAO,EAAE,UAAU,IAAI,KAAK;IAExC,QAAQ,EAAE,OAAO,EAAE,QAAQ;CAC5B,CAAC,CAAC","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"]}
|
|
@@ -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;
|
|
@@ -22,6 +22,23 @@ export function composeScopedKvsValue(scope, rawValue) {
|
|
|
22
22
|
validateRawPkValueForScopeOrThrow(rawValue);
|
|
23
23
|
return `${scope}${KVS_SCOPE_DELIMITER}${rawValue}`;
|
|
24
24
|
}
|
|
25
|
+
// The inverse of composeScopedKvsValue: split a stored partition key value back into the
|
|
26
|
+
// scope it was written under and the caller's raw key.
|
|
27
|
+
//
|
|
28
|
+
// Needed by anything reading a RAW stored key rather than going through a scoped request.
|
|
29
|
+
// Change data capture is the case that forced it: a stream record carries the composed pk,
|
|
30
|
+
// and the consumer has to re-enter the same scope before it can act on the item. An
|
|
31
|
+
// unscoped value has no delimiter and comes back with `scope: undefined`.
|
|
32
|
+
export const decomposeScopedKvsValue = (composedValue) => {
|
|
33
|
+
const at = composedValue.indexOf(KVS_SCOPE_DELIMITER);
|
|
34
|
+
if (at < 0) {
|
|
35
|
+
return { rawValue: composedValue };
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
scope: composedValue.slice(0, at),
|
|
39
|
+
rawValue: composedValue.slice(at + KVS_SCOPE_DELIMITER.length),
|
|
40
|
+
};
|
|
41
|
+
};
|
|
25
42
|
// The delimiter is reserved in scoped partition-key values: a raw value
|
|
26
43
|
// carrying it would strip ambiguously and could collide with (or forge)
|
|
27
44
|
// 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":"AAAA,OAAO,EAAsC,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC9G,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,0EAA0E;AAC1E,6EAA6E;AAC7E,SAAS;AACT,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAElD,gGAAgG;AAChG,8EAA8E;AAC9E,4EAA4E;AAC5E,MAAM,UAAU,qBAAqB,CAAC,KAAyB,EAAE,QAAyB;IACxF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAE5B,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,iBAAiB,CAAC,qBAAqB,CAAC,gBAAgB,EAAE,yDAAyD,CAAC,CAAC;IACjI,CAAC;IAED,iCAAiC,CAAC,QAAQ,CAAC,CAAC;IAE5C,OAAO,GAAG,KAAK,GAAG,mBAAmB,GAAG,QAAQ,EAAE,CAAC;AACrD,CAAC;AAED,wEAAwE;AACxE,wEAAwE;AACxE,8EAA8E;AAC9E,yEAAyE;AACzE,eAAe;AACf,MAAM,UAAU,iCAAiC,CAAC,QAAyB;IACzE,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,iBAAiB,CACzB,qBAAqB,CAAC,iBAAiB,EACvC,8DAA8D,mBAAmB,IAAI,CACtF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,8EAA8E;AAC9E,0DAA0D;AAC1D,MAAM,UAAU,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,mBAAmB,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,MAAM,UAAU,yCAAyC,CAAC,gBAAwB;IAChF,IAAI,gBAAgB,KAAK,QAAQ,EAAE,CAAC;QAClC,MAAM,IAAI,iBAAiB,CACzB,qBAAqB,CAAC,gBAAgB,EACtC,uEAAuE,gBAAgB,KAAK,CAC7F,CAAC;IACJ,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,gFAAgF;AAChF,6EAA6E;AAC7E,+BAA+B;AAC/B,MAAM,UAAU,+BAA+B,CAAC,eAAuB;IACrE,OAAO;QACL,GAAG,EAAE,eAAe;QACpB,SAAS,EAAE,qBAAqB,CAAC,WAAW;QAC5C,MAAM,EAAE,mBAAmB;KAC5B,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,4DAA4D;AAC5D,MAAM,UAAU,gCAAgC,CAAC,eAAuB,EAAE,KAAa;IACrF,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAE5B,OAAO;QACL,GAAG,EAAE,eAAe;QACpB,SAAS,EAAE,qBAAqB,CAAC,UAAU;QAC3C,MAAM,EAAE,GAAG,KAAK,GAAG,mBAAmB,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":"AAAA,OAAO,EAAsC,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC9G,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAE9D,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,0EAA0E;AAC1E,6EAA6E;AAC7E,SAAS;AACT,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAElD,gGAAgG;AAChG,8EAA8E;AAC9E,4EAA4E;AAC5E,MAAM,UAAU,qBAAqB,CAAC,KAAyB,EAAE,QAAyB;IACxF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAE5B,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,iBAAiB,CAAC,qBAAqB,CAAC,gBAAgB,EAAE,yDAAyD,CAAC,CAAC;IACjI,CAAC;IAED,iCAAiC,CAAC,QAAQ,CAAC,CAAC;IAE5C,OAAO,GAAG,KAAK,GAAG,mBAAmB,GAAG,QAAQ,EAAE,CAAC;AACrD,CAAC;AAED,yFAAyF;AACzF,uDAAuD;AACvD,EAAE;AACF,0FAA0F;AAC1F,2FAA2F;AAC3F,oFAAoF;AACpF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,aAAqB,EAAwC,EAAE;IACrG,MAAM,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAEtD,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QACX,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,CAAC;IACrC,CAAC;IAED,OAAO;QACL,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QACjC,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,GAAG,mBAAmB,CAAC,MAAM,CAAC;KAC/D,CAAC;AACJ,CAAC,CAAC;AAEF,wEAAwE;AACxE,wEAAwE;AACxE,8EAA8E;AAC9E,yEAAyE;AACzE,eAAe;AACf,MAAM,UAAU,iCAAiC,CAAC,QAAyB;IACzE,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,iBAAiB,CACzB,qBAAqB,CAAC,iBAAiB,EACvC,8DAA8D,mBAAmB,IAAI,CACtF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,8EAA8E;AAC9E,0DAA0D;AAC1D,MAAM,UAAU,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,mBAAmB,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,MAAM,UAAU,yCAAyC,CAAC,gBAAwB;IAChF,IAAI,gBAAgB,KAAK,QAAQ,EAAE,CAAC;QAClC,MAAM,IAAI,iBAAiB,CACzB,qBAAqB,CAAC,gBAAgB,EACtC,uEAAuE,gBAAgB,KAAK,CAC7F,CAAC;IACJ,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,gFAAgF;AAChF,6EAA6E;AAC7E,+BAA+B;AAC/B,MAAM,UAAU,+BAA+B,CAAC,eAAuB;IACrE,OAAO;QACL,GAAG,EAAE,eAAe;QACpB,SAAS,EAAE,qBAAqB,CAAC,WAAW;QAC5C,MAAM,EAAE,mBAAmB;KAC5B,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,4DAA4D;AAC5D,MAAM,UAAU,gCAAgC,CAAC,eAAuB,EAAE,KAAa;IACrF,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAE5B,OAAO;QACL,GAAG,EAAE,eAAe;QACpB,SAAS,EAAE,qBAAqB,CAAC,UAAU;QAC3C,MAAM,EAAE,GAAG,KAAK,GAAG,mBAAmB,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 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,14 @@
|
|
|
1
|
+
export declare enum KvsStreamEventType {
|
|
2
|
+
Insert = "Insert",
|
|
3
|
+
Modify = "Modify",
|
|
4
|
+
Remove = "Remove"
|
|
5
|
+
}
|
|
6
|
+
export type KvsStreamRecord<TItem extends object = any> = {
|
|
7
|
+
keyValueStoreName: string;
|
|
8
|
+
eventType: KvsStreamEventType;
|
|
9
|
+
scope?: string;
|
|
10
|
+
keys: Record<string, unknown>;
|
|
11
|
+
newImage?: TItem;
|
|
12
|
+
oldImage?: TItem;
|
|
13
|
+
};
|
|
14
|
+
export type KvsStreamEventResponse = void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// One change to a key-value store item, delivered by the store's stream (see
|
|
2
|
+
// defineKeyValueStore's `onStream`).
|
|
3
|
+
//
|
|
4
|
+
// The images are plain objects, not the storage layer's wire format: the action processor
|
|
5
|
+
// unmarshals them, so a stream handler is ordinary story code and never sees a DynamoDB
|
|
6
|
+
// AttributeValue. That also keeps handlers portable to a non-DynamoDB store.
|
|
7
|
+
export var KvsStreamEventType;
|
|
8
|
+
(function (KvsStreamEventType) {
|
|
9
|
+
KvsStreamEventType["Insert"] = "Insert";
|
|
10
|
+
KvsStreamEventType["Modify"] = "Modify";
|
|
11
|
+
KvsStreamEventType["Remove"] = "Remove";
|
|
12
|
+
})(KvsStreamEventType || (KvsStreamEventType = {}));
|
|
13
|
+
//# sourceMappingURL=KvsStreamEvent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KvsStreamEvent.js","sourceRoot":"","sources":["../../../src/types/KvsStreamEvent.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,qCAAqC;AACrC,EAAE;AACF,0FAA0F;AAC1F,wFAAwF;AACxF,6EAA6E;AAC7E,MAAM,CAAN,IAAY,kBAIX;AAJD,WAAY,kBAAkB;IAC5B,uCAAiB,CAAA;IACjB,uCAAiB,CAAA;IACjB,uCAAiB,CAAA;AACnB,CAAC,EAJW,kBAAkB,KAAlB,kBAAkB,QAI7B","sourcesContent":["// One change to a key-value store item, delivered by the store's stream (see\n// defineKeyValueStore's `onStream`).\n//\n// The images are plain objects, not the storage layer's wire format: the action processor\n// unmarshals them, so a stream handler is ordinary story code and never sees a DynamoDB\n// AttributeValue. That also keeps handlers portable to a non-DynamoDB store.\nexport enum KvsStreamEventType {\n Insert = 'Insert',\n Modify = 'Modify',\n Remove = 'Remove',\n}\n\nexport type KvsStreamRecord<TItem extends object = any> = {\n keyValueStoreName: string;\n\n eventType: KvsStreamEventType;\n\n // The storage scope the item was written under; absent for an unscoped row.\n //\n // Scope is composed INTO the partition key value on the way to storage, so the raw stored\n // key carries it. Every other read path hides that (the scoped translator strips it before\n // a consumer ever sees it), and a stream record is the one place it would otherwise leak.\n // Handing it back as its own field keeps that promise: a consumer re-enters the scope by\n // reading this, instead of having to know the composition format and remember to undo it.\n // Forgetting to would mean acting on one tenant's data while pointed at another's.\n scope?: string;\n\n // The item's key attributes only — always present, including on Remove. RAW: the scope has\n // been stripped back out of the partition key, so these are the values the caller wrote.\n keys: Record<string, unknown>;\n\n // The item after the change, scope stripped like `keys`. Absent on Remove.\n newImage?: TItem;\n\n // The item before the change, scope stripped like `keys`. Absent on Insert.\n oldImage?: TItem;\n};\n\n// Handlers are invoked per record, mirroring how storage drive and queue events are\n// dispatched. Ordering is guaranteed per partition key; records for different partition\n// keys may be processed concurrently.\nexport type KvsStreamEventResponse = void;\n"]}
|
|
@@ -72,6 +72,7 @@ export declare enum QpqRuntimeType {
|
|
|
72
72
|
WEBSOCKET_EVENT = "WEBSOCKET_EVENT",
|
|
73
73
|
DEPLOY_EVENT = "DEPLOY_EVENT",
|
|
74
74
|
STORAGEDRIVE_EVENT = "STORAGEDRIVE_EVENT",
|
|
75
|
+
KVS_STREAM_EVENT = "KVS_STREAM_EVENT",
|
|
75
76
|
CLOUD_FLARE_DEPLOY = "CLOUD_FLARE_DEPLOY",
|
|
76
77
|
UNIT_TEST = "UNIT_TEST",
|
|
77
78
|
UI = "UI",
|
|
@@ -10,6 +10,7 @@ export var QpqRuntimeType;
|
|
|
10
10
|
QpqRuntimeType["WEBSOCKET_EVENT"] = "WEBSOCKET_EVENT";
|
|
11
11
|
QpqRuntimeType["DEPLOY_EVENT"] = "DEPLOY_EVENT";
|
|
12
12
|
QpqRuntimeType["STORAGEDRIVE_EVENT"] = "STORAGEDRIVE_EVENT";
|
|
13
|
+
QpqRuntimeType["KVS_STREAM_EVENT"] = "KVS_STREAM_EVENT";
|
|
13
14
|
QpqRuntimeType["CLOUD_FLARE_DEPLOY"] = "CLOUD_FLARE_DEPLOY";
|
|
14
15
|
QpqRuntimeType["UNIT_TEST"] = "UNIT_TEST";
|
|
15
16
|
QpqRuntimeType["UI"] = "UI";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorySession.js","sourceRoot":"","sources":["../../../src/types/StorySession.ts"],"names":[],"mappings":"AA8GA,MAAM,CAAN,IAAY,
|
|
1
|
+
{"version":3,"file":"StorySession.js","sourceRoot":"","sources":["../../../src/types/StorySession.ts"],"names":[],"mappings":"AA8GA,MAAM,CAAN,IAAY,cAsBX;AAtBD,WAAY,cAAc;IACxB,6BAAW,CAAA;IACX,iDAA+B,CAAA;IAC/B,+EAA6D,CAAA;IAC7D,2DAAyC,CAAA;IACzC,6CAA2B,CAAA;IAC3B,+CAA6B,CAAA;IAC7B,+DAA6C,CAAA;IAC7C,qDAAmC,CAAA;IACnC,+CAA6B,CAAA;IAC7B,2DAAyC,CAAA;IACzC,uDAAqC,CAAA;IACrC,2DAAyC,CAAA;IAEzC,yCAAuB,CAAA;IACvB,2BAAS,CAAA;IAET,uDAAqC,CAAA;IAErC,2EAAyD,CAAA;IACzD,2EAAyD,CAAA;IACzD,2EAAyD,CAAA;AAC3D,CAAC,EAtBW,cAAc,KAAd,cAAc,QAsBzB","sourcesContent":["import { Action } from './Action';\nimport { QPQError } from './ErrorTypeEnum';\nimport { QpqContext } from './QpqContextIdentifier';\nimport { QpqFunctionRuntime } from './QpqFunctionRuntime';\n\nexport interface DecodedAccessToken {\n userId: string;\n username: string;\n // Unix timestamp (number of seconds since January 1, 1970 UTC).\n exp: number;\n roles?: string[];\n\n userDirectory: string;\n wasValid: boolean;\n}\n\nexport interface StorySession {\n correlation?: string;\n depth: number;\n\n // This token is never transfer cross storys\n // can't access it via events or anything\n accessToken?: string;\n\n // Decoded access token + roles token\n decodedAccessToken?: DecodedAccessToken;\n\n // Context\n context: QpqContext<any>;\n\n // Service-local context, this is never transferred across a service boundary\n // (queue / event bus / service function), but does flow to in-process sub-runtimes.\n localContext?: QpqContext<any>;\n\n // Globals declared on the executing function's QpqFunctionRuntimeAdvanced.\n // Set per-runtime from qpqFunctionRuntimeInfo; never inherited from the caller.\n functionGlobals?: Record<string, unknown>;\n}\n\nexport type StorySessionUpdater = (newSession: Partial<StorySession>) => void;\n\nexport type AskResponse<T> = Generator<Action<any>, T, any>;\n\nexport type Story<TArgs extends any[], R> = (...args: TArgs) => AskResponse<R>;\nexport type StoryResolver = <S extends Story<any, any>>(story: S) => (...args: Parameters<S>) => Promise<AskResponseReturnType<ReturnType<S>>>;\n\n// function* test(msg: string): AskResponse<number> {\n// return parseInt(msg);\n// }\n\n// function DoThing<S extends Story<any, any>>(story: S): (...args: Parameters<S>) => AskResponseReturnType<ReturnType<S>> {\n// return (...args: Parameters<S>) => {\n// return (() => {}) as any;\n// };\n// }\n\n// const runTest = DoThing(test);\n// const num = runTest('a');\n\n/**\n * Utility Type: ExtractGeneratorReturnType\n *\n * This utility type is designed to extract the return type of a Generator without\n * conflating it with the types of values it may yield. Generators in TypeScript are\n * annotated as Generator<YieldType, ReturnType, NextType>, where YieldType is the\n * type of values yielded by the generator, ReturnType is the type the generator returns\n * when it's done, and NextType is the type of the value passed to the generator's `next` method.\n *\n * By leveraging TypeScript's conditional types and the `infer` keyword, ExtractGeneratorReturnType\n * can precisely capture and extract just the ReturnType of any given Generator, providing a clean,\n * type-safe way to understand what a generator ultimately returns upon completion.\n *\n * Usage of `infer` in Conditional Types:\n * The `infer` keyword is used here to declare a type variable R within the conditional type\n * expression. TypeScript will then infer the type R as the generator's ReturnType when the\n * condition `T extends Generator<any, infer R, any>` is met. This inference is based on the\n * actual type passed to ExtractGeneratorReturnType, allowing us to \"pull out\" the ReturnType\n * from within the Generator type.\n *\n * This approach is particularly useful because it directly targets the ReturnType without\n * having to manually sift through or exclude the types of values that might be yielded by\n * the generator, which can often be diverse and not relevant to the final return type we're\n * interested in capturing.\n *\n * The ExtractGeneratorReturnType utility type simplifies the extraction process and ensures\n * that types dependent on the return value of generators are accurately typed, enhancing\n * code readability and maintainability.\n */\n\nexport type ExtractGeneratorReturnType<T extends Generator> = T extends Generator<any, infer R, any> ? R : never;\n\n// Directly extracting the generator's return type for any given AskResponse.\n// By applying ExtractGeneratorReturnType to an AskResponse, we can determine the\n// precise type that the AskResponse generator returns, bypassing the complexities\n// of its yield types. This is particularly useful in scenarios where the return type\n// is distinct from the types of actions or values yielded by the generator, allowing\n// for clear type inference and usage in TypeScript codebases.\nexport type AskResponseReturnType<T extends AskResponse<any>> = ExtractGeneratorReturnType<T>;\n\n// Any generator function that returns an AskResponse\n// qpq runtimes are built on stories\nexport type qpqStory = <T = any>(...args: any[]) => AskResponse<T>;\n\nexport interface ActionHistory<P = any, R = any> {\n act: Action<P>;\n res: R;\n startedAt: string;\n finishedAt: string;\n}\n\nexport enum QpqRuntimeType {\n API = 'API',\n EXECUTE_STORY = 'EXECUTE_STORY',\n EXECUTE_IMPLEMENTATION_STORY = 'EXECUTE_IMPLEMENTATION_STORY',\n RECURRING_SCHEDULE = 'RECURRING_SCHEDULE',\n QUEUE_EVENT = 'QUEUE_EVENT',\n EVENT_SEO_OR = 'EVENT_SEO_OR',\n SERVICE_FUNCTION_EXE = 'SERVICE_FUNCTION_EXE',\n WEBSOCKET_EVENT = 'WEBSOCKET_EVENT',\n DEPLOY_EVENT = 'DEPLOY_EVENT',\n STORAGEDRIVE_EVENT = 'STORAGEDRIVE_EVENT',\n KVS_STREAM_EVENT = 'KVS_STREAM_EVENT',\n CLOUD_FLARE_DEPLOY = 'CLOUD_FLARE_DEPLOY',\n\n UNIT_TEST = 'UNIT_TEST',\n UI = 'UI',\n\n SEND_EMAIL_EVENT = 'SEND_EMAIL_EVENT',\n\n AUTH_DEFINE_AUTH_CHALLENGE = 'AUTH_DEFINE_AUTH_CHALLENGE',\n AUTH_CREATE_AUTH_CHALLENGE = 'AUTH_CREATE_AUTH_CHALLENGE',\n AUTH_VERIFY_AUTH_CHALLENGE = 'AUTH_VERIFY_AUTH_CHALLENGE',\n}\n\nexport interface qpqConsoleLog {\n t: string;\n a: any[];\n}\n\nexport interface StoryResult<TArgs extends Array<any>, TResult = any> {\n // Params to story\n input: TArgs;\n\n // Story Session Data\n session: StorySession;\n\n // History of actions\n history: ActionHistory[];\n\n // When the story started / finished\n startedAt: string;\n finishedAt: string;\n\n // correlationGuid from the calling story\n fromCorrelation?: string;\n\n // correlation for this story (correlates all actions together)\n correlation: string;\n\n // For logging, tags to help identify the story\n tags: string[];\n moduleName: string;\n\n // Result of the story result or error not both\n result?: TResult;\n error?: QPQError;\n\n // User specified runtime type\n runtimeType: QpqRuntimeType;\n\n // Impure Logs\n logs?: qpqConsoleLog[];\n\n // What was the function runtime that this was executed against\n qpqFunctionRuntimeInfo?: QpqFunctionRuntime;\n}\n\nexport type StoryResultMetadata = {\n correlation: string;\n fromCorrelation?: string;\n\n moduleName: string;\n runtimeType: QpqRuntimeType;\n\n startedAt: string;\n\n generic: string;\n\n error?: string;\n\n executionTimeMs: number;\n\n userInfo?: string;\n\n qpqFunctionRuntimeInfo?: QpqFunctionRuntime;\n};\n\nexport type StoryResultMetadataWithChildren = StoryResultMetadata & {\n children: StoryResultMetadataWithChildren[];\n};\n"]}
|
package/lib/esm/types/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from './EventMessage';
|
|
|
13
13
|
export * from './ExcludeKeys';
|
|
14
14
|
export * from './FullyQualifiedResource';
|
|
15
15
|
export * from './KeyOf';
|
|
16
|
+
export * from './KvsStreamEvent';
|
|
16
17
|
export * from './LogLevelEnum';
|
|
17
18
|
export * from './Lookup';
|
|
18
19
|
export * from './NotifyErrorQueueEvent';
|
package/lib/esm/types/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export * from './EventMessage';
|
|
|
13
13
|
export * from './ExcludeKeys';
|
|
14
14
|
export * from './FullyQualifiedResource';
|
|
15
15
|
export * from './KeyOf';
|
|
16
|
+
export * from './KvsStreamEvent';
|
|
16
17
|
export * from './LogLevelEnum';
|
|
17
18
|
export * from './Lookup';
|
|
18
19
|
export * from './NotifyErrorQueueEvent';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,0BAA0B,CAAC;AACzC,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,SAAS,CAAC","sourcesContent":["export * from './Action';\nexport * from './authChallenge';\nexport * from './BoundLogicStory';\nexport * from './ConfigUrlDefinition';\nexport * from './CrossModuleMessage';\nexport * from './CrossModuleOwner';\nexport * from './CrossServiceResourceName';\nexport * from './DecomposedString';\nexport * from './DeployEvent';\nexport * from './DynamicModuleLoader';\nexport * from './ErrorTypeEnum';\nexport * from './EventMessage';\nexport * from './ExcludeKeys';\nexport * from './FullyQualifiedResource';\nexport * from './KeyOf';\nexport * from './LogLevelEnum';\nexport * from './Lookup';\nexport * from './NotifyErrorQueueEvent';\nexport * from './Nullable';\nexport * from './QPQBinaryData';\nexport * from './QpqContextIdentifier';\nexport * from './QpqExecutionTrace';\nexport * from './QpqFunctionRuntime';\nexport * from './QpqIsoDateTime';\nexport * from './QpqLogger';\nexport * from './QpqPagedData';\nexport * from './QueueEvent';\nexport * from './Readonlyify';\nexport * from './ScheduledEvent';\nexport * from './StorySession';\nexport * from './StreamRegistry';\nexport * from './UnboundLogicStory';\nexport * from './UnionToIntersection';\nexport * from './utils';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,0BAA0B,CAAC;AACzC,cAAc,SAAS,CAAC;AACxB,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,SAAS,CAAC","sourcesContent":["export * from './Action';\nexport * from './authChallenge';\nexport * from './BoundLogicStory';\nexport * from './ConfigUrlDefinition';\nexport * from './CrossModuleMessage';\nexport * from './CrossModuleOwner';\nexport * from './CrossServiceResourceName';\nexport * from './DecomposedString';\nexport * from './DeployEvent';\nexport * from './DynamicModuleLoader';\nexport * from './ErrorTypeEnum';\nexport * from './EventMessage';\nexport * from './ExcludeKeys';\nexport * from './FullyQualifiedResource';\nexport * from './KeyOf';\nexport * from './KvsStreamEvent';\nexport * from './LogLevelEnum';\nexport * from './Lookup';\nexport * from './NotifyErrorQueueEvent';\nexport * from './Nullable';\nexport * from './QPQBinaryData';\nexport * from './QpqContextIdentifier';\nexport * from './QpqExecutionTrace';\nexport * from './QpqFunctionRuntime';\nexport * from './QpqIsoDateTime';\nexport * from './QpqLogger';\nexport * from './QpqPagedData';\nexport * from './QueueEvent';\nexport * from './Readonlyify';\nexport * from './ScheduledEvent';\nexport * from './StorySession';\nexport * from './StreamRegistry';\nexport * from './UnboundLogicStory';\nexport * from './UnionToIntersection';\nexport * from './utils';\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quidproquo-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./lib/commonjs/index.js",
|
|
6
6
|
"module": "./lib/esm/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"homepage": "https://github.com/joe-coady/quidproquo#readme",
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^24.2.1",
|
|
36
|
-
"quidproquo-tsconfig": "0.1.
|
|
36
|
+
"quidproquo-tsconfig": "0.1.13",
|
|
37
37
|
"typescript": "^5.8.2"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|