quidproquo-core 0.1.25 → 0.1.26
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/platform/PlatformActionType.d.ts +2 -1
- package/lib/commonjs/actions/platform/PlatformActionType.js +1 -0
- package/lib/commonjs/actions/platform/PlatformActionType.js.map +1 -1
- package/lib/commonjs/actions/platform/askDelay.d.ts +1 -1
- package/lib/commonjs/actions/platform/askPlatformGetName.d.ts +8 -0
- package/lib/commonjs/actions/platform/askPlatformGetName.js +16 -0
- package/lib/commonjs/actions/platform/askPlatformGetName.js.map +1 -0
- package/lib/commonjs/actions/platform/index.d.ts +1 -0
- package/lib/commonjs/actions/platform/index.js +1 -0
- package/lib/commonjs/actions/platform/index.js.map +1 -1
- package/lib/commonjs/config/settings/keyValueStore.d.ts +4 -2
- package/lib/commonjs/config/settings/keyValueStore.js +2 -1
- package/lib/commonjs/config/settings/keyValueStore.js.map +1 -1
- package/lib/commonjs/config/settings/storageDrive.d.ts +4 -2
- package/lib/commonjs/config/settings/storageDrive.js +9 -3
- package/lib/commonjs/config/settings/storageDrive.js.map +1 -1
- package/lib/commonjs/logic/scope/InvalidScopeError.d.ts +3 -1
- package/lib/commonjs/logic/scope/InvalidScopeError.js +8 -0
- package/lib/commonjs/logic/scope/InvalidScopeError.js.map +1 -1
- package/lib/commonjs/logic/scope/fileScopeGate.d.ts +8 -0
- package/lib/commonjs/logic/scope/fileScopeGate.js +35 -0
- package/lib/commonjs/logic/scope/fileScopeGate.js.map +1 -0
- package/lib/commonjs/logic/scope/index.d.ts +2 -0
- package/lib/commonjs/logic/scope/index.js +2 -0
- package/lib/commonjs/logic/scope/index.js.map +1 -1
- package/lib/commonjs/logic/scope/kvsScopeGate.d.ts +1 -0
- package/lib/commonjs/logic/scope/kvsScopeGate.js +17 -1
- package/lib/commonjs/logic/scope/kvsScopeGate.js.map +1 -1
- package/lib/commonjs/logic/scope/splitScopedFilePath.d.ts +11 -0
- package/lib/commonjs/logic/scope/splitScopedFilePath.js +21 -0
- package/lib/commonjs/logic/scope/splitScopedFilePath.js.map +1 -0
- package/lib/commonjs/types/Brand.d.ts +40 -0
- package/lib/commonjs/types/Brand.js +3 -0
- package/lib/commonjs/types/Brand.js.map +1 -0
- package/lib/commonjs/types/index.d.ts +1 -0
- package/lib/commonjs/types/index.js +1 -0
- package/lib/commonjs/types/index.js.map +1 -1
- package/lib/commonjs/validators/createBrandGuard.d.ts +6 -0
- package/lib/commonjs/validators/createBrandGuard.js +11 -0
- package/lib/commonjs/validators/createBrandGuard.js.map +1 -0
- package/lib/commonjs/validators/index.d.ts +1 -0
- package/lib/commonjs/validators/index.js +1 -0
- package/lib/commonjs/validators/index.js.map +1 -1
- package/lib/esm/actions/platform/PlatformActionType.d.ts +2 -1
- package/lib/esm/actions/platform/PlatformActionType.js +1 -0
- package/lib/esm/actions/platform/PlatformActionType.js.map +1 -1
- package/lib/esm/actions/platform/askDelay.d.ts +1 -1
- package/lib/esm/actions/platform/askPlatformGetName.d.ts +8 -0
- package/lib/esm/actions/platform/askPlatformGetName.js +13 -0
- package/lib/esm/actions/platform/askPlatformGetName.js.map +1 -0
- package/lib/esm/actions/platform/index.d.ts +1 -0
- package/lib/esm/actions/platform/index.js +1 -0
- package/lib/esm/actions/platform/index.js.map +1 -1
- package/lib/esm/config/settings/keyValueStore.d.ts +4 -2
- package/lib/esm/config/settings/keyValueStore.js +2 -1
- package/lib/esm/config/settings/keyValueStore.js.map +1 -1
- package/lib/esm/config/settings/storageDrive.d.ts +4 -2
- package/lib/esm/config/settings/storageDrive.js +20 -12
- package/lib/esm/config/settings/storageDrive.js.map +1 -1
- package/lib/esm/logic/scope/InvalidScopeError.d.ts +3 -1
- package/lib/esm/logic/scope/InvalidScopeError.js +8 -0
- package/lib/esm/logic/scope/InvalidScopeError.js.map +1 -1
- package/lib/esm/logic/scope/fileScopeGate.d.ts +8 -0
- package/lib/esm/logic/scope/fileScopeGate.js +30 -0
- package/lib/esm/logic/scope/fileScopeGate.js.map +1 -0
- package/lib/esm/logic/scope/index.d.ts +2 -0
- package/lib/esm/logic/scope/index.js +2 -0
- package/lib/esm/logic/scope/index.js.map +1 -1
- package/lib/esm/logic/scope/kvsScopeGate.d.ts +1 -0
- package/lib/esm/logic/scope/kvsScopeGate.js +15 -0
- package/lib/esm/logic/scope/kvsScopeGate.js.map +1 -1
- package/lib/esm/logic/scope/splitScopedFilePath.d.ts +11 -0
- package/lib/esm/logic/scope/splitScopedFilePath.js +18 -0
- package/lib/esm/logic/scope/splitScopedFilePath.js.map +1 -0
- package/lib/esm/types/Brand.d.ts +40 -0
- package/lib/esm/types/Brand.js +2 -0
- package/lib/esm/types/Brand.js.map +1 -0
- package/lib/esm/types/index.d.ts +1 -0
- package/lib/esm/types/index.js +1 -0
- package/lib/esm/types/index.js.map +1 -1
- package/lib/esm/validators/createBrandGuard.d.ts +6 -0
- package/lib/esm/validators/createBrandGuard.js +8 -0
- package/lib/esm/validators/createBrandGuard.js.map +1 -0
- package/lib/esm/validators/index.d.ts +1 -0
- package/lib/esm/validators/index.js +1 -0
- package/lib/esm/validators/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -4,5 +4,6 @@ exports.PlatformActionType = void 0;
|
|
|
4
4
|
var PlatformActionType;
|
|
5
5
|
(function (PlatformActionType) {
|
|
6
6
|
PlatformActionType["Delay"] = "@quidproquo-core/Platform/Delay";
|
|
7
|
+
PlatformActionType["GetName"] = "@quidproquo-core/Platform/GetName";
|
|
7
8
|
})(PlatformActionType || (exports.PlatformActionType = PlatformActionType = {}));
|
|
8
9
|
//# sourceMappingURL=PlatformActionType.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlatformActionType.js","sourceRoot":"","sources":["../../../../src/actions/platform/PlatformActionType.ts"],"names":[],"mappings":";;;AAAA,IAAY,
|
|
1
|
+
{"version":3,"file":"PlatformActionType.js","sourceRoot":"","sources":["../../../../src/actions/platform/PlatformActionType.ts"],"names":[],"mappings":";;;AAAA,IAAY,kBAGX;AAHD,WAAY,kBAAkB;IAC5B,+DAAyC,CAAA;IACzC,mEAA6C,CAAA;AAC/C,CAAC,EAHW,kBAAkB,kCAAlB,kBAAkB,QAG7B","sourcesContent":["export enum PlatformActionType {\n Delay = '@quidproquo-core/Platform/Delay',\n GetName = '@quidproquo-core/Platform/GetName',\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { PlatformActionType } from './PlatformActionType';
|
|
2
|
-
export declare const askDelay: import("../../types").ActionRequesterFunction<PlatformActionType, [timeMs: number], {
|
|
2
|
+
export declare const askDelay: import("../../types").ActionRequesterFunction<PlatformActionType.Delay, [timeMs: number], {
|
|
3
3
|
timeMs: number;
|
|
4
4
|
}, void, never>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { PlatformActionType } from './PlatformActionType';
|
|
2
|
+
/**
|
|
3
|
+
* The name of the runtime executing the story, as its action processor set declares it.
|
|
4
|
+
* An opaque string a story compares against a known constant to skip or degrade work a
|
|
5
|
+
* platform cannot do (a smoke test with no local equivalent, say). Never branch on it
|
|
6
|
+
* for ordinary logic; that is what action processors are for.
|
|
7
|
+
*/
|
|
8
|
+
export declare const askPlatformGetName: import("../../types").ActionRequesterFunction<PlatformActionType.GetName, [], {}, string, never>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.askPlatformGetName = void 0;
|
|
4
|
+
const types_1 = require("../../types");
|
|
5
|
+
const PlatformActionType_1 = require("./PlatformActionType");
|
|
6
|
+
/**
|
|
7
|
+
* The name of the runtime executing the story, as its action processor set declares it.
|
|
8
|
+
* An opaque string a story compares against a known constant to skip or degrade work a
|
|
9
|
+
* platform cannot do (a smoke test with no local equivalent, say). Never branch on it
|
|
10
|
+
* for ordinary logic; that is what action processors are for.
|
|
11
|
+
*/
|
|
12
|
+
exports.askPlatformGetName = (0, types_1.createActionRequester)()({
|
|
13
|
+
actionType: PlatformActionType_1.PlatformActionType.GetName,
|
|
14
|
+
getPayload: () => ({}),
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=askPlatformGetName.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"askPlatformGetName.js","sourceRoot":"","sources":["../../../../src/actions/platform/askPlatformGetName.ts"],"names":[],"mappings":";;;AAAA,uCAAoD;AACpD,6DAA0D;AAE1D;;;;;GAKG;AACU,QAAA,kBAAkB,GAAG,IAAA,6BAAqB,GAAU,CAAC;IAChE,UAAU,EAAE,uCAAkB,CAAC,OAAO;IACtC,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;CACvB,CAAC,CAAC","sourcesContent":["import { createActionRequester } from '../../types';\nimport { PlatformActionType } from './PlatformActionType';\n\n/**\n * The name of the runtime executing the story, as its action processor set declares it.\n * An opaque string a story compares against a known constant to skip or degrade work a\n * platform cannot do (a smoke test with no local equivalent, say). Never branch on it\n * for ordinary logic; that is what action processors are for.\n */\nexport const askPlatformGetName = createActionRequester<string>()({\n actionType: PlatformActionType.GetName,\n getPayload: () => ({}),\n});\n"]}
|
|
@@ -15,5 +15,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./askDelay"), exports);
|
|
18
|
+
__exportStar(require("./askPlatformGetName"), exports);
|
|
18
19
|
__exportStar(require("./PlatformActionType"), exports);
|
|
19
20
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/actions/platform/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B;AAC3B,uDAAqC","sourcesContent":["export * from './askDelay';\nexport * from './PlatformActionType';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/actions/platform/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B;AAC3B,uDAAqC;AACrC,uDAAqC","sourcesContent":["export * from './askDelay';\nexport * from './askPlatformGetName';\nexport * from './PlatformActionType';\n"]}
|
|
@@ -57,7 +57,8 @@ export interface QPQConfigAdvancedKeyValueStoreSettings<T extends object = any>
|
|
|
57
57
|
owner?: CrossModuleOwner;
|
|
58
58
|
ttlAttribute?: string;
|
|
59
59
|
disablePointInTimeRecovery?: boolean;
|
|
60
|
-
|
|
60
|
+
cryptoKeyName?: string;
|
|
61
|
+
scoped?: boolean;
|
|
61
62
|
onStream?: KvsStreamSettings;
|
|
62
63
|
}
|
|
63
64
|
export interface KeyValueStoreQPQConfigSetting<T extends object = any> extends QPQConfigSetting {
|
|
@@ -68,7 +69,8 @@ export interface KeyValueStoreQPQConfigSetting<T extends object = any> extends Q
|
|
|
68
69
|
global: boolean;
|
|
69
70
|
ttlAttribute?: string;
|
|
70
71
|
disablePointInTimeRecovery: boolean;
|
|
71
|
-
|
|
72
|
+
cryptoKeyName?: string;
|
|
73
|
+
scoped: boolean;
|
|
72
74
|
onStream?: KvsStreamSettings;
|
|
73
75
|
}
|
|
74
76
|
export declare const defineKeyValueStore: <T extends object = any>(keyValueStoreName: string, partitionKey: CompositeKvsKey<T>, sortKeys?: CompositeKvsKey<T>[], options?: QPQConfigAdvancedKeyValueStoreSettings<T>) => KeyValueStoreQPQConfigSetting<T>;
|
|
@@ -42,7 +42,8 @@ const defineKeyValueStore = (keyValueStoreName, partitionKey, sortKeys = [], opt
|
|
|
42
42
|
owner: options === null || options === void 0 ? void 0 : options.owner,
|
|
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
|
+
cryptoKeyName: options === null || options === void 0 ? void 0 : options.cryptoKeyName,
|
|
46
|
+
scoped: (_d = options === null || options === void 0 ? void 0 : options.scoped) !== null && _d !== void 0 ? _d : false,
|
|
46
47
|
onStream: options === null || options === void 0 ? void 0 : options.onStream,
|
|
47
48
|
});
|
|
48
49
|
};
|
|
@@ -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;AAuFK,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,aAAa,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,aAAa;QACrC,MAAM,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,mCAAI,KAAK;QAEhC,QAAQ,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAQ;KAC5B,CAAC,CAAA;CAAA,CAAC;AA9BU,QAAA,mBAAmB,uBA8B7B","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 // Encrypt the table with the named defineCryptoKey instead of the provider's own managed\n // encryption, which always applies. The key must be declared in the same config, owned or\n // foreign; a foreign key's owning service must be deployed first.\n cryptoKeyName?: string;\n\n // Every action on this store must carry a scope; an unscoped call is refused rather\n // than hitting the unpartitioned rows. askKeyValueStoreScanAllScopes is exempt.\n scoped?: 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 cryptoKeyName?: string;\n scoped: 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 cryptoKeyName: options?.cryptoKeyName,\n scoped: options?.scoped ?? false,\n\n onStream: options?.onStream,\n});\n"]}
|
|
@@ -77,7 +77,8 @@ export interface QPQConfigAdvancedStorageDriveSettings extends QPQConfigAdvanced
|
|
|
77
77
|
global?: boolean;
|
|
78
78
|
onEvent?: StorageDriveEvents;
|
|
79
79
|
lifecycleRules?: StorageDriveLifecycleRule[];
|
|
80
|
-
|
|
80
|
+
cryptoKeyName?: string;
|
|
81
|
+
scoped?: boolean;
|
|
81
82
|
lockedDown?: boolean;
|
|
82
83
|
owner?: CrossModuleOwner<'storageDriveName'>;
|
|
83
84
|
}
|
|
@@ -87,7 +88,8 @@ export interface StorageDriveQPQConfigSetting extends QPQConfigSetting {
|
|
|
87
88
|
global: boolean;
|
|
88
89
|
onEvent?: StorageDriveEvents;
|
|
89
90
|
lifecycleRules?: StorageDriveLifecycleRule[];
|
|
90
|
-
|
|
91
|
+
cryptoKeyName?: string;
|
|
92
|
+
scoped: boolean;
|
|
91
93
|
lockedDown: boolean;
|
|
92
94
|
}
|
|
93
95
|
export declare const defineStorageDrive: (storageDrive: string, options?: QPQConfigAdvancedStorageDriveSettings) => StorageDriveQPQConfigSetting;
|
|
@@ -57,7 +57,12 @@ var StorageDriveTier;
|
|
|
57
57
|
})(StorageDriveTier || (exports.StorageDriveTier = StorageDriveTier = {}));
|
|
58
58
|
const defineStorageDrive = (storageDrive, options) => {
|
|
59
59
|
var _a, _b, _c;
|
|
60
|
-
|
|
60
|
+
// copyPath content is deployed straight to the bucket root, outside every
|
|
61
|
+
// scope, so it can never coexist with a scoped drive.
|
|
62
|
+
if ((options === null || options === void 0 ? void 0 : options.scoped) && options.copyPath) {
|
|
63
|
+
throw new Error(`Storage drive '${storageDrive}' cannot be both scoped and have a copyPath`);
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
61
66
|
configSettingType: QPQConfig_1.QPQCoreConfigSettingType.storageDrive,
|
|
62
67
|
uniqueKey: storageDrive,
|
|
63
68
|
storageDrive,
|
|
@@ -65,10 +70,11 @@ const defineStorageDrive = (storageDrive, options) => {
|
|
|
65
70
|
global: (_a = options === null || options === void 0 ? void 0 : options.global) !== null && _a !== void 0 ? _a : false,
|
|
66
71
|
onEvent: options === null || options === void 0 ? void 0 : options.onEvent,
|
|
67
72
|
lifecycleRules: options === null || options === void 0 ? void 0 : options.lifecycleRules,
|
|
68
|
-
|
|
73
|
+
cryptoKeyName: options === null || options === void 0 ? void 0 : options.cryptoKeyName,
|
|
74
|
+
scoped: (_b = options === null || options === void 0 ? void 0 : options.scoped) !== null && _b !== void 0 ? _b : false,
|
|
69
75
|
lockedDown: (_c = options === null || options === void 0 ? void 0 : options.lockedDown) !== null && _c !== void 0 ? _c : false,
|
|
70
76
|
owner: (0, crossModuleUtils_1.convertCrossModuleOwnerToGenericResourceNameOverride)(options === null || options === void 0 ? void 0 : options.owner),
|
|
71
|
-
}
|
|
77
|
+
};
|
|
72
78
|
};
|
|
73
79
|
exports.defineStorageDrive = defineStorageDrive;
|
|
74
80
|
//# sourceMappingURL=storageDrive.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storageDrive.js","sourceRoot":"","sources":["../../../../src/config/settings/storageDrive.ts"],"names":[],"mappings":";;;AACA,4CAAqG;AACrG,gEAAiG;AAEjG;;GAEG;AACH,IAAY,gBAqDX;AArDD,WAAY,gBAAgB;IAC1B;;;OAGG;IACH,uCAAmB,CAAA;IAEnB;;;;;OAKG;IACH,2DAAuC,CAAA;IAEvC;;;;OAIG;IACH,mFAA+D,CAAA;IAE/D;;;;;OAKG;IACH,iDAA6B,CAAA;IAE7B;;;;;OAKG;IACH,+EAA2D,CAAA;IAE3D;;;;;OAKG;IACH,2DAAuC,CAAA;IAEvC;;;;;OAKG;IACH,mDAA+B,CAAA;AACjC,CAAC,EArDW,gBAAgB,gCAAhB,gBAAgB,QAqD3B;
|
|
1
|
+
{"version":3,"file":"storageDrive.js","sourceRoot":"","sources":["../../../../src/config/settings/storageDrive.ts"],"names":[],"mappings":";;;AACA,4CAAqG;AACrG,gEAAiG;AAEjG;;GAEG;AACH,IAAY,gBAqDX;AArDD,WAAY,gBAAgB;IAC1B;;;OAGG;IACH,uCAAmB,CAAA;IAEnB;;;;;OAKG;IACH,2DAAuC,CAAA;IAEvC;;;;OAIG;IACH,mFAA+D,CAAA;IAE/D;;;;;OAKG;IACH,iDAA6B,CAAA;IAE7B;;;;;OAKG;IACH,+EAA2D,CAAA;IAE3D;;;;;OAKG;IACH,2DAAuC,CAAA;IAEvC;;;;;OAKG;IACH,mDAA+B,CAAA;AACjC,CAAC,EArDW,gBAAgB,gCAAhB,gBAAgB,QAqD3B;AAoEM,MAAM,kBAAkB,GAAG,CAAC,YAAoB,EAAE,OAA+C,EAAgC,EAAE;;IACxI,0EAA0E;IAC1E,sDAAsD;IACtD,IAAI,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,KAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,kBAAkB,YAAY,6CAA6C,CAAC,CAAC;IAC/F,CAAC;IAED,OAAO;QACL,iBAAiB,EAAE,oCAAwB,CAAC,YAAY;QACxD,SAAS,EAAE,YAAY;QAEvB,YAAY;QAEZ,QAAQ,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAQ;QAE3B,MAAM,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,mCAAI,KAAK;QAEhC,OAAO,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO;QAEzB,cAAc,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,cAAc;QAEvC,aAAa,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,aAAa;QACrC,MAAM,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,mCAAI,KAAK;QAChC,UAAU,EAAE,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,UAAU,mCAAI,KAAK;QAExC,KAAK,EAAE,IAAA,uEAAoD,EAAC,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,KAAK,CAAC;KAC5E,CAAC;AACJ,CAAC,CAAC;AA3BW,QAAA,kBAAkB,sBA2B7B","sourcesContent":["import { CrossModuleOwner, QpqFunctionRuntime } from '../../types';\nimport { QPQConfigAdvancedSettings, QPQConfigSetting, QPQCoreConfigSettingType } from '../QPQConfig';\nimport { convertCrossModuleOwnerToGenericResourceNameOverride } from '../utils/crossModuleUtils';\n\n/**\n * Represents different storage tiers for a \"storageDrive\".\n */\nexport enum StorageDriveTier {\n /**\n * Standard storage tier suitable for frequently accessed data.\n * (Reference: AWS S3 Standard)\n */\n REGULAR = 'REGULAR',\n\n /**\n * Storage tier optimized for data that is accessed less frequently,\n * but requires rapid access when needed. Lower availability compared\n * to the REGULAR tier.\n * (Reference: AWS S3 Infrequent Access)\n */\n OCCASIONAL_ACCESS = 'OCCASIONAL_ACCESS',\n\n /**\n * Optimized for infrequently accessed data stored in a single zone.\n * Lower availability compared to OCCASIONAL_ACCESS tier.\n * (Reference: AWS S3 One Zone-Infrequent Access)\n */\n SINGLE_ZONE_OCCASIONAL_ACCESS = 'SINGLE_ZONE_OCCASIONAL_ACCESS',\n\n /**\n * Cold storage tier suitable for long-term archival. Data retrieval\n * can take between minutes to hours. A cost-effective solution for\n * archival purposes where prompt data retrieval isn't crucial.\n * (Reference: AWS S3 Glacier)\n */\n COLD_STORAGE = 'COLD_STORAGE',\n\n /**\n * Cold storage tier optimized for long-term archival, but with the advantage\n * of immediate data access (in milliseconds). Ideal for data that might be\n * accessed once or twice per quarter.\n * (Reference: AWS S3 Glacier Instant Retrieval)\n */\n COLD_STORAGE_INSTANT_ACCESS = 'COLD_STORAGE_INSTANT_ACCESS',\n\n /**\n * Extremely cold storage suitable for data that's rarely accessed.\n * Data retrieval might take up to 12 hours. This offers the lowest\n * storage cost at the expense of retrieval time.\n * (Reference: AWS S3 Glacier Deep Archive)\n */\n DEEP_COLD_STORAGE = 'DEEP_COLD_STORAGE',\n\n /**\n * Dynamic storage tier designed to auto-optimize costs by moving data\n * between frequently and infrequently accessed tiers based on access patterns.\n * Ideal for long-lived data with unpredictable access patterns.\n * (Reference: AWS S3 Intelligent Tiering)\n */\n SMART_TIERING = 'SMART_TIERING',\n}\n\nexport type StorageDriveTransition = { storageDriveTier: StorageDriveTier } & (\n | {\n transitionAfterDays: number;\n transitionDate?: never;\n }\n | {\n transitionAfterDays?: never;\n transitionDate: string;\n }\n);\n\nexport type StorageDriveLifecycleRule = {\n prefix?: string;\n transitions?: StorageDriveTransition[];\n deleteAfterDays?: number;\n fileSizeLessThan?: number;\n fileSizeGreaterThan?: number;\n enabled?: boolean;\n\n // TODO: If we ever add versions\n // add noncurrentVersionExpiration / noncurrentVersionTransitions support\n};\n\nexport interface StorageDriveEvents {\n create?: QpqFunctionRuntime;\n delete?: QpqFunctionRuntime;\n}\n\nexport interface QPQConfigAdvancedStorageDriveSettings extends QPQConfigAdvancedSettings {\n copyPath?: string;\n global?: boolean;\n\n onEvent?: StorageDriveEvents;\n\n lifecycleRules?: StorageDriveLifecycleRule[];\n\n // Encrypt the drive with the named defineCryptoKey instead of the provider's own managed\n // encryption, which always applies. The key must be declared in the same config, owned or\n // foreign; a foreign key's owning service must be deployed first.\n cryptoKeyName?: string;\n\n // Every file action on this drive must carry a scope; an unscoped call is refused\n // rather than written to the bare path. Incompatible with copyPath.\n scoped?: boolean;\n\n // Only the owning service's runtime role may read objects. Every other principal in the\n // account, humans included, is denied at the bucket; writes and listing are unaffected.\n lockedDown?: boolean;\n\n owner?: CrossModuleOwner<'storageDriveName'>;\n}\n\nexport interface StorageDriveQPQConfigSetting extends QPQConfigSetting {\n storageDrive: string;\n copyPath?: string;\n global: boolean;\n\n onEvent?: StorageDriveEvents;\n\n lifecycleRules?: StorageDriveLifecycleRule[];\n\n cryptoKeyName?: string;\n scoped: boolean;\n lockedDown: boolean;\n}\n\nexport const defineStorageDrive = (storageDrive: string, options?: QPQConfigAdvancedStorageDriveSettings): StorageDriveQPQConfigSetting => {\n // copyPath content is deployed straight to the bucket root, outside every\n // scope, so it can never coexist with a scoped drive.\n if (options?.scoped && options.copyPath) {\n throw new Error(`Storage drive '${storageDrive}' cannot be both scoped and have a copyPath`);\n }\n\n return {\n configSettingType: QPQCoreConfigSettingType.storageDrive,\n uniqueKey: storageDrive,\n\n storageDrive,\n\n copyPath: options?.copyPath,\n\n global: options?.global ?? false,\n\n onEvent: options?.onEvent,\n\n lifecycleRules: options?.lifecycleRules,\n\n cryptoKeyName: options?.cryptoKeyName,\n scoped: options?.scoped ?? false,\n lockedDown: options?.lockedDown ?? false,\n\n owner: convertCrossModuleOwnerToGenericResourceNameOverride(options?.owner),\n };\n};\n"]}
|
|
@@ -4,7 +4,9 @@ export declare enum InvalidScopeErrorCode {
|
|
|
4
4
|
tooLong = "tooLong",
|
|
5
5
|
unsafePath = "unsafePath",
|
|
6
6
|
queryMissingPartitionKey = "queryMissingPartitionKey",
|
|
7
|
-
reservedDelimiter = "reservedDelimiter"
|
|
7
|
+
reservedDelimiter = "reservedDelimiter",
|
|
8
|
+
scopeRequired = "scopeRequired",
|
|
9
|
+
notScoped = "notScoped"
|
|
8
10
|
}
|
|
9
11
|
export declare class InvalidScopeError extends Error {
|
|
10
12
|
readonly code: InvalidScopeErrorCode;
|
|
@@ -23,6 +23,14 @@ var InvalidScopeErrorCode;
|
|
|
23
23
|
// A raw partition-key value containing the scope delimiter: storing it would
|
|
24
24
|
// make the row indistinguishable from another scope's composed data.
|
|
25
25
|
InvalidScopeErrorCode["reservedDelimiter"] = "reservedDelimiter";
|
|
26
|
+
// The resource is declared `scoped` in config but the call carried no scope.
|
|
27
|
+
// On a drive that would land the object beside every tenant's prefix; on a
|
|
28
|
+
// store it would read or write the unpartitioned rows.
|
|
29
|
+
InvalidScopeErrorCode["scopeRequired"] = "scopeRequired";
|
|
30
|
+
// The call carried a scope but the resource is not declared `scoped` in
|
|
31
|
+
// config: the data would land under a prefix nothing else on that resource
|
|
32
|
+
// expects, invisible to every unscoped reader.
|
|
33
|
+
InvalidScopeErrorCode["notScoped"] = "notScoped";
|
|
26
34
|
})(InvalidScopeErrorCode || (exports.InvalidScopeErrorCode = InvalidScopeErrorCode = {}));
|
|
27
35
|
class InvalidScopeError extends Error {
|
|
28
36
|
constructor(code, message) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InvalidScopeError.js","sourceRoot":"","sources":["../../../../src/logic/scope/InvalidScopeError.ts"],"names":[],"mappings":";;;AAAA,8EAA8E;AAC9E,4EAA4E;AAC5E,uEAAuE;AACvE,6EAA6E;AAC7E,mBAAmB;AACnB,IAAY,
|
|
1
|
+
{"version":3,"file":"InvalidScopeError.js","sourceRoot":"","sources":["../../../../src/logic/scope/InvalidScopeError.ts"],"names":[],"mappings":";;;AAAA,8EAA8E;AAC9E,4EAA4E;AAC5E,uEAAuE;AACvE,6EAA6E;AAC7E,mBAAmB;AACnB,IAAY,qBAwBX;AAxBD,WAAY,qBAAqB;IAC/B,kCAAkC;IAClC,wCAAe,CAAA;IACf,mDAAmD;IACnD,8DAAqC,CAAA;IACrC,kEAAkE;IAClE,4CAAmB,CAAA;IACnB,uEAAuE;IACvE,yCAAyC;IACzC,kDAAyB,CAAA;IACzB,0EAA0E;IAC1E,0EAA0E;IAC1E,8EAAqD,CAAA;IACrD,6EAA6E;IAC7E,qEAAqE;IACrE,gEAAuC,CAAA;IACvC,6EAA6E;IAC7E,2EAA2E;IAC3E,uDAAuD;IACvD,wDAA+B,CAAA;IAC/B,wEAAwE;IACxE,2EAA2E;IAC3E,+CAA+C;IAC/C,gDAAuB,CAAA;AACzB,CAAC,EAxBW,qBAAqB,qCAArB,qBAAqB,QAwBhC;AAED,MAAa,iBAAkB,SAAQ,KAAK;IAC1C,YACkB,IAA2B,EAC3C,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,SAAI,GAAJ,IAAI,CAAuB;QAI3C,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AARD,8CAQC","sourcesContent":["// Raised when a scope segment fails validation. Scope is an opaque identifier\n// (supplied by a higher-level data-partitioning feature) that gets embedded\n// into file paths and kvs partition keys, so a malformed one is always\n// rejected outright - never normalized - to make escaping a scope impossible\n// by construction.\nexport enum InvalidScopeErrorCode {\n // Empty or whitespace-only scope.\n empty = 'empty',\n // Contains a path separator, '..', or a null byte.\n unsafeCharacters = 'unsafeCharacters',\n // Longer than the allowed maximum (scopes are ids, not payloads).\n tooLong = 'tooLong',\n // A filepath composed under a scope could traverse out of it (absolute\n // path, a '..' segment, or a null byte).\n unsafePath = 'unsafePath',\n // A scoped query whose key condition never constrains the partition key -\n // on value-composed backends (dynamo) it would silently span every scope.\n queryMissingPartitionKey = 'queryMissingPartitionKey',\n // A raw partition-key value containing the scope delimiter: storing it would\n // make the row indistinguishable from another scope's composed data.\n reservedDelimiter = 'reservedDelimiter',\n // The resource is declared `scoped` in config but the call carried no scope.\n // On a drive that would land the object beside every tenant's prefix; on a\n // store it would read or write the unpartitioned rows.\n scopeRequired = 'scopeRequired',\n // The call carried a scope but the resource is not declared `scoped` in\n // config: the data would land under a prefix nothing else on that resource\n // expects, invisible to every unscoped reader.\n notScoped = 'notScoped',\n}\n\nexport class InvalidScopeError extends Error {\n constructor(\n public readonly code: InvalidScopeErrorCode,\n message: string,\n ) {\n super(message);\n this.name = 'InvalidScopeError';\n }\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { QPQConfig } from '../../config';
|
|
2
|
+
export declare const assertStorageDriveScopeRequirementOrThrow: (qpqConfig: QPQConfig, drive: string, scope: string | undefined) => void;
|
|
3
|
+
/**
|
|
4
|
+
* Gate then compose: the path a key-based backend (s3) stores under. Throws
|
|
5
|
+
* InvalidScopeError when the drive is scoped and no scope was given, or when
|
|
6
|
+
* the scope or filepath fails validation.
|
|
7
|
+
*/
|
|
8
|
+
export declare const composeStorageDriveFilePathOrThrow: (qpqConfig: QPQConfig, drive: string, scope: string | undefined, filepath: string) => string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.composeStorageDriveFilePathOrThrow = exports.assertStorageDriveScopeRequirementOrThrow = void 0;
|
|
4
|
+
const qpqCoreUtils_1 = require("../../qpqCoreUtils");
|
|
5
|
+
const composeScopedFilePath_1 = require("./composeScopedFilePath");
|
|
6
|
+
const InvalidScopeError_1 = require("./InvalidScopeError");
|
|
7
|
+
// THE shared scope gate for every file backend (s3, node fs, ...). The drive's
|
|
8
|
+
// `scoped` flag and the call's scope must agree: a scoped drive refuses a call
|
|
9
|
+
// without one, an unscoped drive refuses a call with one, so a mismatch is an
|
|
10
|
+
// error rather than a different path. An unknown drive is left for the backend
|
|
11
|
+
// to report as its own DriveNotFound.
|
|
12
|
+
const assertStorageDriveScopeRequirementOrThrow = (qpqConfig, drive, scope) => {
|
|
13
|
+
const driveConfig = (0, qpqCoreUtils_1.getStorageDriveByName)(drive, qpqConfig);
|
|
14
|
+
if (!driveConfig) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (driveConfig.scoped && scope === undefined) {
|
|
18
|
+
throw new InvalidScopeError_1.InvalidScopeError(InvalidScopeError_1.InvalidScopeErrorCode.scopeRequired, `Storage drive '${drive}' is scoped; every file action must carry a scope.`);
|
|
19
|
+
}
|
|
20
|
+
if (!driveConfig.scoped && scope !== undefined) {
|
|
21
|
+
throw new InvalidScopeError_1.InvalidScopeError(InvalidScopeError_1.InvalidScopeErrorCode.notScoped, `Storage drive '${drive}' is not scoped; declare it scoped or drop the scope.`);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
exports.assertStorageDriveScopeRequirementOrThrow = assertStorageDriveScopeRequirementOrThrow;
|
|
25
|
+
/**
|
|
26
|
+
* Gate then compose: the path a key-based backend (s3) stores under. Throws
|
|
27
|
+
* InvalidScopeError when the drive is scoped and no scope was given, or when
|
|
28
|
+
* the scope or filepath fails validation.
|
|
29
|
+
*/
|
|
30
|
+
const composeStorageDriveFilePathOrThrow = (qpqConfig, drive, scope, filepath) => {
|
|
31
|
+
(0, exports.assertStorageDriveScopeRequirementOrThrow)(qpqConfig, drive, scope);
|
|
32
|
+
return (0, composeScopedFilePath_1.composeScopedFilePath)(scope, filepath);
|
|
33
|
+
};
|
|
34
|
+
exports.composeStorageDriveFilePathOrThrow = composeStorageDriveFilePathOrThrow;
|
|
35
|
+
//# sourceMappingURL=fileScopeGate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileScopeGate.js","sourceRoot":"","sources":["../../../../src/logic/scope/fileScopeGate.ts"],"names":[],"mappings":";;;AACA,qDAA2D;AAC3D,mEAAgE;AAChE,2DAA+E;AAE/E,+EAA+E;AAC/E,+EAA+E;AAC/E,8EAA8E;AAC9E,+EAA+E;AAC/E,sCAAsC;AAC/B,MAAM,yCAAyC,GAAG,CAAC,SAAoB,EAAE,KAAa,EAAE,KAAyB,EAAQ,EAAE;IAChI,MAAM,WAAW,GAAG,IAAA,oCAAqB,EAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAE5D,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO;IACT,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC9C,MAAM,IAAI,qCAAiB,CAAC,yCAAqB,CAAC,aAAa,EAAE,kBAAkB,KAAK,oDAAoD,CAAC,CAAC;IAChJ,CAAC;IAED,IAAI,CAAC,WAAW,CAAC,MAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/C,MAAM,IAAI,qCAAiB,CAAC,yCAAqB,CAAC,SAAS,EAAE,kBAAkB,KAAK,uDAAuD,CAAC,CAAC;IAC/I,CAAC;AACH,CAAC,CAAC;AAdW,QAAA,yCAAyC,6CAcpD;AAEF;;;;GAIG;AACI,MAAM,kCAAkC,GAAG,CAAC,SAAoB,EAAE,KAAa,EAAE,KAAyB,EAAE,QAAgB,EAAU,EAAE;IAC7I,IAAA,iDAAyC,EAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAEnE,OAAO,IAAA,6CAAqB,EAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAChD,CAAC,CAAC;AAJW,QAAA,kCAAkC,sCAI7C","sourcesContent":["import { QPQConfig } from '../../config';\nimport { getStorageDriveByName } from '../../qpqCoreUtils';\nimport { composeScopedFilePath } from './composeScopedFilePath';\nimport { InvalidScopeError, InvalidScopeErrorCode } from './InvalidScopeError';\n\n// THE shared scope gate for every file backend (s3, node fs, ...). The drive's\n// `scoped` flag and the call's scope must agree: a scoped drive refuses a call\n// without one, an unscoped drive refuses a call with one, so a mismatch is an\n// error rather than a different path. An unknown drive is left for the backend\n// to report as its own DriveNotFound.\nexport const assertStorageDriveScopeRequirementOrThrow = (qpqConfig: QPQConfig, drive: string, scope: string | undefined): void => {\n const driveConfig = getStorageDriveByName(drive, qpqConfig);\n\n if (!driveConfig) {\n return;\n }\n\n if (driveConfig.scoped && scope === undefined) {\n throw new InvalidScopeError(InvalidScopeErrorCode.scopeRequired, `Storage drive '${drive}' is scoped; every file action must carry a scope.`);\n }\n\n if (!driveConfig.scoped && scope !== undefined) {\n throw new InvalidScopeError(InvalidScopeErrorCode.notScoped, `Storage drive '${drive}' is not scoped; declare it scoped or drop the scope.`);\n }\n};\n\n/**\n * Gate then compose: the path a key-based backend (s3) stores under. Throws\n * InvalidScopeError when the drive is scoped and no scope was given, or when\n * the scope or filepath fails validation.\n */\nexport const composeStorageDriveFilePathOrThrow = (qpqConfig: QPQConfig, drive: string, scope: string | undefined, filepath: string): string => {\n assertStorageDriveScopeRequirementOrThrow(qpqConfig, drive, scope);\n\n return composeScopedFilePath(scope, filepath);\n};\n"]}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export * from './composeScopedFilePath';
|
|
2
|
+
export * from './fileScopeGate';
|
|
2
3
|
export * from './InvalidScopeError';
|
|
3
4
|
export * from './kvsScopeGate';
|
|
4
5
|
export * from './KvsStoreNotFoundError';
|
|
5
6
|
export * from './scopedKvsQueryOperation';
|
|
6
7
|
export * from './scopedKvsTranslator';
|
|
7
8
|
export * from './scopedKvsValue';
|
|
9
|
+
export * from './splitScopedFilePath';
|
|
8
10
|
export * from './storageScopeContext';
|
|
9
11
|
export * from './stripScopedFilePath';
|
|
10
12
|
export * from './validateScopeSegment';
|
|
@@ -15,12 +15,14 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./composeScopedFilePath"), exports);
|
|
18
|
+
__exportStar(require("./fileScopeGate"), exports);
|
|
18
19
|
__exportStar(require("./InvalidScopeError"), exports);
|
|
19
20
|
__exportStar(require("./kvsScopeGate"), exports);
|
|
20
21
|
__exportStar(require("./KvsStoreNotFoundError"), exports);
|
|
21
22
|
__exportStar(require("./scopedKvsQueryOperation"), exports);
|
|
22
23
|
__exportStar(require("./scopedKvsTranslator"), exports);
|
|
23
24
|
__exportStar(require("./scopedKvsValue"), exports);
|
|
25
|
+
__exportStar(require("./splitScopedFilePath"), exports);
|
|
24
26
|
__exportStar(require("./storageScopeContext"), exports);
|
|
25
27
|
__exportStar(require("./stripScopedFilePath"), exports);
|
|
26
28
|
__exportStar(require("./validateScopeSegment"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/logic/scope/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC;AACxC,sDAAoC;AACpC,iDAA+B;AAC/B,0DAAwC;AACxC,4DAA0C;AAC1C,wDAAsC;AACtC,mDAAiC;AACjC,wDAAsC;AACtC,wDAAsC;AACtC,yDAAuC","sourcesContent":["export * from './composeScopedFilePath';\nexport * from './InvalidScopeError';\nexport * from './kvsScopeGate';\nexport * from './KvsStoreNotFoundError';\nexport * from './scopedKvsQueryOperation';\nexport * from './scopedKvsTranslator';\nexport * from './scopedKvsValue';\nexport * from './storageScopeContext';\nexport * from './stripScopedFilePath';\nexport * from './validateScopeSegment';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/logic/scope/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC;AACxC,kDAAgC;AAChC,sDAAoC;AACpC,iDAA+B;AAC/B,0DAAwC;AACxC,4DAA0C;AAC1C,wDAAsC;AACtC,mDAAiC;AACjC,wDAAsC;AACtC,wDAAsC;AACtC,wDAAsC;AACtC,yDAAuC","sourcesContent":["export * from './composeScopedFilePath';\nexport * from './fileScopeGate';\nexport * from './InvalidScopeError';\nexport * from './kvsScopeGate';\nexport * from './KvsStoreNotFoundError';\nexport * from './scopedKvsQueryOperation';\nexport * from './scopedKvsTranslator';\nexport * from './scopedKvsValue';\nexport * from './splitScopedFilePath';\nexport * from './storageScopeContext';\nexport * from './stripScopedFilePath';\nexport * from './validateScopeSegment';\n"]}
|
|
@@ -2,6 +2,7 @@ import { KvsCoreDataType, KvsQueryOperation } from '../../actions/keyValueStore/
|
|
|
2
2
|
import { KeyValueStoreQPQConfigSetting, QPQConfig } from '../../config';
|
|
3
3
|
import { ScopedKvsTranslator } from './scopedKvsTranslator';
|
|
4
4
|
export declare const resolveKvsStoreConfigOrThrow: (qpqConfig: QPQConfig, keyValueStoreName: string) => KeyValueStoreQPQConfigSetting;
|
|
5
|
+
export declare const assertKvsScopeRequirementOrThrow: (qpqConfig: QPQConfig, keyValueStoreName: string, scope: string | undefined) => void;
|
|
5
6
|
export declare const getScopedKvsTranslatorOrThrow: (qpqConfig: QPQConfig, keyValueStoreName: string, scope: string | undefined) => ScopedKvsTranslator;
|
|
6
7
|
export declare const resolveScopedPkAttributeOrThrow: (qpqConfig: QPQConfig, keyValueStoreName: string, scope: string) => string;
|
|
7
8
|
export declare const validateScopedKvsKeyOrThrow: (qpqConfig: QPQConfig, keyValueStoreName: string, scope: string | undefined, key: KvsCoreDataType) => void;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validateScopedKvsKeyConditionOrThrow = exports.validateScopedKvsItemOrThrow = exports.validateScopedKvsKeyOrThrow = exports.resolveScopedPkAttributeOrThrow = exports.getScopedKvsTranslatorOrThrow = exports.resolveKvsStoreConfigOrThrow = void 0;
|
|
3
|
+
exports.validateScopedKvsKeyConditionOrThrow = exports.validateScopedKvsItemOrThrow = exports.validateScopedKvsKeyOrThrow = exports.resolveScopedPkAttributeOrThrow = exports.getScopedKvsTranslatorOrThrow = exports.assertKvsScopeRequirementOrThrow = exports.resolveKvsStoreConfigOrThrow = void 0;
|
|
4
4
|
const qpqCoreUtils_1 = require("../../qpqCoreUtils");
|
|
5
|
+
const InvalidScopeError_1 = require("./InvalidScopeError");
|
|
5
6
|
const KvsStoreNotFoundError_1 = require("./KvsStoreNotFoundError");
|
|
6
7
|
const scopedKvsTranslator_1 = require("./scopedKvsTranslator");
|
|
7
8
|
const scopedKvsValue_1 = require("./scopedKvsValue");
|
|
@@ -19,6 +20,20 @@ const resolveKvsStoreConfigOrThrow = (qpqConfig, keyValueStoreName) => {
|
|
|
19
20
|
return storeConfig;
|
|
20
21
|
};
|
|
21
22
|
exports.resolveKvsStoreConfigOrThrow = resolveKvsStoreConfigOrThrow;
|
|
23
|
+
// Every backend calls this on every call, scoped or not: the store's `scoped`
|
|
24
|
+
// flag and the call's scope must agree, so a scoped store can never be reached
|
|
25
|
+
// without one and an unscoped store never gains a hidden partition.
|
|
26
|
+
// Scan-all-scopes is the one deliberate exception and does not go through here.
|
|
27
|
+
const assertKvsScopeRequirementOrThrow = (qpqConfig, keyValueStoreName, scope) => {
|
|
28
|
+
const storeConfig = (0, exports.resolveKvsStoreConfigOrThrow)(qpqConfig, keyValueStoreName);
|
|
29
|
+
if (storeConfig.scoped && scope === undefined) {
|
|
30
|
+
throw new InvalidScopeError_1.InvalidScopeError(InvalidScopeError_1.InvalidScopeErrorCode.scopeRequired, `Key value store '${keyValueStoreName}' is scoped; every call must carry a scope.`);
|
|
31
|
+
}
|
|
32
|
+
if (!storeConfig.scoped && scope !== undefined) {
|
|
33
|
+
throw new InvalidScopeError_1.InvalidScopeError(InvalidScopeError_1.InvalidScopeErrorCode.notScoped, `Key value store '${keyValueStoreName}' is not scoped; declare it scoped or drop the scope.`);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
exports.assertKvsScopeRequirementOrThrow = assertKvsScopeRequirementOrThrow;
|
|
22
37
|
// Value-composed backends (dynamo): validate and hand back the translator that
|
|
23
38
|
// knows how to scope every shape the pk appears in - a bare key, an item
|
|
24
39
|
// field, a query condition tree, a scan filter, and stripping results.
|
|
@@ -27,6 +42,7 @@ exports.resolveKvsStoreConfigOrThrow = resolveKvsStoreConfigOrThrow;
|
|
|
27
42
|
// scope.
|
|
28
43
|
const getScopedKvsTranslatorOrThrow = (qpqConfig, keyValueStoreName, scope) => {
|
|
29
44
|
const storeConfig = (0, exports.resolveKvsStoreConfigOrThrow)(qpqConfig, keyValueStoreName);
|
|
45
|
+
(0, exports.assertKvsScopeRequirementOrThrow)(qpqConfig, keyValueStoreName, scope);
|
|
30
46
|
if (scope === undefined) {
|
|
31
47
|
// Only a string pk can hold composed values, so only then does the
|
|
32
48
|
// unscoped scan exclusion apply.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kvsScopeGate.js","sourceRoot":"","sources":["../../../../src/logic/scope/kvsScopeGate.ts"],"names":[],"mappings":";;;AAEA,qDAA4D;AAC5D,mEAAgE;AAChE,+DAAuF;AACvF,qDAA6E;AAC7E,iEAA8D;AAE9D,8EAA8E;AAC9E,2EAA2E;AAC3E,kEAAkE;AAClE,iEAAiE;AAEjE,2EAA2E;AACpE,MAAM,4BAA4B,GAAG,CAAC,SAAoB,EAAE,iBAAyB,EAAiC,EAAE;IAC7H,MAAM,WAAW,GAAG,IAAA,qCAAsB,EAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;IACzE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,6CAAqB,CAAC,iBAAiB,CAAC,CAAC;IACrD,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC,CAAC;AAPW,QAAA,4BAA4B,gCAOvC;AAEF,+EAA+E;AAC/E,yEAAyE;AACzE,uEAAuE;AACvE,4EAA4E;AAC5E,6EAA6E;AAC7E,SAAS;AACF,MAAM,6BAA6B,GAAG,CAAC,SAAoB,EAAE,iBAAyB,EAAE,KAAyB,EAAuB,EAAE;IAC/I,MAAM,WAAW,GAAG,IAAA,oCAA4B,EAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"kvsScopeGate.js","sourceRoot":"","sources":["../../../../src/logic/scope/kvsScopeGate.ts"],"names":[],"mappings":";;;AAEA,qDAA4D;AAC5D,2DAA+E;AAC/E,mEAAgE;AAChE,+DAAuF;AACvF,qDAA6E;AAC7E,iEAA8D;AAE9D,8EAA8E;AAC9E,2EAA2E;AAC3E,kEAAkE;AAClE,iEAAiE;AAEjE,2EAA2E;AACpE,MAAM,4BAA4B,GAAG,CAAC,SAAoB,EAAE,iBAAyB,EAAiC,EAAE;IAC7H,MAAM,WAAW,GAAG,IAAA,qCAAsB,EAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;IACzE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,6CAAqB,CAAC,iBAAiB,CAAC,CAAC;IACrD,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC,CAAC;AAPW,QAAA,4BAA4B,gCAOvC;AAEF,8EAA8E;AAC9E,+EAA+E;AAC/E,oEAAoE;AACpE,gFAAgF;AACzE,MAAM,gCAAgC,GAAG,CAAC,SAAoB,EAAE,iBAAyB,EAAE,KAAyB,EAAQ,EAAE;IACnI,MAAM,WAAW,GAAG,IAAA,oCAA4B,EAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;IAE/E,IAAI,WAAW,CAAC,MAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC9C,MAAM,IAAI,qCAAiB,CACzB,yCAAqB,CAAC,aAAa,EACnC,oBAAoB,iBAAiB,6CAA6C,CACnF,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,WAAW,CAAC,MAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/C,MAAM,IAAI,qCAAiB,CACzB,yCAAqB,CAAC,SAAS,EAC/B,oBAAoB,iBAAiB,uDAAuD,CAC7F,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAhBW,QAAA,gCAAgC,oCAgB3C;AAEF,+EAA+E;AAC/E,yEAAyE;AACzE,uEAAuE;AACvE,4EAA4E;AAC5E,6EAA6E;AAC7E,SAAS;AACF,MAAM,6BAA6B,GAAG,CAAC,SAAoB,EAAE,iBAAyB,EAAE,KAAyB,EAAuB,EAAE;IAC/I,MAAM,WAAW,GAAG,IAAA,oCAA4B,EAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;IAC/E,IAAA,wCAAgC,EAAC,SAAS,EAAE,iBAAiB,EAAE,KAAK,CAAC,CAAC;IAEtE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,mEAAmE;QACnE,iCAAiC;QACjC,MAAM,iBAAiB,GAAG,WAAW,CAAC,YAAY,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACzG,OAAO,IAAA,+CAAyB,EAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;IACjE,CAAC;IAED,IAAA,2CAAoB,EAAC,KAAK,CAAC,CAAC;IAC5B,IAAA,0DAAyC,EAAC,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAEzE,OAAO,IAAA,+CAAyB,EAAC,KAAK,EAAE,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;AACxE,CAAC,CAAC;AAfW,QAAA,6BAA6B,iCAexC;AAEF,0EAA0E;AAC1E,6EAA6E;AAC7E,qCAAqC;AAC9B,MAAM,+BAA+B,GAAG,CAAC,SAAoB,EAAE,iBAAyB,EAAE,KAAa,EAAU,EAAE;IACxH,MAAM,WAAW,GAAG,IAAA,oCAA4B,EAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;IAE/E,IAAA,2CAAoB,EAAC,KAAK,CAAC,CAAC;IAC5B,IAAA,0DAAyC,EAAC,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAEzE,OAAO,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC;AACtC,CAAC,CAAC;AAPW,QAAA,+BAA+B,mCAO1C;AAEF,8EAA8E;AAC9E,kEAAkE;AAClE,4EAA4E;AAC5E,uEAAuE;AACvE,yDAAyD;AAElD,MAAM,2BAA2B,GAAG,CACzC,SAAoB,EACpB,iBAAyB,EACzB,KAAyB,EACzB,GAAoB,EACd,EAAE;IACR,IAAA,qCAA6B,EAAC,SAAS,EAAE,iBAAiB,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAC9E,CAAC,CAAC;AAPW,QAAA,2BAA2B,+BAOtC;AAEK,MAAM,4BAA4B,GAAG,CAC1C,SAAoB,EACpB,iBAAyB,EACzB,KAAyB,EACzB,IAAyB,EACnB,EAAE;IACR,IAAA,qCAA6B,EAAC,SAAS,EAAE,iBAAiB,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,EAAE,CAAC,CAAC;AACtF,CAAC,CAAC;AAPW,QAAA,4BAA4B,gCAOvC;AAEK,MAAM,oCAAoC,GAAG,CAClD,SAAoB,EACpB,iBAAyB,EACzB,KAAyB,EACzB,YAA+B,EACzB,EAAE;IACR,IAAA,qCAA6B,EAAC,SAAS,EAAE,iBAAiB,EAAE,KAAK,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;AAChG,CAAC,CAAC;AAPW,QAAA,oCAAoC,wCAO/C","sourcesContent":["import { KvsCoreDataType, KvsQueryOperation } from '../../actions/keyValueStore/types';\nimport { KeyValueStoreQPQConfigSetting, QPQConfig } from '../../config';\nimport { getKeyValueStoreByName } from '../../qpqCoreUtils';\nimport { InvalidScopeError, InvalidScopeErrorCode } from './InvalidScopeError';\nimport { KvsStoreNotFoundError } from './KvsStoreNotFoundError';\nimport { createScopedKvsTranslator, ScopedKvsTranslator } from './scopedKvsTranslator';\nimport { validateScopeSupportedForPartitionKeyType } from './scopedKvsValue';\nimport { validateScopeSegment } from './validateScopeSegment';\n\n// THE shared scope gate for every kvs backend (dynamo, dev-server json, ...):\n// resolve the store's config, validate the scope against it, and hand back\n// what the backend needs. Lives in core so the value-composed and\n// file-partitioned backends can never drift apart on validation.\n\n// Store lookup with the typed misconfiguration error (never a bare throw).\nexport const resolveKvsStoreConfigOrThrow = (qpqConfig: QPQConfig, keyValueStoreName: string): KeyValueStoreQPQConfigSetting => {\n const storeConfig = getKeyValueStoreByName(qpqConfig, keyValueStoreName);\n if (!storeConfig) {\n throw new KvsStoreNotFoundError(keyValueStoreName);\n }\n\n return storeConfig;\n};\n\n// Every backend calls this on every call, scoped or not: the store's `scoped`\n// flag and the call's scope must agree, so a scoped store can never be reached\n// without one and an unscoped store never gains a hidden partition.\n// Scan-all-scopes is the one deliberate exception and does not go through here.\nexport const assertKvsScopeRequirementOrThrow = (qpqConfig: QPQConfig, keyValueStoreName: string, scope: string | undefined): void => {\n const storeConfig = resolveKvsStoreConfigOrThrow(qpqConfig, keyValueStoreName);\n\n if (storeConfig.scoped && scope === undefined) {\n throw new InvalidScopeError(\n InvalidScopeErrorCode.scopeRequired,\n `Key value store '${keyValueStoreName}' is scoped; every call must carry a scope.`,\n );\n }\n\n if (!storeConfig.scoped && scope !== undefined) {\n throw new InvalidScopeError(\n InvalidScopeErrorCode.notScoped,\n `Key value store '${keyValueStoreName}' is not scoped; declare it scoped or drop the scope.`,\n );\n }\n};\n\n// Value-composed backends (dynamo): validate and hand back the translator that\n// knows how to scope every shape the pk appears in - a bare key, an item\n// field, a query condition tree, a scan filter, and stripping results.\n// Unscoped requests get the identity translator (plus the composed-row scan\n// exclusion when the store's string pk is known), so callers never branch on\n// scope.\nexport const getScopedKvsTranslatorOrThrow = (qpqConfig: QPQConfig, keyValueStoreName: string, scope: string | undefined): ScopedKvsTranslator => {\n const storeConfig = resolveKvsStoreConfigOrThrow(qpqConfig, keyValueStoreName);\n assertKvsScopeRequirementOrThrow(qpqConfig, keyValueStoreName, scope);\n\n if (scope === undefined) {\n // Only a string pk can hold composed values, so only then does the\n // unscoped scan exclusion apply.\n const stringPkAttribute = storeConfig.partitionKey.type === 'string' ? storeConfig.partitionKey.key : '';\n return createScopedKvsTranslator(undefined, stringPkAttribute);\n }\n\n validateScopeSegment(scope);\n validateScopeSupportedForPartitionKeyType(storeConfig.partitionKey.type);\n\n return createScopedKvsTranslator(scope, storeConfig.partitionKey.key);\n};\n\n// File-partitioned backends (dev-server json): the scope becomes a FOLDER\n// name, so the same validations apply, but all the backend needs back is the\n// real partition key attribute name.\nexport const resolveScopedPkAttributeOrThrow = (qpqConfig: QPQConfig, keyValueStoreName: string, scope: string): string => {\n const storeConfig = resolveKvsStoreConfigOrThrow(qpqConfig, keyValueStoreName);\n\n validateScopeSegment(scope);\n validateScopeSupportedForPartitionKeyType(storeConfig.partitionKey.type);\n\n return storeConfig.partitionKey.key;\n};\n\n// Validation-only counterparts for file-partitioned backends. They store keys\n// and items raw (the scope just picks the file), but anything the\n// value-composed translator would reject must fail locally too - same scope\n// validation, same reserved-delimiter rule - so local behavior matches\n// deployed behavior. The composed results are discarded.\n\nexport const validateScopedKvsKeyOrThrow = (\n qpqConfig: QPQConfig,\n keyValueStoreName: string,\n scope: string | undefined,\n key: KvsCoreDataType,\n): void => {\n getScopedKvsTranslatorOrThrow(qpqConfig, keyValueStoreName, scope).key(key);\n};\n\nexport const validateScopedKvsItemOrThrow = (\n qpqConfig: QPQConfig,\n keyValueStoreName: string,\n scope: string | undefined,\n item: Record<string, any>,\n): void => {\n getScopedKvsTranslatorOrThrow(qpqConfig, keyValueStoreName, scope).item(item ?? {});\n};\n\nexport const validateScopedKvsKeyConditionOrThrow = (\n qpqConfig: QPQConfig,\n keyValueStoreName: string,\n scope: string | undefined,\n keyCondition: KvsQueryOperation,\n): void => {\n getScopedKvsTranslatorOrThrow(qpqConfig, keyValueStoreName, scope).keyCondition(keyCondition);\n};\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type SplitScopedFilePath = {
|
|
2
|
+
scope: string;
|
|
3
|
+
filepath: string;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Undo composeScopedFilePath on a stored path whose scope is NOT known up front
|
|
7
|
+
* (a storage event for an object on a scoped drive). The first segment is the
|
|
8
|
+
* scope. Throws InvalidScopeError when there is no scope segment or it fails
|
|
9
|
+
* validation: on a scoped drive that object was written outside the framework.
|
|
10
|
+
*/
|
|
11
|
+
export declare function splitScopedFilePath(storedPath: string): SplitScopedFilePath;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.splitScopedFilePath = splitScopedFilePath;
|
|
4
|
+
const InvalidScopeError_1 = require("./InvalidScopeError");
|
|
5
|
+
const validateScopeSegment_1 = require("./validateScopeSegment");
|
|
6
|
+
/**
|
|
7
|
+
* Undo composeScopedFilePath on a stored path whose scope is NOT known up front
|
|
8
|
+
* (a storage event for an object on a scoped drive). The first segment is the
|
|
9
|
+
* scope. Throws InvalidScopeError when there is no scope segment or it fails
|
|
10
|
+
* validation: on a scoped drive that object was written outside the framework.
|
|
11
|
+
*/
|
|
12
|
+
function splitScopedFilePath(storedPath) {
|
|
13
|
+
const separatorIndex = storedPath.indexOf('/');
|
|
14
|
+
if (separatorIndex <= 0 || separatorIndex === storedPath.length - 1) {
|
|
15
|
+
throw new InvalidScopeError_1.InvalidScopeError(InvalidScopeError_1.InvalidScopeErrorCode.scopeRequired, `Stored path '${storedPath}' has no scope segment.`);
|
|
16
|
+
}
|
|
17
|
+
const scope = storedPath.slice(0, separatorIndex);
|
|
18
|
+
(0, validateScopeSegment_1.validateScopeSegment)(scope);
|
|
19
|
+
return { scope, filepath: storedPath.slice(separatorIndex + 1) };
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=splitScopedFilePath.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"splitScopedFilePath.js","sourceRoot":"","sources":["../../../../src/logic/scope/splitScopedFilePath.ts"],"names":[],"mappings":";;AAcA,kDAWC;AAzBD,2DAA+E;AAC/E,iEAA8D;AAO9D;;;;;GAKG;AACH,SAAgB,mBAAmB,CAAC,UAAkB;IACpD,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAE/C,IAAI,cAAc,IAAI,CAAC,IAAI,cAAc,KAAK,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,qCAAiB,CAAC,yCAAqB,CAAC,aAAa,EAAE,gBAAgB,UAAU,yBAAyB,CAAC,CAAC;IACxH,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;IAClD,IAAA,2CAAoB,EAAC,KAAK,CAAC,CAAC;IAE5B,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,EAAE,CAAC;AACnE,CAAC","sourcesContent":["import { InvalidScopeError, InvalidScopeErrorCode } from './InvalidScopeError';\nimport { validateScopeSegment } from './validateScopeSegment';\n\nexport type SplitScopedFilePath = {\n scope: string;\n filepath: string;\n};\n\n/**\n * Undo composeScopedFilePath on a stored path whose scope is NOT known up front\n * (a storage event for an object on a scoped drive). The first segment is the\n * scope. Throws InvalidScopeError when there is no scope segment or it fails\n * validation: on a scoped drive that object was written outside the framework.\n */\nexport function splitScopedFilePath(storedPath: string): SplitScopedFilePath {\n const separatorIndex = storedPath.indexOf('/');\n\n if (separatorIndex <= 0 || separatorIndex === storedPath.length - 1) {\n throw new InvalidScopeError(InvalidScopeErrorCode.scopeRequired, `Stored path '${storedPath}' has no scope segment.`);\n }\n\n const scope = storedPath.slice(0, separatorIndex);\n validateScopeSegment(scope);\n\n return { scope, filepath: storedPath.slice(separatorIndex + 1) };\n}\n"]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
declare const brandSymbol: unique symbol;
|
|
2
|
+
/**
|
|
3
|
+
* Nominal wrapper over a structural type: `Brand<string, 'UserId'>` is a string the compiler
|
|
4
|
+
* refuses to confuse with any other string. The brand exists only at the type level, so the
|
|
5
|
+
* only way in is a cast; funnel construction through a guard from `createBrandGuard`.
|
|
6
|
+
*
|
|
7
|
+
* @example Two ids that must never be mixed up
|
|
8
|
+
* ```ts
|
|
9
|
+
* type UserId = Brand<string, 'UserId'>;
|
|
10
|
+
* type TenantId = Brand<string, 'TenantId'>;
|
|
11
|
+
*
|
|
12
|
+
* const isUserId = createBrandGuard<string, UserId>((s) => s.startsWith('user_'));
|
|
13
|
+
*
|
|
14
|
+
* declare function getUser(id: UserId): User;
|
|
15
|
+
*
|
|
16
|
+
* const raw: string = request.params.id;
|
|
17
|
+
* if (isUserId(raw)) {
|
|
18
|
+
* getUser(raw); // ok, raw is UserId here
|
|
19
|
+
* }
|
|
20
|
+
* getUser(raw); // error: string is not UserId
|
|
21
|
+
* getUser('t_1' as TenantId); // error: TenantId is not UserId
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @example A validated primitive
|
|
25
|
+
* ```ts
|
|
26
|
+
* type Port = Brand<number, 'Port'>;
|
|
27
|
+
* const isPort = createBrandGuard<number, Port>((n) => Number.isInteger(n) && n > 0 && n < 65536);
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example Branded values still behave as their base type
|
|
31
|
+
* ```ts
|
|
32
|
+
* const id: UserId = 'user_1' as UserId;
|
|
33
|
+
* id.toUpperCase(); // fine, it is still a string
|
|
34
|
+
* const plain: string = id; // fine, UserId is assignable to string
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export type Brand<T, B extends string> = T & {
|
|
38
|
+
readonly [brandSymbol]: B;
|
|
39
|
+
};
|
|
40
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Brand.js","sourceRoot":"","sources":["../../../src/types/Brand.ts"],"names":[],"mappings":"","sourcesContent":["declare const brandSymbol: unique symbol;\n\n/**\n * Nominal wrapper over a structural type: `Brand<string, 'UserId'>` is a string the compiler\n * refuses to confuse with any other string. The brand exists only at the type level, so the\n * only way in is a cast; funnel construction through a guard from `createBrandGuard`.\n *\n * @example Two ids that must never be mixed up\n * ```ts\n * type UserId = Brand<string, 'UserId'>;\n * type TenantId = Brand<string, 'TenantId'>;\n *\n * const isUserId = createBrandGuard<string, UserId>((s) => s.startsWith('user_'));\n *\n * declare function getUser(id: UserId): User;\n *\n * const raw: string = request.params.id;\n * if (isUserId(raw)) {\n * getUser(raw); // ok, raw is UserId here\n * }\n * getUser(raw); // error: string is not UserId\n * getUser('t_1' as TenantId); // error: TenantId is not UserId\n * ```\n *\n * @example A validated primitive\n * ```ts\n * type Port = Brand<number, 'Port'>;\n * const isPort = createBrandGuard<number, Port>((n) => Number.isInteger(n) && n > 0 && n < 65536);\n * ```\n *\n * @example Branded values still behave as their base type\n * ```ts\n * const id: UserId = 'user_1' as UserId;\n * id.toUpperCase(); // fine, it is still a string\n * const plain: string = id; // fine, UserId is assignable to string\n * ```\n */\nexport type Brand<T, B extends string> = T & { readonly [brandSymbol]: B };\n"]}
|
|
@@ -2,6 +2,7 @@ export * from './Action';
|
|
|
2
2
|
export * from './ActionRequesterFunction';
|
|
3
3
|
export * from './authChallenge';
|
|
4
4
|
export * from './BoundLogicStory';
|
|
5
|
+
export * from './Brand';
|
|
5
6
|
export * from './ConfigUrlDefinition';
|
|
6
7
|
export * from './CrossModuleMessage';
|
|
7
8
|
export * from './CrossModuleOwner';
|
|
@@ -18,6 +18,7 @@ __exportStar(require("./Action"), exports);
|
|
|
18
18
|
__exportStar(require("./ActionRequesterFunction"), exports);
|
|
19
19
|
__exportStar(require("./authChallenge"), exports);
|
|
20
20
|
__exportStar(require("./BoundLogicStory"), exports);
|
|
21
|
+
__exportStar(require("./Brand"), exports);
|
|
21
22
|
__exportStar(require("./ConfigUrlDefinition"), exports);
|
|
22
23
|
__exportStar(require("./CrossModuleMessage"), exports);
|
|
23
24
|
__exportStar(require("./CrossModuleOwner"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,4DAA0C;AAC1C,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,oDAAkC;AAClC,+CAA6B;AAC7B,gDAA8B;AAC9B,mDAAiC;AACjC,iDAA+B;AAC/B,mDAAiC;AACjC,sDAAoC;AACpC,wDAAsC;AACtC,0CAAwB","sourcesContent":["export * from './Action';\nexport * from './ActionRequesterFunction';\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 './QpqPureFunction';\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,4DAA0C;AAC1C,kDAAgC;AAChC,oDAAkC;AAClC,0CAAwB;AACxB,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,oDAAkC;AAClC,+CAA6B;AAC7B,gDAA8B;AAC9B,mDAAiC;AACjC,iDAA+B;AAC/B,mDAAiC;AACjC,sDAAoC;AACpC,wDAAsC;AACtC,0CAAwB","sourcesContent":["export * from './Action';\nexport * from './ActionRequesterFunction';\nexport * from './authChallenge';\nexport * from './BoundLogicStory';\nexport * from './Brand';\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 './QpqPureFunction';\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"]}
|