@milaboratories/pl-model-common 1.28.0 → 1.30.0
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/dist/bmodel/block_config.d.ts +1 -0
- package/dist/bmodel/code.d.ts +1 -0
- package/dist/driver_kit.d.ts +1 -1
- package/dist/drivers/index.d.ts +6 -6
- package/dist/drivers/index.js +1 -1
- package/dist/drivers/pframe/driver.cjs.map +1 -1
- package/dist/drivers/pframe/driver.d.ts +13 -5
- package/dist/drivers/pframe/driver.js.map +1 -1
- package/dist/drivers/pframe/index.d.ts +6 -5
- package/dist/drivers/pframe/index.js +1 -1
- package/dist/drivers/pframe/pframe.d.ts +2 -2
- package/dist/drivers/pframe/query/query_common.d.ts +2 -2
- package/dist/drivers/pframe/query/query_data.d.ts +1 -1
- package/dist/drivers/pframe/query/query_spec.d.ts +2 -2
- package/dist/drivers/pframe/spec/ids.d.ts +2 -2
- package/dist/drivers/pframe/spec/index.d.ts +1 -1
- package/dist/drivers/pframe/spec/index.js +1 -1
- package/dist/drivers/pframe/spec/selectors.d.ts +1 -1
- package/dist/drivers/pframe/spec/spec.cjs +14 -1
- package/dist/drivers/pframe/spec/spec.cjs.map +1 -1
- package/dist/drivers/pframe/spec/spec.d.ts +9 -3
- package/dist/drivers/pframe/spec/spec.js +14 -2
- package/dist/drivers/pframe/spec/spec.js.map +1 -1
- package/dist/drivers/{pspec.d.ts → pframe/spec_driver.d.ts} +61 -16
- package/dist/drivers/pframe/table_calculate.d.ts +2 -2
- package/dist/drivers/pframe/table_common.d.ts +1 -1
- package/dist/drivers/pframe/unique_values.d.ts +2 -2
- package/dist/errors.cjs +56 -0
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +29 -1
- package/dist/errors.js +43 -1
- package/dist/errors.js.map +1 -1
- package/dist/flags/block_flags.cjs.map +1 -1
- package/dist/flags/block_flags.d.ts +4 -1
- package/dist/flags/block_flags.js.map +1 -1
- package/dist/flags/flag_utils.d.ts +1 -1
- package/dist/flags/index.d.ts +3 -0
- package/dist/index.cjs +40 -0
- package/dist/index.d.ts +30 -21
- package/dist/index.js +11 -3
- package/dist/pool/query.d.ts +1 -1
- package/dist/pool_entry.cjs +30 -0
- package/dist/pool_entry.cjs.map +1 -0
- package/dist/pool_entry.d.ts +30 -0
- package/dist/pool_entry.js +29 -0
- package/dist/pool_entry.js.map +1 -0
- package/dist/services/index.cjs +6 -0
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.js +6 -0
- package/dist/services/service_capabilities.cjs +51 -0
- package/dist/services/service_capabilities.cjs.map +1 -0
- package/dist/services/service_capabilities.d.ts +33 -0
- package/dist/services/service_capabilities.js +46 -0
- package/dist/services/service_capabilities.js.map +1 -0
- package/dist/services/service_declarations.cjs +17 -0
- package/dist/services/service_declarations.cjs.map +1 -0
- package/dist/services/service_declarations.d.ts +16 -0
- package/dist/services/service_declarations.js +17 -0
- package/dist/services/service_declarations.js.map +1 -0
- package/dist/services/service_injector_factory.cjs +19 -0
- package/dist/services/service_injector_factory.cjs.map +1 -0
- package/dist/services/service_injector_factory.d.ts +8 -0
- package/dist/services/service_injector_factory.js +18 -0
- package/dist/services/service_injector_factory.js.map +1 -0
- package/dist/services/service_injectors.cjs +48 -0
- package/dist/services/service_injectors.cjs.map +1 -0
- package/dist/services/service_injectors.d.ts +23 -0
- package/dist/services/service_injectors.js +46 -0
- package/dist/services/service_injectors.js.map +1 -0
- package/dist/services/service_registry.cjs +52 -0
- package/dist/services/service_registry.cjs.map +1 -0
- package/dist/services/service_registry.d.ts +25 -0
- package/dist/services/service_registry.js +51 -0
- package/dist/services/service_registry.js.map +1 -0
- package/dist/services/service_types.cjs +30 -0
- package/dist/services/service_types.cjs.map +1 -0
- package/dist/services/service_types.d.ts +45 -0
- package/dist/services/service_types.js +28 -0
- package/dist/services/service_types.js.map +1 -0
- package/package.json +5 -5
- package/src/drivers/index.ts +0 -1
- package/src/drivers/pframe/driver.ts +11 -1
- package/src/drivers/pframe/index.ts +1 -0
- package/src/drivers/pframe/spec/spec.ts +18 -4
- package/src/drivers/{pspec.ts → pframe/spec_driver.ts} +75 -17
- package/src/errors.ts +61 -0
- package/src/flags/block_flags.ts +5 -2
- package/src/index.ts +2 -0
- package/src/pool_entry.ts +42 -0
- package/src/services/index.ts +5 -0
- package/src/services/service_capabilities.test.ts +119 -0
- package/src/services/service_capabilities.ts +64 -0
- package/src/services/service_declarations.ts +25 -0
- package/src/services/service_injector_factory.ts +27 -0
- package/src/services/service_injectors.ts +79 -0
- package/src/services/service_registry.test.ts +69 -0
- package/src/services/service_registry.ts +94 -0
- package/src/services/service_types.ts +114 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ServiceAlreadyRegisteredError, ServiceInvalidIdError } from "../errors.js";
|
|
2
|
+
|
|
3
|
+
//#region src/services/service_types.ts
|
|
4
|
+
const SERVICE_ID_PATTERN = /^[a-zA-Z][a-zA-Z0-9]*$/;
|
|
5
|
+
const { service, isNodeService } = (() => {
|
|
6
|
+
const typeMap = /* @__PURE__ */ new Map();
|
|
7
|
+
return {
|
|
8
|
+
service() {
|
|
9
|
+
return (options) => {
|
|
10
|
+
const { name, type } = options;
|
|
11
|
+
if (!SERVICE_ID_PATTERN.test(name)) throw new ServiceInvalidIdError(`Invalid service ID "${name}": must match ${SERVICE_ID_PATTERN}`);
|
|
12
|
+
if (typeMap.has(name)) throw new ServiceAlreadyRegisteredError(`Service "${name}" already registered`);
|
|
13
|
+
typeMap.set(name, type);
|
|
14
|
+
return name;
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
isNodeService(id) {
|
|
18
|
+
return typeMap.get(id) === "node";
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
})();
|
|
22
|
+
function serviceFnKey(serviceId, method = "") {
|
|
23
|
+
return `service:${serviceId}:${method}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
export { isNodeService, service, serviceFnKey };
|
|
28
|
+
//# sourceMappingURL=service_types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service_types.js","names":[],"sources":["../../src/services/service_types.ts"],"sourcesContent":["import type { Branded } from \"../branding\";\nimport { ServiceAlreadyRegisteredError, ServiceInvalidIdError } from \"../errors\";\nimport type { Services } from \"./service_declarations\";\n\nexport type ServiceTypesLike<\n Model = unknown,\n Ui = unknown,\n Kind extends ServiceType = ServiceType,\n> = {\n readonly __types?: { model: Model; ui: Ui; kind: Kind };\n};\n\nexport type InferServiceModel<S extends ServiceTypesLike> =\n S extends ServiceTypesLike<infer M, unknown, ServiceType> ? M : unknown;\n\nexport type InferServiceUi<S extends ServiceTypesLike> =\n S extends ServiceTypesLike<unknown, infer U, ServiceType> ? U : unknown;\n\nexport type InferServiceKind<S extends ServiceTypesLike> =\n S extends ServiceTypesLike<unknown, unknown, infer K> ? K : ServiceType;\n\nexport type ServiceName<S extends ServiceTypesLike = ServiceTypesLike> = Branded<string, S>;\n\nexport type ServiceType = \"node\" | \"wasm\";\n\nconst SERVICE_ID_PATTERN = /^[a-zA-Z][a-zA-Z0-9]*$/;\n\nexport const { service, isNodeService } = (() => {\n const typeMap = new Map<string, ServiceType>();\n return {\n service<Model, Ui>() {\n return <K extends ServiceType, N extends string>(options: {\n readonly type: K;\n readonly name: N;\n }): Branded<N, ServiceTypesLike<Model, Ui, K>> => {\n const { name, type } = options;\n if (!SERVICE_ID_PATTERN.test(name)) {\n throw new ServiceInvalidIdError(\n `Invalid service ID \"${name}\": must match ${SERVICE_ID_PATTERN}`,\n );\n }\n if (typeMap.has(name)) {\n throw new ServiceAlreadyRegisteredError(`Service \"${name}\" already registered`);\n }\n typeMap.set(name, type);\n return name as Branded<N, ServiceTypesLike<Model, Ui, K>>;\n };\n },\n isNodeService(id: ServiceName): boolean {\n return typeMap.get(id) === \"node\";\n },\n };\n})();\n\nexport function serviceFnKey(serviceId: string, method = \"\"): string {\n return `service:${serviceId}:${method}`;\n}\n\nexport type ServiceBrand<T> =\n T extends Branded<string, infer S extends ServiceTypesLike> ? S : never;\n\nexport type ModelServiceFactoryMap<SMap extends Record<string, ServiceName>> = {\n [K in keyof SMap]: (() => InferServiceModel<ServiceBrand<SMap[K]>>) | null;\n};\n\nexport type UiServiceFactoryMap<SMap extends Record<string, ServiceName>> = {\n [K in keyof SMap]: (() => InferServiceUi<ServiceBrand<SMap[K]>>) | null;\n};\n\n/** Contract between any service provider and any service consumer. */\nexport interface ServiceDispatch {\n getServiceNames(): ServiceName[];\n getServiceMethods(serviceId: ServiceName): string[];\n callServiceMethod(serviceId: ServiceName, method: string, ...args: unknown[]): unknown;\n}\n\n// Auto-derived types from the Services const in service_declarations.ts.\n// Adding a service to Services automatically updates all of these.\n\ntype SMap = typeof Services;\n\ntype ExtractServiceName<T> = T extends Branded<infer N extends string, any> ? N : never;\n\n/** Model-side service interfaces keyed by service name literal. */\nexport type ModelServices = {\n [K in keyof SMap as ExtractServiceName<SMap[K]>]: InferServiceModel<ServiceBrand<SMap[K]>>;\n};\n\n/** UI-side service interfaces keyed by service name literal. */\nexport type UiServices = {\n [K in keyof SMap as ExtractServiceName<SMap[K]>]: InferServiceUi<ServiceBrand<SMap[K]>>;\n};\n\n/** Map from Services keys to their unbranded string name literals. */\nexport type ServiceNameLiterals = {\n [K in keyof SMap]: ExtractServiceName<SMap[K]>;\n};\n\n/** Auto-derived requires* feature flags from Services keys. */\nexport type ServiceRequireFlags = {\n [K in keyof SMap as `requires${K & string}`]?: boolean;\n};\n\ntype UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (\n k: infer I,\n) => void\n ? I\n : never;\n\nexport type RequireServices<T extends ServiceName> = UnionToIntersection<\n T extends Branded<infer N extends string, infer S extends ServiceTypesLike>\n ? Record<N, InferServiceModel<S>>\n : never\n>;\n"],"mappings":";;;AAyBA,MAAM,qBAAqB;AAE3B,MAAa,EAAE,SAAS,yBAAyB;CAC/C,MAAM,0BAAU,IAAI,KAA0B;AAC9C,QAAO;EACL,UAAqB;AACnB,WAAiD,YAGC;IAChD,MAAM,EAAE,MAAM,SAAS;AACvB,QAAI,CAAC,mBAAmB,KAAK,KAAK,CAChC,OAAM,IAAI,sBACR,uBAAuB,KAAK,gBAAgB,qBAC7C;AAEH,QAAI,QAAQ,IAAI,KAAK,CACnB,OAAM,IAAI,8BAA8B,YAAY,KAAK,sBAAsB;AAEjF,YAAQ,IAAI,MAAM,KAAK;AACvB,WAAO;;;EAGX,cAAc,IAA0B;AACtC,UAAO,QAAQ,IAAI,GAAG,KAAK;;EAE9B;IACC;AAEJ,SAAgB,aAAa,WAAmB,SAAS,IAAY;AACnE,QAAO,WAAW,UAAU,GAAG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/pl-model-common",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.0",
|
|
4
4
|
"description": "Platforma SDK Model",
|
|
5
5
|
"files": [
|
|
6
6
|
"./dist/**/*",
|
|
@@ -19,16 +19,16 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"canonicalize": "~2.1.0",
|
|
21
21
|
"zod": "~3.23.8",
|
|
22
|
-
"@milaboratories/
|
|
23
|
-
"@milaboratories/
|
|
22
|
+
"@milaboratories/helpers": "1.14.0",
|
|
23
|
+
"@milaboratories/pl-error-like": "1.12.9"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@vitest/coverage-istanbul": "^4.0.18",
|
|
27
27
|
"typescript": "~5.9.3",
|
|
28
28
|
"vitest": "^4.0.18",
|
|
29
|
+
"@milaboratories/ts-configs": "1.2.2",
|
|
29
30
|
"@milaboratories/ts-builder": "1.3.0",
|
|
30
|
-
"@milaboratories/build-configs": "1.5.2"
|
|
31
|
-
"@milaboratories/ts-configs": "1.2.2"
|
|
31
|
+
"@milaboratories/build-configs": "1.5.2"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "ts-builder build --target node",
|
package/src/drivers/index.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { Branded } from "../../branding";
|
|
2
2
|
import type { PTable } from "./table";
|
|
3
|
-
import type { PFrame } from "./pframe";
|
|
3
|
+
import type { PFrame, PFrameDef } from "./pframe";
|
|
4
|
+
import type { PColumn } from "./spec/spec";
|
|
5
|
+
import type { PColumnValues, DataInfo } from "./data_info";
|
|
6
|
+
import type { PTableDef, PTableDefV2 } from "./table_calculate";
|
|
4
7
|
import type { AddParameterToAllMethods } from "./type_util";
|
|
5
8
|
import type { PTableShape, PTableVector, TableRange } from "./data_types";
|
|
6
9
|
import type { FindColumnsRequest, FindColumnsResponse } from "./find_columns";
|
|
@@ -16,6 +19,13 @@ export type PFrameHandle = Branded<string, "PFrame">;
|
|
|
16
19
|
/** PFrame handle */
|
|
17
20
|
export type PTableHandle = Branded<string, "PTable">;
|
|
18
21
|
|
|
22
|
+
/** Model-side PFrame service — creates frames/tables from column definitions. */
|
|
23
|
+
export interface PFrameModelDriver<Col = PColumn<string | PColumnValues | DataInfo<string>>> {
|
|
24
|
+
createPFrame(def: PFrameDef<Col>): PFrameHandle;
|
|
25
|
+
createPTable(def: PTableDef<Col>): PTableHandle;
|
|
26
|
+
createPTableV2(def: PTableDefV2<Col>): PTableHandle;
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
/** Allows to access main data layer features of platforma */
|
|
20
30
|
export interface PFrameDriver {
|
|
21
31
|
//
|
|
@@ -303,7 +303,7 @@ export function isLinkerColumn(column: PColumnSpec): boolean {
|
|
|
303
303
|
*/
|
|
304
304
|
export type AxisSpec = {
|
|
305
305
|
/** Type of the axis value. Should not use non-key types like float or double. */
|
|
306
|
-
readonly type:
|
|
306
|
+
readonly type: AxisValueType;
|
|
307
307
|
|
|
308
308
|
/** Name of the axis */
|
|
309
309
|
readonly name: string;
|
|
@@ -508,8 +508,6 @@ export function getNormalizedAxesList(axes: AxisSpec[]): AxisSpecNormalized[] {
|
|
|
508
508
|
modifiedAxis.parentAxesSpec = parents.some((p) => p === undefined)
|
|
509
509
|
? []
|
|
510
510
|
: (parents as AxisSpecNormalized[]);
|
|
511
|
-
|
|
512
|
-
delete modifiedAxis.annotations?.[Annotation.Parents];
|
|
513
511
|
}
|
|
514
512
|
});
|
|
515
513
|
|
|
@@ -543,6 +541,22 @@ export function getDenormalizedAxesList(axesSpec: AxisSpecNormalized[]): AxisSpe
|
|
|
543
541
|
});
|
|
544
542
|
}
|
|
545
543
|
|
|
544
|
+
/**
|
|
545
|
+
* Resolve annotation-based parents (`pl7.app/parents`) to numeric `parentAxes`
|
|
546
|
+
* on a column spec. Returns the spec unchanged if all axes already use numeric
|
|
547
|
+
* indices or have no parent annotations.
|
|
548
|
+
*/
|
|
549
|
+
export function resolveAnnotationParents(spec: PColumnSpec): PColumnSpec {
|
|
550
|
+
const hasAnnotationParents = spec.axesSpec.some(
|
|
551
|
+
(axis) => !!readAnnotationJson(axis, Annotation.Parents),
|
|
552
|
+
);
|
|
553
|
+
if (!hasAnnotationParents) return spec;
|
|
554
|
+
|
|
555
|
+
const normalized = getNormalizedAxesList(spec.axesSpec);
|
|
556
|
+
const denormalized = getDenormalizedAxesList(normalized);
|
|
557
|
+
return { ...spec, axesSpec: denormalized };
|
|
558
|
+
}
|
|
559
|
+
|
|
546
560
|
/** Common type representing spec for all the axes in a column */
|
|
547
561
|
export type AxesSpec = AxisSpec[];
|
|
548
562
|
|
|
@@ -706,7 +720,7 @@ export interface PColumnInfo extends PColumnIdAndSpec {
|
|
|
706
720
|
export interface AxisId {
|
|
707
721
|
/** Type of the axis or column value. For an axis should not use non-key
|
|
708
722
|
* types like float or double. */
|
|
709
|
-
readonly type:
|
|
723
|
+
readonly type: AxisValueType;
|
|
710
724
|
|
|
711
725
|
/** Name of the axis or column */
|
|
712
726
|
readonly name: string;
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
import type { Branded } from "@milaboratories/helpers";
|
|
2
|
+
import type { PoolEntry } from "../../pool_entry";
|
|
2
3
|
import type {
|
|
3
|
-
ValueType,
|
|
4
|
-
SingleAxisSelector,
|
|
5
4
|
AxisSpec,
|
|
5
|
+
AxesSpec,
|
|
6
|
+
AxesId,
|
|
6
7
|
PColumnIdAndSpec,
|
|
7
8
|
PColumnSpec,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
SingleAxisSelector,
|
|
10
|
+
AxisValueType,
|
|
11
|
+
ColumnValueType,
|
|
12
|
+
} from "./spec";
|
|
13
|
+
import type { PTableColumnId, PTableColumnSpec } from "./table_common";
|
|
14
|
+
import { DataQuery, SpecQuery } from "./query";
|
|
11
15
|
|
|
12
16
|
/** Matches a string value either exactly or by regex pattern */
|
|
13
|
-
export type StringMatcher =
|
|
17
|
+
export type StringMatcher =
|
|
18
|
+
| {
|
|
19
|
+
type: "exact";
|
|
20
|
+
value: string;
|
|
21
|
+
}
|
|
22
|
+
| {
|
|
23
|
+
type: "regex";
|
|
24
|
+
value: string;
|
|
25
|
+
};
|
|
14
26
|
|
|
15
27
|
/** Map of key to array of string matchers (OR-ed per key, AND-ed across keys) */
|
|
16
28
|
export type MatcherMap = Record<string, StringMatcher[]>;
|
|
@@ -18,7 +30,7 @@ export type MatcherMap = Record<string, StringMatcher[]>;
|
|
|
18
30
|
/** Selector for matching axes by various criteria */
|
|
19
31
|
export interface MultiAxisSelector {
|
|
20
32
|
/** Match any of the axis types listed here */
|
|
21
|
-
readonly type?:
|
|
33
|
+
readonly type?: AxisValueType[];
|
|
22
34
|
/** Match any of the axis names listed here */
|
|
23
35
|
readonly name?: StringMatcher[];
|
|
24
36
|
/** Match requires all the domains listed here */
|
|
@@ -33,7 +45,7 @@ export interface MultiAxisSelector {
|
|
|
33
45
|
* Multiple selectors are OR-ed: a column matches if it satisfies any selector. */
|
|
34
46
|
export interface MultiColumnSelector {
|
|
35
47
|
/** Match any of the value types listed here */
|
|
36
|
-
readonly type?:
|
|
48
|
+
readonly type?: ColumnValueType[];
|
|
37
49
|
/** Match any of the names listed here */
|
|
38
50
|
readonly name?: StringMatcher[];
|
|
39
51
|
/** Match requires all the domains listed here */
|
|
@@ -78,10 +90,14 @@ export interface DiscoverColumnsConstraints {
|
|
|
78
90
|
|
|
79
91
|
/** Request for discovering columns compatible with a given axes integration */
|
|
80
92
|
export interface DiscoverColumnsRequest {
|
|
81
|
-
/**
|
|
82
|
-
|
|
93
|
+
/** Include columns matching these selectors (OR-ed); empty or omitted matches all columns */
|
|
94
|
+
includeColumns?: MultiColumnSelector[];
|
|
95
|
+
/** Exclude columns matching these selectors (OR-ed); applied after include filter */
|
|
96
|
+
excludeColumns?: MultiColumnSelector[];
|
|
83
97
|
/** Already integrated axes with qualifications */
|
|
84
98
|
axes: ColumnAxesWithQualifications[];
|
|
99
|
+
/** Maximum number of hops allowed between provided axes integration and returned hits (0 = direct only) */
|
|
100
|
+
maxHops?: number;
|
|
85
101
|
/** Constraints controlling axes matching and qualification behavior */
|
|
86
102
|
constraints: DiscoverColumnsConstraints;
|
|
87
103
|
}
|
|
@@ -120,6 +136,8 @@ export interface DiscoverColumnsResponseHit {
|
|
|
120
136
|
hit: PColumnIdAndSpec;
|
|
121
137
|
/** Possible ways to integrate this column with the existing set */
|
|
122
138
|
mappingVariants: DiscoverColumnsMappingVariant[];
|
|
139
|
+
/** Linker steps traversed to reach this hit; empty for direct matches */
|
|
140
|
+
path: DiscoverColumnsStepInfo[];
|
|
123
141
|
}
|
|
124
142
|
|
|
125
143
|
/** Response from discover columns */
|
|
@@ -128,7 +146,32 @@ export interface DiscoverColumnsResponse {
|
|
|
128
146
|
hits: DiscoverColumnsResponseHit[];
|
|
129
147
|
}
|
|
130
148
|
|
|
131
|
-
|
|
149
|
+
/** Request for deleting an entry from a given axes integration */
|
|
150
|
+
export interface DeleteColumnRequest {
|
|
151
|
+
/** Already integrated axes with qualifications */
|
|
152
|
+
axes: ColumnAxesWithQualifications[];
|
|
153
|
+
/** Zero based index of the entry to be deleted */
|
|
154
|
+
delete: number;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Response from delete column */
|
|
158
|
+
export interface DeleteColumnResponse {
|
|
159
|
+
axes: ColumnAxesWithQualifications[];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Response from evaluating a query against a PFrame. */
|
|
163
|
+
export type EvaluateQueryResponse = {
|
|
164
|
+
/**
|
|
165
|
+
* The table specification describing the structure of the query result,
|
|
166
|
+
* including all axes and columns that will be present in the output.
|
|
167
|
+
*/
|
|
168
|
+
tableSpec: PTableColumnSpec[];
|
|
169
|
+
/**
|
|
170
|
+
* The data layer query representation with numeric indices,
|
|
171
|
+
* suitable for execution by the data processing engine.
|
|
172
|
+
*/
|
|
173
|
+
dataQuery: DataQuery;
|
|
174
|
+
};
|
|
132
175
|
|
|
133
176
|
/** Handle to a spec-only PFrame (no data, synchronous operations). */
|
|
134
177
|
export type SpecFrameHandle = Branded<string, "SpecFrameHandle">;
|
|
@@ -140,16 +183,31 @@ export type SpecFrameHandle = Branded<string, "SpecFrameHandle">;
|
|
|
140
183
|
* operates on column specifications only. All methods are synchronous
|
|
141
184
|
* because the underlying WASM PFrame computes results immediately.
|
|
142
185
|
*/
|
|
143
|
-
export interface
|
|
144
|
-
/** Create a spec-only PFrame from column specs. Returns a handle. */
|
|
145
|
-
createSpecFrame(specs: Record<string, PColumnSpec>): SpecFrameHandle
|
|
186
|
+
export interface PFrameSpecDriver {
|
|
187
|
+
/** Create a spec-only PFrame from column specs. Returns a pool entry with handle and unref. */
|
|
188
|
+
createSpecFrame(specs: Record<string, PColumnSpec>): PoolEntry<SpecFrameHandle>;
|
|
146
189
|
|
|
147
190
|
/** Discover columns compatible with given axes integration. */
|
|
148
|
-
|
|
191
|
+
discoverColumns(
|
|
149
192
|
handle: SpecFrameHandle,
|
|
150
193
|
request: DiscoverColumnsRequest,
|
|
151
194
|
): DiscoverColumnsResponse;
|
|
152
195
|
|
|
153
|
-
/**
|
|
154
|
-
|
|
196
|
+
/** Delete an entry from a given axes integration */
|
|
197
|
+
deleteColumn(handle: SpecFrameHandle, request: DeleteColumnRequest): DeleteColumnResponse;
|
|
198
|
+
|
|
199
|
+
/** Evaluates a query specification against this PFrame */
|
|
200
|
+
evaluateQuery(handle: SpecFrameHandle, request: SpecQuery): EvaluateQueryResponse;
|
|
201
|
+
|
|
202
|
+
/** Expand index-based parentAxes in AxesSpec to resolved AxisId parents in AxesId. */
|
|
203
|
+
expandAxes(spec: AxesSpec): AxesId;
|
|
204
|
+
|
|
205
|
+
/** Collapse resolved AxisId parents back to index-based parentAxes in AxesSpec. */
|
|
206
|
+
collapseAxes(ids: AxesId): AxesSpec;
|
|
207
|
+
|
|
208
|
+
/** Find the index of an axis matching the given selector. Returns -1 if not found. */
|
|
209
|
+
findAxis(spec: AxesSpec, selector: SingleAxisSelector): number;
|
|
210
|
+
|
|
211
|
+
/** Find the flat index of a table column matching the given selector. Returns -1 if not found. */
|
|
212
|
+
findTableColumn(tableSpec: PTableColumnSpec[], selector: PTableColumnId): number;
|
|
155
213
|
}
|
package/src/errors.ts
CHANGED
|
@@ -28,6 +28,59 @@ export function isAggregateError(error: unknown): error is AggregateError {
|
|
|
28
28
|
return error instanceof Error && error.name === "AggregateError";
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
export class ServiceError extends Error {
|
|
32
|
+
name = "ServiceError";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function isServiceError(error: unknown): error is ServiceError {
|
|
36
|
+
return (
|
|
37
|
+
error instanceof Error &&
|
|
38
|
+
(error.name === "ServiceError" || error.name.startsWith("ServiceError."))
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class ServiceInvalidIdError extends ServiceError {
|
|
43
|
+
name = "ServiceError.InvalidId";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function isServiceInvalidIdError(error: unknown): error is ServiceInvalidIdError {
|
|
47
|
+
return error instanceof Error && error.name === "ServiceError.InvalidId";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class ServiceAlreadyRegisteredError extends ServiceError {
|
|
51
|
+
name = "ServiceError.AlreadyRegistered";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function isServiceAlreadyRegisteredError(
|
|
55
|
+
error: unknown,
|
|
56
|
+
): error is ServiceAlreadyRegisteredError {
|
|
57
|
+
return error instanceof Error && error.name === "ServiceError.AlreadyRegistered";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export class ServiceInjectionError extends ServiceError {
|
|
61
|
+
name = "ServiceError.Injection";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function isServiceInjectionError(error: unknown): error is ServiceInjectionError {
|
|
65
|
+
return error instanceof Error && error.name === "ServiceError.Injection";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export class ServiceNotRegisteredError extends ServiceError {
|
|
69
|
+
name = "ServiceError.NotRegistered";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function isServiceNotRegisteredError(error: unknown): error is ServiceNotRegisteredError {
|
|
73
|
+
return error instanceof Error && error.name === "ServiceError.NotRegistered";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export class ServiceMethodNotFoundError extends ServiceError {
|
|
77
|
+
name = "ServiceError.MethodNotFound";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function isServiceMethodNotFoundError(error: unknown): error is ServiceMethodNotFoundError {
|
|
81
|
+
return error instanceof Error && error.name === "ServiceError.MethodNotFound";
|
|
82
|
+
}
|
|
83
|
+
|
|
31
84
|
export class PFrameError extends Error {
|
|
32
85
|
name = "PFrameError";
|
|
33
86
|
}
|
|
@@ -44,6 +97,14 @@ export function isPFrameDriverError(error: unknown): error is PFrameDriverError
|
|
|
44
97
|
return error instanceof Error && error.name === "PFrameError.Driver";
|
|
45
98
|
}
|
|
46
99
|
|
|
100
|
+
export class PFrameSpecDriverError extends PFrameError {
|
|
101
|
+
name = "PFrameError.SpecDriver";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function isPFrameSpecDriverError(error: unknown): error is PFrameSpecDriverError {
|
|
105
|
+
return error instanceof Error && error.name === "PFrameError.SpecDriver";
|
|
106
|
+
}
|
|
107
|
+
|
|
47
108
|
function stringifyValue(value: unknown): string {
|
|
48
109
|
if (typeof value === "string") {
|
|
49
110
|
return `String value was thrown: ${value}`;
|
package/src/flags/block_flags.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ServiceRequireFlags } from "../services";
|
|
1
2
|
import type { ArrayTypeUnion, Assert, Is, IsSubtypeOf } from "./type_utils";
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -20,7 +21,7 @@ export type BlockCodeKnownFeatureFlags = {
|
|
|
20
21
|
readonly requiresModelAPIVersion?: number;
|
|
21
22
|
readonly requiresUIAPIVersion?: number;
|
|
22
23
|
readonly requiresCreatePTable?: number;
|
|
23
|
-
};
|
|
24
|
+
} & ServiceRequireFlags;
|
|
24
25
|
|
|
25
26
|
export const AllSupportsFeatureFlags = ["supportsLazyState", "supportsPframeQueryRanking"] as const;
|
|
26
27
|
|
|
@@ -42,9 +43,11 @@ type _KnownFlagsAreValidFlags = Assert<
|
|
|
42
43
|
|
|
43
44
|
// This check ensures that all keys in BlockConfigV3FeatureFlags are covered in the arrays above.
|
|
44
45
|
// It will produce a compile-time error if there's a mismatch.
|
|
46
|
+
// Adding a service to Services automatically satisfies this assertion via ServiceRequireFlags.
|
|
45
47
|
type _AllFlagsAreCovered = Assert<
|
|
46
48
|
Is<
|
|
47
49
|
keyof BlockCodeKnownFeatureFlags,
|
|
48
|
-
ArrayTypeUnion<typeof AllRequiresFeatureFlags, typeof AllSupportsFeatureFlags>
|
|
50
|
+
| ArrayTypeUnion<typeof AllRequiresFeatureFlags, typeof AllSupportsFeatureFlags>
|
|
51
|
+
| keyof ServiceRequireFlags
|
|
49
52
|
>
|
|
50
53
|
>;
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface PoolEntry<K extends string = string, R extends {} = {}> extends Disposable {
|
|
2
|
+
/** Resource key, calculated using provided `calculateParamsKey` function */
|
|
3
|
+
readonly key: K;
|
|
4
|
+
|
|
5
|
+
/** Resource itself created by `createNewResource` function */
|
|
6
|
+
readonly resource: R;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Release the reference. Idempotent.
|
|
10
|
+
* Same as `[Symbol.dispose]()` — provided as a named function
|
|
11
|
+
* for use in callbacks (e.g. `addOnDestroy(entry.unref)`).
|
|
12
|
+
*/
|
|
13
|
+
readonly unref: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Wraps a PoolEntry for use with `using`. Auto-calls `unref()` at end of scope
|
|
18
|
+
* unless `keep()` is called to transfer ownership to the caller.
|
|
19
|
+
*/
|
|
20
|
+
export class PoolEntryGuard<K extends string = string, R extends {} = {}> implements Disposable {
|
|
21
|
+
private kept = false;
|
|
22
|
+
|
|
23
|
+
constructor(readonly entry: PoolEntry<K, R>) {}
|
|
24
|
+
|
|
25
|
+
get key(): K {
|
|
26
|
+
return this.entry.key;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get resource(): R {
|
|
30
|
+
return this.entry.resource;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Disarm the guard — caller takes ownership of the entry. */
|
|
34
|
+
keep(): PoolEntry<K, R> {
|
|
35
|
+
this.kept = true;
|
|
36
|
+
return this.entry;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
[Symbol.dispose](): void {
|
|
40
|
+
if (!this.kept) this.entry.unref();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
SERVICE_CAPABILITY_FLAGS,
|
|
4
|
+
registerServiceCapabilities,
|
|
5
|
+
resolveRequiredServices,
|
|
6
|
+
getMethodNames,
|
|
7
|
+
isKnownServiceName,
|
|
8
|
+
} from "./service_capabilities";
|
|
9
|
+
import { Services } from "./service_declarations";
|
|
10
|
+
|
|
11
|
+
describe("SERVICE_CAPABILITY_FLAGS", () => {
|
|
12
|
+
it("should have one flag per service", () => {
|
|
13
|
+
expect(SERVICE_CAPABILITY_FLAGS).toHaveLength(Object.keys(Services).length);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should derive requires* flag names from Services keys", () => {
|
|
17
|
+
for (const key of Object.keys(Services)) {
|
|
18
|
+
expect(SERVICE_CAPABILITY_FLAGS).toContain(`requires${key}`);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("registerServiceCapabilities", () => {
|
|
24
|
+
it("should call register once per service with (flag, true)", () => {
|
|
25
|
+
const register = vi.fn();
|
|
26
|
+
registerServiceCapabilities(register);
|
|
27
|
+
expect(register).toHaveBeenCalledTimes(Object.keys(Services).length);
|
|
28
|
+
for (const call of register.mock.calls) {
|
|
29
|
+
expect(call[0]).toMatch(/^requires[A-Z]/);
|
|
30
|
+
expect(call[1]).toBe(true);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("resolveRequiredServices", () => {
|
|
36
|
+
it("should return empty array for undefined flags", () => {
|
|
37
|
+
expect(resolveRequiredServices(undefined)).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should return empty array for flags without requires*", () => {
|
|
41
|
+
expect(resolveRequiredServices({ supportsLazyState: true })).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should return empty array when requires* is false", () => {
|
|
45
|
+
expect(resolveRequiredServices({ requiresPFrameSpec: false })).toEqual([]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should resolve a single required service", () => {
|
|
49
|
+
const result = resolveRequiredServices({ requiresPFrameSpec: true });
|
|
50
|
+
expect(result).toEqual([Services.PFrameSpec]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should resolve multiple required services", () => {
|
|
54
|
+
const result = resolveRequiredServices({
|
|
55
|
+
requiresPFrameSpec: true,
|
|
56
|
+
requiresPFrame: true,
|
|
57
|
+
});
|
|
58
|
+
expect(result).toContain(Services.PFrameSpec);
|
|
59
|
+
expect(result).toContain(Services.PFrame);
|
|
60
|
+
expect(result).toHaveLength(2);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should ignore non-boolean requires* values", () => {
|
|
64
|
+
const result = resolveRequiredServices({
|
|
65
|
+
requiresPFrameSpec: true,
|
|
66
|
+
requiresModelAPIVersion: 2,
|
|
67
|
+
});
|
|
68
|
+
expect(result).toEqual([Services.PFrameSpec]);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("isKnownServiceName", () => {
|
|
73
|
+
it("should return true for registered service names", () => {
|
|
74
|
+
for (const id of Object.values(Services)) {
|
|
75
|
+
expect(isKnownServiceName(id as string)).toBe(true);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should return false for unknown names", () => {
|
|
80
|
+
expect(isKnownServiceName("nonexistent")).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("getMethodNames", () => {
|
|
85
|
+
it("should return own method names", () => {
|
|
86
|
+
const obj = {
|
|
87
|
+
foo() {},
|
|
88
|
+
bar() {},
|
|
89
|
+
baz: 42,
|
|
90
|
+
};
|
|
91
|
+
const names = getMethodNames(obj as any);
|
|
92
|
+
expect(names).toContain("foo");
|
|
93
|
+
expect(names).toContain("bar");
|
|
94
|
+
expect(names).not.toContain("baz");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should include prototype methods", () => {
|
|
98
|
+
class Base {
|
|
99
|
+
baseMethod() {}
|
|
100
|
+
}
|
|
101
|
+
class Child extends Base {
|
|
102
|
+
childMethod() {}
|
|
103
|
+
}
|
|
104
|
+
const names = getMethodNames(new Child() as any);
|
|
105
|
+
expect(names).toContain("baseMethod");
|
|
106
|
+
expect(names).toContain("childMethod");
|
|
107
|
+
expect(names).not.toContain("constructor");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("should not include getters", () => {
|
|
111
|
+
const obj = Object.create(null, {
|
|
112
|
+
method: { value: () => {}, enumerable: true },
|
|
113
|
+
getter: { get: () => 42, enumerable: true },
|
|
114
|
+
});
|
|
115
|
+
const names = getMethodNames(obj);
|
|
116
|
+
expect(names).toContain("method");
|
|
117
|
+
expect(names).not.toContain("getter");
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ServiceName, ServiceRequireFlags } from "./service_types";
|
|
2
|
+
import { Services } from "./service_declarations";
|
|
3
|
+
import type { SupportedRequirement } from "../flags/flag_utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* All service-related `requires*` capability flag names, auto-derived from Services.
|
|
7
|
+
* Single source of truth — use this everywhere runtime capabilities are registered.
|
|
8
|
+
*/
|
|
9
|
+
export const SERVICE_CAPABILITY_FLAGS: readonly SupportedRequirement[] = Object.keys(Services).map(
|
|
10
|
+
(key) => `requires${key}` as SupportedRequirement,
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Register all service capability flags with the given callback.
|
|
15
|
+
* Works with both `RuntimeCapabilities.addSupportedRequirement`
|
|
16
|
+
* and `MiddleLayer.addRuntimeCapability`.
|
|
17
|
+
*/
|
|
18
|
+
export function registerServiceCapabilities(
|
|
19
|
+
register: (flag: SupportedRequirement, value: true) => void,
|
|
20
|
+
): void {
|
|
21
|
+
for (const flag of SERVICE_CAPABILITY_FLAGS) {
|
|
22
|
+
register(flag, true);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resolve which services are required by the given feature flags.
|
|
28
|
+
* Accepts Record<string, unknown> so it works with both BlockCodeKnownFeatureFlags
|
|
29
|
+
* (from middle layer) and Zod-parsed records (from preload).
|
|
30
|
+
*/
|
|
31
|
+
export function resolveRequiredServices(flags: Record<string, unknown> | undefined): ServiceName[] {
|
|
32
|
+
if (!flags) return [];
|
|
33
|
+
return (Object.keys(Services) as (keyof typeof Services)[])
|
|
34
|
+
.filter((key) => flags[`requires${key}`] === true)
|
|
35
|
+
.map((key) => Services[key]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type KnownServiceName = (typeof Services)[keyof typeof Services] & string;
|
|
39
|
+
|
|
40
|
+
export function isKnownServiceName(name: string): name is KnownServiceName {
|
|
41
|
+
return Object.values(Services).some((v) => v === name);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** All service require flags set to true, auto-generated from Services.
|
|
45
|
+
* Used to distinguish service-related feature flags from non-service flags. */
|
|
46
|
+
export const SERVICE_FEATURE_FLAGS: { readonly [K in keyof ServiceRequireFlags]-?: true } =
|
|
47
|
+
Object.fromEntries(Object.keys(Services).map((key) => [`requires${key}`, true])) as any;
|
|
48
|
+
|
|
49
|
+
/** Introspect method names on an instance (including prototype chain).
|
|
50
|
+
* Uses Object.getOwnPropertyDescriptor to avoid triggering getters. */
|
|
51
|
+
export function getMethodNames<T extends object>(instance: T): string[] {
|
|
52
|
+
const methods = new Set<string>();
|
|
53
|
+
let proto: object | null = instance;
|
|
54
|
+
while (proto && proto !== Object.prototype) {
|
|
55
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
56
|
+
const descriptor = Object.getOwnPropertyDescriptor(proto, key);
|
|
57
|
+
if (key !== "constructor" && typeof descriptor?.value === "function") {
|
|
58
|
+
methods.add(key);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
proto = Object.getPrototypeOf(proto);
|
|
62
|
+
}
|
|
63
|
+
return [...methods];
|
|
64
|
+
}
|