@cosmicdrift/kumiko-framework 0.2.3 → 0.4.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/CHANGELOG.md +93 -0
- package/package.json +124 -39
- package/src/__tests__/full-stack.integration.ts +2 -2
- package/src/api/auth-routes.ts +5 -5
- package/src/api/jwt.ts +2 -2
- package/src/api/route-registrars.ts +1 -1
- package/src/api/routes.ts +3 -3
- package/src/api/server.ts +6 -7
- package/src/compliance/profiles.ts +8 -8
- package/src/db/assert-exists-in.ts +2 -2
- package/src/db/cursor.ts +3 -3
- package/src/db/event-store-executor.ts +19 -13
- package/src/db/located-timestamp.ts +1 -1
- package/src/db/money.ts +12 -2
- package/src/db/pg-error.ts +1 -1
- package/src/db/row-helpers.ts +1 -1
- package/src/db/table-builder.ts +3 -5
- package/src/db/tenant-db.ts +9 -9
- package/src/engine/__tests__/_pipeline-test-utils.ts +23 -0
- package/src/engine/__tests__/build-target.test.ts +135 -0
- package/src/engine/__tests__/codemod-pipeline.test.ts +551 -0
- package/src/engine/__tests__/entity-handlers.test.ts +3 -3
- package/src/engine/__tests__/event-helpers.test.ts +4 -4
- package/src/engine/__tests__/pipeline-engine.test.ts +215 -0
- package/src/engine/__tests__/pipeline-handler.integration.ts +894 -0
- package/src/engine/__tests__/pipeline-observability.integration.ts +142 -0
- package/src/engine/__tests__/pipeline-performance.integration.ts +152 -0
- package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +288 -0
- package/src/engine/__tests__/raw-table.test.ts +2 -2
- package/src/engine/__tests__/steps-aggregate-append-event.test.ts +115 -0
- package/src/engine/__tests__/steps-aggregate-create.test.ts +92 -0
- package/src/engine/__tests__/steps-aggregate-update.test.ts +127 -0
- package/src/engine/__tests__/steps-call-feature.test.ts +123 -0
- package/src/engine/__tests__/steps-mail-send.test.ts +136 -0
- package/src/engine/__tests__/steps-read.test.ts +142 -0
- package/src/engine/__tests__/steps-resolver-utils.test.ts +50 -0
- package/src/engine/__tests__/steps-unsafe-projection-delete.test.ts +69 -0
- package/src/engine/__tests__/steps-unsafe-projection-upsert.test.ts +117 -0
- package/src/engine/__tests__/steps-webhook-send.test.ts +135 -0
- package/src/engine/__tests__/steps-workflow.test.ts +198 -0
- package/src/engine/__tests__/validate-projection-allowlist.test.ts +491 -0
- package/src/engine/__tests__/visual-tree-patterns.test.ts +251 -0
- package/src/engine/boot-validator/api-ext.ts +77 -0
- package/src/engine/boot-validator/config-deps.ts +163 -0
- package/src/engine/boot-validator/entity-handler.ts +466 -0
- package/src/engine/boot-validator/index.ts +159 -0
- package/src/engine/boot-validator/ownership.ts +198 -0
- package/src/engine/boot-validator/pii-retention.ts +155 -0
- package/src/engine/boot-validator/screens-nav.ts +624 -0
- package/src/engine/boot-validator.ts +1 -1804
- package/src/engine/build-app-schema.ts +1 -1
- package/src/engine/build-target.ts +99 -0
- package/src/engine/codemod/index.ts +15 -0
- package/src/engine/codemod/pipeline-codemod.ts +641 -0
- package/src/engine/config-helpers.ts +9 -19
- package/src/engine/constants.ts +1 -1
- package/src/engine/define-feature.ts +88 -9
- package/src/engine/define-handler.ts +89 -3
- package/src/engine/define-roles.ts +2 -2
- package/src/engine/define-step.ts +28 -0
- package/src/engine/define-workflow.ts +110 -0
- package/src/engine/entity-handlers.ts +10 -9
- package/src/engine/event-helpers.ts +4 -4
- package/src/engine/factories.ts +12 -12
- package/src/engine/feature-ast/__tests__/visual-tree-parse.test.ts +184 -0
- package/src/engine/feature-ast/extractors/index.ts +74 -0
- package/src/engine/feature-ast/extractors/round1.ts +110 -0
- package/src/engine/feature-ast/extractors/round2.ts +253 -0
- package/src/engine/feature-ast/extractors/round3.ts +471 -0
- package/src/engine/feature-ast/extractors/round4.ts +1365 -0
- package/src/engine/feature-ast/extractors/round5.ts +72 -0
- package/src/engine/feature-ast/extractors/round6.ts +66 -0
- package/src/engine/feature-ast/extractors/shared.ts +177 -0
- package/src/engine/feature-ast/parse.ts +7 -0
- package/src/engine/feature-ast/patch.ts +9 -1
- package/src/engine/feature-ast/patcher.ts +10 -3
- package/src/engine/feature-ast/patterns.ts +49 -1
- package/src/engine/feature-ast/render.ts +17 -1
- package/src/engine/index.ts +44 -2
- package/src/engine/pattern-library/__tests__/library.test.ts +6 -0
- package/src/engine/pattern-library/library.ts +42 -2
- package/src/engine/pipeline.ts +88 -0
- package/src/engine/projection-helpers.ts +1 -1
- package/src/engine/read-claim.ts +1 -1
- package/src/engine/registry.ts +30 -2
- package/src/engine/resolve-config-or-param.ts +4 -0
- package/src/engine/run-pipeline.ts +162 -0
- package/src/engine/schema-builder.ts +2 -4
- package/src/engine/state-machine.ts +1 -1
- package/src/engine/steps/_drizzle-boundary.ts +19 -0
- package/src/engine/steps/_duration-utils.ts +33 -0
- package/src/engine/steps/_no-return-guard.ts +21 -0
- package/src/engine/steps/_resolver-utils.ts +42 -0
- package/src/engine/steps/_step-dispatch-constants.ts +38 -0
- package/src/engine/steps/aggregate-append-event.ts +56 -0
- package/src/engine/steps/aggregate-create.ts +56 -0
- package/src/engine/steps/aggregate-update.ts +68 -0
- package/src/engine/steps/branch.ts +84 -0
- package/src/engine/steps/call-feature.ts +49 -0
- package/src/engine/steps/compute.ts +41 -0
- package/src/engine/steps/for-each.ts +111 -0
- package/src/engine/steps/mail-send.ts +44 -0
- package/src/engine/steps/read-find-many.ts +51 -0
- package/src/engine/steps/read-find-one.ts +58 -0
- package/src/engine/steps/retry.ts +87 -0
- package/src/engine/steps/return.ts +34 -0
- package/src/engine/steps/unsafe-projection-delete.ts +46 -0
- package/src/engine/steps/unsafe-projection-upsert.ts +69 -0
- package/src/engine/steps/wait-for-event.ts +71 -0
- package/src/engine/steps/wait.ts +69 -0
- package/src/engine/steps/webhook-send.ts +71 -0
- package/src/engine/system-user.ts +1 -1
- package/src/engine/types/feature.ts +93 -1
- package/src/engine/types/handlers.ts +18 -10
- package/src/engine/types/index.ts +11 -1
- package/src/engine/types/step.ts +334 -0
- package/src/engine/types/target-ref.ts +21 -0
- package/src/engine/types/tree-node.ts +132 -0
- package/src/engine/types/workspace.ts +7 -0
- package/src/engine/validate-projection-allowlist.ts +161 -0
- package/src/event-store/snapshot.ts +1 -1
- package/src/event-store/upcaster-dead-letter.ts +1 -1
- package/src/event-store/upcaster.ts +1 -1
- package/src/files/file-routes.ts +1 -1
- package/src/files/types.ts +2 -2
- package/src/jobs/job-runner.ts +10 -10
- package/src/lifecycle/lifecycle.ts +0 -3
- package/src/logging/index.ts +1 -0
- package/src/logging/pino-logger.ts +11 -7
- package/src/logging/utils.ts +24 -0
- package/src/observability/prometheus-meter.ts +7 -5
- package/src/pipeline/__tests__/archive-stream.integration.ts +1 -1
- package/src/pipeline/__tests__/causation-chain.integration.ts +1 -1
- package/src/pipeline/__tests__/domain-events-projections.integration.ts +3 -3
- package/src/pipeline/__tests__/event-define-event-strict.integration.ts +4 -4
- package/src/pipeline/__tests__/load-aggregate-query.integration.ts +1 -1
- package/src/pipeline/__tests__/msp-multi-hop.integration.ts +3 -3
- package/src/pipeline/__tests__/msp-rebuild.integration.ts +3 -3
- package/src/pipeline/__tests__/multi-stream-projection.integration.ts +2 -2
- package/src/pipeline/__tests__/query-projection.integration.ts +5 -5
- package/src/pipeline/append-event-core.ts +22 -6
- package/src/pipeline/dispatcher-utils.ts +188 -0
- package/src/pipeline/dispatcher.ts +63 -283
- package/src/pipeline/distributed-lock.ts +1 -1
- package/src/pipeline/entity-cache.ts +2 -2
- package/src/pipeline/event-consumer-state.ts +0 -13
- package/src/pipeline/event-dispatcher.ts +4 -4
- package/src/pipeline/index.ts +0 -2
- package/src/pipeline/lifecycle-pipeline.ts +6 -12
- package/src/pipeline/msp-rebuild.ts +5 -5
- package/src/pipeline/multi-stream-apply-context.ts +6 -7
- package/src/pipeline/projection-rebuild.ts +2 -2
- package/src/pipeline/projection-state.ts +0 -12
- package/src/rate-limit/__tests__/resolver.integration.ts +8 -4
- package/src/rate-limit/resolver.ts +1 -1
- package/src/search/in-memory-adapter.ts +1 -1
- package/src/search/meilisearch-adapter.ts +3 -3
- package/src/search/types.ts +1 -1
- package/src/secrets/leak-guard.ts +2 -2
- package/src/stack/request-helper.ts +9 -5
- package/src/stack/test-stack.ts +1 -1
- package/src/testing/handler-context.ts +4 -4
- package/src/testing/http-cookies.ts +1 -1
- package/src/time/tz-context.ts +1 -2
- package/src/ui-types/index.ts +4 -0
- package/src/engine/feature-ast/extractors.ts +0 -2602
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { CallExpression, SourceFile } from "ts-morph";
|
|
2
|
+
import { SyntaxKind } from "ts-morph";
|
|
3
|
+
import type { ExposesApiPattern, ExtendsRegistrarPattern, UsesApiPattern } from "../patterns";
|
|
4
|
+
import { sourceLocationFromNode } from "../source-location";
|
|
5
|
+
import { type ExtractOutput, fail, ok } from "./shared";
|
|
6
|
+
|
|
7
|
+
export function extractExtendsRegistrar(
|
|
8
|
+
call: CallExpression,
|
|
9
|
+
sourceFile: SourceFile,
|
|
10
|
+
): ExtractOutput<ExtendsRegistrarPattern> {
|
|
11
|
+
const args = call.getArguments();
|
|
12
|
+
const nameArg = args[0]?.asKind(SyntaxKind.StringLiteral);
|
|
13
|
+
if (!nameArg) {
|
|
14
|
+
return fail(
|
|
15
|
+
"extendsRegistrar",
|
|
16
|
+
sourceLocationFromNode(call, sourceFile),
|
|
17
|
+
"first argument must be a string literal extension name",
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
const defArg = args[1];
|
|
21
|
+
if (!defArg) {
|
|
22
|
+
return fail(
|
|
23
|
+
"extendsRegistrar",
|
|
24
|
+
sourceLocationFromNode(call, sourceFile),
|
|
25
|
+
"expected a definition argument",
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return ok({
|
|
29
|
+
kind: "extendsRegistrar",
|
|
30
|
+
source: sourceLocationFromNode(call, sourceFile),
|
|
31
|
+
extensionName: nameArg.getLiteralValue(),
|
|
32
|
+
defBody: sourceLocationFromNode(defArg, sourceFile),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function extractUsesApi(
|
|
37
|
+
call: CallExpression,
|
|
38
|
+
sourceFile: SourceFile,
|
|
39
|
+
): ExtractOutput<UsesApiPattern> {
|
|
40
|
+
const arg = call.getArguments()[0]?.asKind(SyntaxKind.StringLiteral);
|
|
41
|
+
if (!arg) {
|
|
42
|
+
return fail(
|
|
43
|
+
"usesApi",
|
|
44
|
+
sourceLocationFromNode(call, sourceFile),
|
|
45
|
+
'expected a single string-literal API name (e.g. "sessions.revokeAllForUser")',
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return ok({
|
|
49
|
+
kind: "usesApi",
|
|
50
|
+
source: sourceLocationFromNode(call, sourceFile),
|
|
51
|
+
apiName: arg.getLiteralValue(),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function extractExposesApi(
|
|
56
|
+
call: CallExpression,
|
|
57
|
+
sourceFile: SourceFile,
|
|
58
|
+
): ExtractOutput<ExposesApiPattern> {
|
|
59
|
+
const arg = call.getArguments()[0]?.asKind(SyntaxKind.StringLiteral);
|
|
60
|
+
if (!arg) {
|
|
61
|
+
return fail(
|
|
62
|
+
"exposesApi",
|
|
63
|
+
sourceLocationFromNode(call, sourceFile),
|
|
64
|
+
'expected a single string-literal API name (e.g. "sessions.revokeAllForUser")',
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return ok({
|
|
68
|
+
kind: "exposesApi",
|
|
69
|
+
source: sourceLocationFromNode(call, sourceFile),
|
|
70
|
+
apiName: arg.getLiteralValue(),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { CallExpression, SourceFile } from "ts-morph";
|
|
2
|
+
import type { TreeActionDef } from "../../types/tree-node";
|
|
3
|
+
import type { TreeActionsPattern, TreePattern } from "../patterns";
|
|
4
|
+
import { sourceLocationFromNode } from "../source-location";
|
|
5
|
+
import {
|
|
6
|
+
type ExtractOutput,
|
|
7
|
+
fail,
|
|
8
|
+
findFunctionLiteral,
|
|
9
|
+
isPlainObject,
|
|
10
|
+
ok,
|
|
11
|
+
readDataLiteralNode,
|
|
12
|
+
} from "./shared";
|
|
13
|
+
|
|
14
|
+
export function extractTreeActions(
|
|
15
|
+
call: CallExpression,
|
|
16
|
+
sourceFile: SourceFile,
|
|
17
|
+
): ExtractOutput<TreeActionsPattern> {
|
|
18
|
+
const arg = call.getArguments()[0];
|
|
19
|
+
if (!arg) {
|
|
20
|
+
return fail(
|
|
21
|
+
"treeActions",
|
|
22
|
+
sourceLocationFromNode(call, sourceFile),
|
|
23
|
+
"expected an action-map object literal as first argument",
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
const definitions = readDataLiteralNode(arg);
|
|
27
|
+
if (!isPlainObject(definitions)) {
|
|
28
|
+
return fail(
|
|
29
|
+
"treeActions",
|
|
30
|
+
sourceLocationFromNode(call, sourceFile),
|
|
31
|
+
"action-map could not be read as a plain object",
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return ok({
|
|
35
|
+
kind: "treeActions",
|
|
36
|
+
source: sourceLocationFromNode(call, sourceFile),
|
|
37
|
+
definitions: definitions as Readonly<Record<string, TreeActionDef>>,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function extractTree(
|
|
42
|
+
call: CallExpression,
|
|
43
|
+
sourceFile: SourceFile,
|
|
44
|
+
): ExtractOutput<TreePattern> {
|
|
45
|
+
const arg = call.getArguments()[0];
|
|
46
|
+
if (!arg) {
|
|
47
|
+
return fail(
|
|
48
|
+
"tree",
|
|
49
|
+
sourceLocationFromNode(call, sourceFile),
|
|
50
|
+
"expected a tree-provider function as first argument",
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
const fn = findFunctionLiteral(arg);
|
|
54
|
+
if (!fn) {
|
|
55
|
+
return fail(
|
|
56
|
+
"tree",
|
|
57
|
+
sourceLocationFromNode(call, sourceFile),
|
|
58
|
+
"first argument must be an inline arrow function or function expression",
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return ok({
|
|
62
|
+
kind: "tree",
|
|
63
|
+
source: sourceLocationFromNode(call, sourceFile),
|
|
64
|
+
providerBody: sourceLocationFromNode(fn, sourceFile),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { CallExpression, Node } from "ts-morph";
|
|
2
|
+
import { SyntaxKind } from "ts-morph";
|
|
3
|
+
import type { ParseError } from "../parse";
|
|
4
|
+
|
|
5
|
+
export type ExtractOutput<TPattern> =
|
|
6
|
+
| { readonly kind: "pattern"; readonly pattern: TPattern }
|
|
7
|
+
| { readonly kind: "error"; readonly error: ParseError };
|
|
8
|
+
|
|
9
|
+
export function ok<TPattern>(pattern: TPattern): ExtractOutput<TPattern> {
|
|
10
|
+
return { kind: "pattern", pattern };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function fail(
|
|
14
|
+
methodName: string,
|
|
15
|
+
source: ParseError["source"],
|
|
16
|
+
reason: string,
|
|
17
|
+
): { readonly kind: "error"; readonly error: ParseError } {
|
|
18
|
+
return { kind: "error", error: { methodName, source, reason } };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function readStringLiteralArgs(call: CallExpression): readonly string[] | undefined {
|
|
22
|
+
const out: string[] = [];
|
|
23
|
+
for (const arg of call.getArguments()) {
|
|
24
|
+
const literal = arg.asKind(SyntaxKind.StringLiteral);
|
|
25
|
+
if (!literal) return undefined;
|
|
26
|
+
out.push(literal.getLiteralValue());
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function readBooleanProperty(
|
|
32
|
+
objectLiteral: Node,
|
|
33
|
+
propertyName: string,
|
|
34
|
+
): boolean | undefined {
|
|
35
|
+
const obj = objectLiteral.asKind(SyntaxKind.ObjectLiteralExpression);
|
|
36
|
+
if (!obj) return undefined;
|
|
37
|
+
const prop = obj.getProperty(propertyName);
|
|
38
|
+
if (!prop) return undefined;
|
|
39
|
+
const assignment = prop.asKind(SyntaxKind.PropertyAssignment);
|
|
40
|
+
if (!assignment) return undefined;
|
|
41
|
+
const initializer = assignment.getInitializer();
|
|
42
|
+
if (!initializer) return undefined;
|
|
43
|
+
const kind = initializer.getKind();
|
|
44
|
+
if (kind === SyntaxKind.TrueKeyword) return true;
|
|
45
|
+
if (kind === SyntaxKind.FalseKeyword) return false;
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function readDataLiteralNode(node: Node): unknown {
|
|
50
|
+
const kind = node.getKind();
|
|
51
|
+
switch (kind) {
|
|
52
|
+
case SyntaxKind.StringLiteral:
|
|
53
|
+
return node.asKindOrThrow(SyntaxKind.StringLiteral).getLiteralValue();
|
|
54
|
+
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
|
55
|
+
return node.asKindOrThrow(SyntaxKind.NoSubstitutionTemplateLiteral).getLiteralValue();
|
|
56
|
+
case SyntaxKind.NumericLiteral:
|
|
57
|
+
return Number(node.asKindOrThrow(SyntaxKind.NumericLiteral).getText());
|
|
58
|
+
case SyntaxKind.TrueKeyword:
|
|
59
|
+
return true;
|
|
60
|
+
case SyntaxKind.FalseKeyword:
|
|
61
|
+
return false;
|
|
62
|
+
case SyntaxKind.NullKeyword:
|
|
63
|
+
return null;
|
|
64
|
+
case SyntaxKind.PrefixUnaryExpression: {
|
|
65
|
+
const expr = node.asKindOrThrow(SyntaxKind.PrefixUnaryExpression);
|
|
66
|
+
if (expr.getOperatorToken() !== SyntaxKind.MinusToken) return undefined;
|
|
67
|
+
const inner = readDataLiteralNode(expr.getOperand());
|
|
68
|
+
if (typeof inner !== "number") return undefined;
|
|
69
|
+
return -inner;
|
|
70
|
+
}
|
|
71
|
+
case SyntaxKind.ArrayLiteralExpression: {
|
|
72
|
+
const arr = node.asKindOrThrow(SyntaxKind.ArrayLiteralExpression);
|
|
73
|
+
const out: unknown[] = [];
|
|
74
|
+
for (const el of arr.getElements()) {
|
|
75
|
+
const value = readDataLiteralNode(el);
|
|
76
|
+
if (value === undefined) return undefined;
|
|
77
|
+
out.push(value);
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
case SyntaxKind.ObjectLiteralExpression: {
|
|
82
|
+
const obj = node.asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
|
|
83
|
+
const out: Record<string, unknown> = {};
|
|
84
|
+
for (const prop of obj.getProperties()) {
|
|
85
|
+
const propAssign = prop.asKind(SyntaxKind.PropertyAssignment);
|
|
86
|
+
if (!propAssign) return undefined;
|
|
87
|
+
const initializer = propAssign.getInitializer();
|
|
88
|
+
if (!initializer) return undefined;
|
|
89
|
+
const value = readDataLiteralNode(initializer);
|
|
90
|
+
if (value === undefined) return undefined;
|
|
91
|
+
out[readPropertyKey(propAssign)] = value;
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
case SyntaxKind.AsExpression:
|
|
96
|
+
return readDataLiteralNode(node.asKindOrThrow(SyntaxKind.AsExpression).getExpression());
|
|
97
|
+
case SyntaxKind.SatisfiesExpression:
|
|
98
|
+
return readDataLiteralNode(
|
|
99
|
+
node.asKindOrThrow(SyntaxKind.SatisfiesExpression).getExpression(),
|
|
100
|
+
);
|
|
101
|
+
case SyntaxKind.ParenthesizedExpression:
|
|
102
|
+
return readDataLiteralNode(
|
|
103
|
+
node.asKindOrThrow(SyntaxKind.ParenthesizedExpression).getExpression(),
|
|
104
|
+
);
|
|
105
|
+
default:
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
111
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function readPropertyKey(propAssign: import("ts-morph").PropertyAssignment): string {
|
|
115
|
+
const nameNode = propAssign.getNameNode();
|
|
116
|
+
const literal = nameNode.asKind(SyntaxKind.StringLiteral);
|
|
117
|
+
if (literal) return literal.getLiteralValue();
|
|
118
|
+
return propAssign.getName();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function readNameOrRef(node: Node): string | undefined {
|
|
122
|
+
const literal = node.asKind(SyntaxKind.StringLiteral);
|
|
123
|
+
if (literal) return literal.getLiteralValue();
|
|
124
|
+
const obj = readDataLiteralNode(node);
|
|
125
|
+
if (isPlainObject(obj) && typeof obj["name"] === "string") return obj["name"];
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function findFunctionLiteral(node: Node): Node | undefined {
|
|
130
|
+
if (node.getKind() === SyntaxKind.ArrowFunction) return node;
|
|
131
|
+
if (node.getKind() === SyntaxKind.FunctionExpression) return node;
|
|
132
|
+
const paren = node.asKind(SyntaxKind.ParenthesizedExpression);
|
|
133
|
+
if (paren) return findFunctionLiteral(paren.getExpression());
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function readNameOrRefOrList(node: Node): string | readonly string[] | undefined {
|
|
138
|
+
const single = readNameOrRef(node);
|
|
139
|
+
if (single) return single;
|
|
140
|
+
const arr = node.asKind(SyntaxKind.ArrayLiteralExpression);
|
|
141
|
+
if (!arr) return undefined;
|
|
142
|
+
const out: string[] = [];
|
|
143
|
+
for (const el of arr.getElements()) {
|
|
144
|
+
const name = readNameOrRef(el);
|
|
145
|
+
if (!name) return undefined;
|
|
146
|
+
out.push(name);
|
|
147
|
+
}
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function readVarargsOrArrayProp(
|
|
152
|
+
call: CallExpression,
|
|
153
|
+
arrayPropName: "features" | "keys",
|
|
154
|
+
): readonly string[] | undefined {
|
|
155
|
+
const args = call.getArguments();
|
|
156
|
+
if (args.length === 1) {
|
|
157
|
+
const obj = args[0]?.asKind(SyntaxKind.ObjectLiteralExpression);
|
|
158
|
+
if (obj) {
|
|
159
|
+
const propInit = obj
|
|
160
|
+
.getProperty(arrayPropName)
|
|
161
|
+
?.asKind(SyntaxKind.PropertyAssignment)
|
|
162
|
+
?.getInitializer();
|
|
163
|
+
if (propInit) {
|
|
164
|
+
const arr = propInit.asKind(SyntaxKind.ArrayLiteralExpression);
|
|
165
|
+
if (!arr) return undefined;
|
|
166
|
+
const out: string[] = [];
|
|
167
|
+
for (const el of arr.getElements()) {
|
|
168
|
+
const lit = el.asKind(SyntaxKind.StringLiteral);
|
|
169
|
+
if (!lit) return undefined;
|
|
170
|
+
out.push(lit.getLiteralValue());
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return readStringLiteralArgs(call);
|
|
177
|
+
}
|
|
@@ -54,6 +54,8 @@ import {
|
|
|
54
54
|
extractSystemScope,
|
|
55
55
|
extractToggleable,
|
|
56
56
|
extractTranslations,
|
|
57
|
+
extractTree,
|
|
58
|
+
extractTreeActions,
|
|
57
59
|
extractUseExtension,
|
|
58
60
|
extractUsesApi,
|
|
59
61
|
extractWorkspace,
|
|
@@ -352,6 +354,11 @@ function dispatchExtractor(
|
|
|
352
354
|
return extractUsesApi(call, sourceFile);
|
|
353
355
|
case "exposesApi":
|
|
354
356
|
return extractExposesApi(call, sourceFile);
|
|
357
|
+
// Round 6 — Visual-Tree patterns
|
|
358
|
+
case "treeActions":
|
|
359
|
+
return extractTreeActions(call, sourceFile);
|
|
360
|
+
case "tree":
|
|
361
|
+
return extractTree(call, sourceFile);
|
|
355
362
|
// Unknown method — UnknownPattern signal so Designer/AI surface it
|
|
356
363
|
// as "custom call" without losing the source location.
|
|
357
364
|
default:
|
|
@@ -81,7 +81,9 @@ export type PatternId =
|
|
|
81
81
|
| { readonly kind: "toggleable" }
|
|
82
82
|
| { readonly kind: "config" }
|
|
83
83
|
| { readonly kind: "translations" }
|
|
84
|
-
| { readonly kind: "authClaims" }
|
|
84
|
+
| { readonly kind: "authClaims" }
|
|
85
|
+
| { readonly kind: "treeActions" }
|
|
86
|
+
| { readonly kind: "tree" };
|
|
85
87
|
|
|
86
88
|
// =============================================================================
|
|
87
89
|
// Change ops — generic apply API
|
|
@@ -271,6 +273,10 @@ export const SINGLETON_KINDS: ReadonlySet<PatternId["kind"]> = new Set([
|
|
|
271
273
|
"config",
|
|
272
274
|
"translations",
|
|
273
275
|
"authClaims",
|
|
276
|
+
// Visual-Tree slots — at-most-one per feature, mirrors the registrar's
|
|
277
|
+
// only-once-guard in define-feature.ts.
|
|
278
|
+
"treeActions",
|
|
279
|
+
"tree",
|
|
274
280
|
]);
|
|
275
281
|
|
|
276
282
|
/**
|
|
@@ -323,6 +329,8 @@ function callMatchesId(call: CallExpression, id: PatternId): boolean {
|
|
|
323
329
|
case "config":
|
|
324
330
|
case "translations":
|
|
325
331
|
case "authClaims":
|
|
332
|
+
case "treeActions":
|
|
333
|
+
case "tree":
|
|
326
334
|
return true;
|
|
327
335
|
|
|
328
336
|
case "entity":
|
|
@@ -93,7 +93,7 @@ export type AddWriteHandlerArgs = {
|
|
|
93
93
|
readonly handlerSource: string;
|
|
94
94
|
readonly access?: AccessRule;
|
|
95
95
|
readonly rateLimit?: RateLimitOption;
|
|
96
|
-
readonly
|
|
96
|
+
readonly unsafeSkipTransitionGuard?: boolean;
|
|
97
97
|
};
|
|
98
98
|
|
|
99
99
|
export type AddQueryHandlerArgs = {
|
|
@@ -364,7 +364,14 @@ export function createFeaturePatcher(sourceFile: SourceFile): FeaturePatcher {
|
|
|
364
364
|
add({ kind: "screen", source: SYNTHETIC_LOC, definition, opaqueProps });
|
|
365
365
|
},
|
|
366
366
|
|
|
367
|
-
addWriteHandler({
|
|
367
|
+
addWriteHandler({
|
|
368
|
+
name,
|
|
369
|
+
schemaSource,
|
|
370
|
+
handlerSource,
|
|
371
|
+
access,
|
|
372
|
+
rateLimit,
|
|
373
|
+
unsafeSkipTransitionGuard,
|
|
374
|
+
}) {
|
|
368
375
|
add({
|
|
369
376
|
kind: "writeHandler",
|
|
370
377
|
source: SYNTHETIC_LOC,
|
|
@@ -373,7 +380,7 @@ export function createFeaturePatcher(sourceFile: SourceFile): FeaturePatcher {
|
|
|
373
380
|
handlerBody: rawLoc(handlerSource),
|
|
374
381
|
...(access !== undefined && { access }),
|
|
375
382
|
...(rateLimit !== undefined && { rateLimit }),
|
|
376
|
-
...(
|
|
383
|
+
...(unsafeSkipTransitionGuard === true && { unsafeSkipTransitionGuard: true }),
|
|
377
384
|
});
|
|
378
385
|
},
|
|
379
386
|
|
|
@@ -26,6 +26,28 @@
|
|
|
26
26
|
// **Naming convention.** Pattern `kind` matches the r.* method name
|
|
27
27
|
// 1:1 (e.g. `r.writeHandler` → `kind: "writeHandler"`). No kebab/camel
|
|
28
28
|
// translation layer.
|
|
29
|
+
//
|
|
30
|
+
// **Adding a new FeaturePattern kind — full consumer cascade.** The
|
|
31
|
+
// extension-point is wider than just this file + the parser. Update
|
|
32
|
+
// ALL of these when introducing a new r.* API, otherwise tests/checks
|
|
33
|
+
// catch the drift but the call-site jumps across files:
|
|
34
|
+
// 1. patterns.ts (this file): Pattern type + add to FeaturePattern
|
|
35
|
+
// union + getEditability switch
|
|
36
|
+
// 2. feature-ast/extractors.ts: extract<Kind> function + import in
|
|
37
|
+
// patterns-import-block
|
|
38
|
+
// 3. feature-ast/parse.ts: dispatcher case + import
|
|
39
|
+
// 4. feature-ast/render.ts: render<Kind> function + import + switch
|
|
40
|
+
// case
|
|
41
|
+
// 5. feature-ast/patch.ts: PatternId variant; if singleton-per-feature
|
|
42
|
+
// add to SINGLETON_KINDS; callMatchesId case
|
|
43
|
+
// 6. pattern-library/library.ts: <kind>Schema + entry in
|
|
44
|
+
// PATTERN_LIBRARY map
|
|
45
|
+
// 7. pattern-library/__tests__/library.test.ts: ALL_KINDS array +
|
|
46
|
+
// makePlaceholderPattern case
|
|
47
|
+
// TS-exhaustiveness catches most omissions automatically (1, 3, 4, 5,
|
|
48
|
+
// 7-via-makePlaceholderPattern), but the runtime-checked maps in 6 +
|
|
49
|
+
// the ALL_KINDS array in 7 are silent if forgotten — pin them with the
|
|
50
|
+
// library.test.ts coverage tests.
|
|
29
51
|
|
|
30
52
|
import type { LifecycleHookType } from "../constants";
|
|
31
53
|
import type {
|
|
@@ -45,6 +67,7 @@ import type { NavDefinition } from "../types/nav";
|
|
|
45
67
|
import type { MspErrorMode } from "../types/projection";
|
|
46
68
|
import type { RelationDefinition } from "../types/relations";
|
|
47
69
|
import type { ScreenDefinition } from "../types/screen";
|
|
70
|
+
import type { TreeActionDef } from "../types/tree-node";
|
|
48
71
|
import type { WorkspaceDefinition } from "../types/workspace";
|
|
49
72
|
import type { SourceLocation } from "./source-location";
|
|
50
73
|
|
|
@@ -158,6 +181,17 @@ export type UseExtensionPattern = {
|
|
|
158
181
|
readonly options?: Readonly<Record<string, unknown>>;
|
|
159
182
|
};
|
|
160
183
|
|
|
184
|
+
// r.treeActions({ ... }) — Schema-Map für Visual-Tree-Action-Verben.
|
|
185
|
+
// Static: Args sind Type-Samples (kein Runtime-Validator), Designer
|
|
186
|
+
// rendert das als nested form pro Action. Compile-Time-Validation
|
|
187
|
+
// passiert via setup-export-Handle (TreeActionsHandle), nicht über
|
|
188
|
+
// dieses Pattern — das hier ist reine Runtime-Repräsentation.
|
|
189
|
+
export type TreeActionsPattern = {
|
|
190
|
+
readonly kind: "treeActions";
|
|
191
|
+
readonly source: SourceLocation;
|
|
192
|
+
readonly definitions: Readonly<Record<string, TreeActionDef>>;
|
|
193
|
+
};
|
|
194
|
+
|
|
161
195
|
// =============================================================================
|
|
162
196
|
// Mixed patterns — header (name/access/etc.) is declarative, body
|
|
163
197
|
// (handler/hook/apply/transform fn) is opaque. Designer renders the
|
|
@@ -202,7 +236,7 @@ export type WriteHandlerPattern = {
|
|
|
202
236
|
readonly handlerBody: SourceLocation;
|
|
203
237
|
readonly access?: AccessRule;
|
|
204
238
|
readonly rateLimit?: RateLimitOption;
|
|
205
|
-
readonly
|
|
239
|
+
readonly unsafeSkipTransitionGuard?: boolean;
|
|
206
240
|
};
|
|
207
241
|
|
|
208
242
|
export type QueryHandlerPattern = {
|
|
@@ -262,6 +296,16 @@ export type AuthClaimsPattern = {
|
|
|
262
296
|
readonly fnBody: SourceLocation;
|
|
263
297
|
};
|
|
264
298
|
|
|
299
|
+
// r.tree(provider) — Top-Level-Tree-Provider-Function. Closure-only,
|
|
300
|
+
// kein Header-Form. Designer rendert als read-only Code-Block, AI-
|
|
301
|
+
// Patcher überschreibt span verbatim. Konsistent mit r.authClaims —
|
|
302
|
+
// auch da ist die Function-Body die einzige Information.
|
|
303
|
+
export type TreePattern = {
|
|
304
|
+
readonly kind: "tree";
|
|
305
|
+
readonly source: SourceLocation;
|
|
306
|
+
readonly providerBody: SourceLocation;
|
|
307
|
+
};
|
|
308
|
+
|
|
265
309
|
export type HttpRoutePattern = {
|
|
266
310
|
readonly kind: "httpRoute";
|
|
267
311
|
readonly source: SourceLocation;
|
|
@@ -374,6 +418,7 @@ export type FeaturePattern =
|
|
|
374
418
|
| UseExtensionPattern
|
|
375
419
|
| UsesApiPattern
|
|
376
420
|
| ExposesApiPattern
|
|
421
|
+
| TreeActionsPattern
|
|
377
422
|
// Mixed
|
|
378
423
|
| ScreenPattern
|
|
379
424
|
| WriteHandlerPattern
|
|
@@ -389,6 +434,7 @@ export type FeaturePattern =
|
|
|
389
434
|
| DefineEventPattern
|
|
390
435
|
| EventMigrationPattern
|
|
391
436
|
| ExtendsRegistrarPattern
|
|
437
|
+
| TreePattern
|
|
392
438
|
// Catch-all
|
|
393
439
|
| UnknownPattern;
|
|
394
440
|
|
|
@@ -428,6 +474,7 @@ export function getEditability(pattern: FeaturePattern): Editability {
|
|
|
428
474
|
case "useExtension":
|
|
429
475
|
case "usesApi":
|
|
430
476
|
case "exposesApi":
|
|
477
|
+
case "treeActions":
|
|
431
478
|
return "static";
|
|
432
479
|
case "screen":
|
|
433
480
|
case "writeHandler":
|
|
@@ -444,6 +491,7 @@ export function getEditability(pattern: FeaturePattern): Editability {
|
|
|
444
491
|
return "mixed";
|
|
445
492
|
case "authClaims":
|
|
446
493
|
case "extendsRegistrar":
|
|
494
|
+
case "tree":
|
|
447
495
|
case "unknown":
|
|
448
496
|
return "opaque";
|
|
449
497
|
default: {
|
|
@@ -46,6 +46,8 @@ import type {
|
|
|
46
46
|
SystemScopePattern,
|
|
47
47
|
ToggleablePattern,
|
|
48
48
|
TranslationsPattern,
|
|
49
|
+
TreeActionsPattern,
|
|
50
|
+
TreePattern,
|
|
49
51
|
UnknownPattern,
|
|
50
52
|
UseExtensionPattern,
|
|
51
53
|
UsesApiPattern,
|
|
@@ -128,6 +130,10 @@ export function renderPattern(pattern: FeaturePattern): string {
|
|
|
128
130
|
return renderUsesApi(pattern);
|
|
129
131
|
case "exposesApi":
|
|
130
132
|
return renderExposesApi(pattern);
|
|
133
|
+
case "treeActions":
|
|
134
|
+
return renderTreeActions(pattern);
|
|
135
|
+
case "tree":
|
|
136
|
+
return renderTree(pattern);
|
|
131
137
|
case "unknown":
|
|
132
138
|
return renderUnknown(pattern);
|
|
133
139
|
default: {
|
|
@@ -346,7 +352,7 @@ function renderWriteHandler(p: WriteHandlerPattern): string {
|
|
|
346
352
|
lines.push(` handler: ${reindentBody(p.handlerBody.raw, PATTERN_INDENT)},`);
|
|
347
353
|
if (p.access !== undefined) lines.push(` access: ${renderValue(p.access)},`);
|
|
348
354
|
if (p.rateLimit !== undefined) lines.push(` rateLimit: ${renderValue(p.rateLimit)},`);
|
|
349
|
-
if (p.
|
|
355
|
+
if (p.unsafeSkipTransitionGuard === true) lines.push(" unsafeSkipTransitionGuard: true,");
|
|
350
356
|
lines.push("});");
|
|
351
357
|
return lines.join("\n");
|
|
352
358
|
}
|
|
@@ -414,6 +420,16 @@ function renderAuthClaims(p: AuthClaimsPattern): string {
|
|
|
414
420
|
return `r.authClaims(${p.fnBody.raw});`;
|
|
415
421
|
}
|
|
416
422
|
|
|
423
|
+
// Visual-Tree patterns. treeActions is a static object-literal (mirrors
|
|
424
|
+
// renderWorkspace), tree is opaque-only (mirrors renderAuthClaims).
|
|
425
|
+
function renderTreeActions(p: TreeActionsPattern): string {
|
|
426
|
+
return `r.treeActions(${renderValue(p.definitions)});`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function renderTree(p: TreePattern): string {
|
|
430
|
+
return `r.tree(${p.providerBody.raw});`;
|
|
431
|
+
}
|
|
432
|
+
|
|
417
433
|
function renderHttpRoute(p: HttpRoutePattern): string {
|
|
418
434
|
const lines: string[] = ["r.httpRoute({"];
|
|
419
435
|
lines.push(` method: ${JSON.stringify(p.method)},`);
|
package/src/engine/index.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
export { hasAccess } from "./access";
|
|
4
4
|
export { validateBoot } from "./boot-validator";
|
|
5
5
|
export { buildAppSchema } from "./build-app-schema";
|
|
6
|
+
export { buildTarget } from "./build-target";
|
|
6
7
|
export { access, createSystemConfig, createTenantConfig, createUserConfig } from "./config-helpers";
|
|
7
8
|
export type { SystemHookName } from "./constants";
|
|
8
9
|
export {
|
|
@@ -18,9 +19,16 @@ export {
|
|
|
18
19
|
export type { App, AppConfig } from "./create-app";
|
|
19
20
|
export { createApp } from "./create-app";
|
|
20
21
|
export { defineFeature } from "./define-feature";
|
|
21
|
-
export type {
|
|
22
|
+
export type {
|
|
23
|
+
QueryHandlerDefinition,
|
|
24
|
+
WriteHandlerDefinition,
|
|
25
|
+
WriteHandlerInput,
|
|
26
|
+
} from "./define-handler";
|
|
22
27
|
export { defineQueryHandler, defineWriteHandler } from "./define-handler";
|
|
23
28
|
export { defineRoles } from "./define-roles";
|
|
29
|
+
export { defineStep, getStep, listStepKinds } from "./define-step";
|
|
30
|
+
export type { WorkflowDefinition, WorkflowInput, WorkflowTrigger } from "./define-workflow";
|
|
31
|
+
export { computeDefinitionFingerprint, defineWorkflow } from "./define-workflow";
|
|
24
32
|
export type { ToggleReader } from "./effective-features";
|
|
25
33
|
export { computeEffectiveFeatures } from "./effective-features";
|
|
26
34
|
export {
|
|
@@ -125,6 +133,7 @@ export {
|
|
|
125
133
|
} from "./field-access";
|
|
126
134
|
export type { OwnershipClause, OwnershipMap, OwnershipRef, OwnershipRule } from "./ownership";
|
|
127
135
|
export { from } from "./ownership";
|
|
136
|
+
export { buildPipelineSteps, pipeline } from "./pipeline";
|
|
128
137
|
export { defineApply, defineMspApply, setFields } from "./projection-helpers";
|
|
129
138
|
export type { BuiltinQnType, ParsedQn, QnType } from "./qualified-name";
|
|
130
139
|
export { isValidQn, parseQn, QnTypes, qn, toKebab } from "./qualified-name";
|
|
@@ -133,9 +142,22 @@ export { createRegistry } from "./registry";
|
|
|
133
142
|
export type { ClampInfo, ResolveOptions } from "./resolve-config-or-param";
|
|
134
143
|
export { resolveConfigOrParam } from "./resolve-config-or-param";
|
|
135
144
|
export { runsInLane } from "./run-in";
|
|
145
|
+
export type { StepListOutcome } from "./run-pipeline";
|
|
146
|
+
export { runPipeline, runStepList } from "./run-pipeline";
|
|
136
147
|
export { buildInsertSchema, buildUpdateSchema } from "./schema-builder";
|
|
137
148
|
export type { TransitionGraph } from "./state-machine";
|
|
138
149
|
export { defineTransitions, guardTransition } from "./state-machine";
|
|
150
|
+
export {
|
|
151
|
+
SUSPEND_SENTINEL,
|
|
152
|
+
WORKFLOW_AGGREGATE_TYPE,
|
|
153
|
+
WORKFLOW_RESUMED_TYPE,
|
|
154
|
+
WORKFLOW_RETRY_SCHEDULED_TYPE,
|
|
155
|
+
WORKFLOW_RUN_COMPLETED_TYPE,
|
|
156
|
+
WORKFLOW_RUN_FAILED_TYPE,
|
|
157
|
+
WORKFLOW_RUN_STARTED_TYPE,
|
|
158
|
+
WORKFLOW_WAITING_FOR_EVENT_TYPE,
|
|
159
|
+
WORKFLOW_WAITING_TYPE,
|
|
160
|
+
} from "./steps/_step-dispatch-constants";
|
|
139
161
|
export {
|
|
140
162
|
ANONYMOUS_ROLE,
|
|
141
163
|
ANONYMOUS_USER_ID,
|
|
@@ -157,7 +179,6 @@ export type {
|
|
|
157
179
|
AppContext,
|
|
158
180
|
AppendEventArgs,
|
|
159
181
|
AppendEventFn,
|
|
160
|
-
AppendEventUnsafeFn,
|
|
161
182
|
AuthClaimsContext,
|
|
162
183
|
AuthClaimsFn,
|
|
163
184
|
AuthClaimsHookDef,
|
|
@@ -261,11 +282,20 @@ export type {
|
|
|
261
282
|
ScreenSlots,
|
|
262
283
|
SelectFieldDef,
|
|
263
284
|
SessionUser,
|
|
285
|
+
Subscribe,
|
|
286
|
+
TargetRef,
|
|
264
287
|
TenantId,
|
|
265
288
|
TextFieldDef,
|
|
266
289
|
ToolbarAction,
|
|
267
290
|
TranslationKeys,
|
|
268
291
|
TranslationsDef,
|
|
292
|
+
TreeAction,
|
|
293
|
+
TreeActionDef,
|
|
294
|
+
TreeActionsHandle,
|
|
295
|
+
TreeChildrenSubscribe,
|
|
296
|
+
TreeNode,
|
|
297
|
+
TreeNodeState,
|
|
298
|
+
UnsafeAppendEventFn,
|
|
269
299
|
ValidationError,
|
|
270
300
|
ValidationHookFn,
|
|
271
301
|
WorkspaceDefinition,
|
|
@@ -278,4 +308,16 @@ export { DEFAULT_CURRENCIES, HookPhases } from "./types";
|
|
|
278
308
|
export { resolveName, withResponseData } from "./types/handlers";
|
|
279
309
|
export { isSystemTenant, parseTenantId, SYSTEM_TENANT_ID } from "./types/identifiers";
|
|
280
310
|
export { normalizeEditField, normalizeListColumn } from "./types/screen";
|
|
311
|
+
export type {
|
|
312
|
+
PipelineBuildCtx,
|
|
313
|
+
PipelineCtx,
|
|
314
|
+
PipelineDef,
|
|
315
|
+
StepBuilder,
|
|
316
|
+
StepDef,
|
|
317
|
+
StepFailureStrategy,
|
|
318
|
+
StepInstance,
|
|
319
|
+
StepKind,
|
|
320
|
+
StepNamespace,
|
|
321
|
+
StepResolver,
|
|
322
|
+
} from "./types/step";
|
|
281
323
|
export { runValidation } from "./validation";
|
|
@@ -57,6 +57,8 @@ const ALL_KINDS: readonly FeaturePatternKind[] = [
|
|
|
57
57
|
"extendsRegistrar",
|
|
58
58
|
"usesApi",
|
|
59
59
|
"exposesApi",
|
|
60
|
+
"treeActions",
|
|
61
|
+
"tree",
|
|
60
62
|
"unknown",
|
|
61
63
|
];
|
|
62
64
|
|
|
@@ -340,6 +342,10 @@ function makePlaceholderPattern(kind: FeaturePatternKind): FeaturePattern {
|
|
|
340
342
|
extensionName: "x",
|
|
341
343
|
defBody: PLACEHOLDER_BODY_LOC,
|
|
342
344
|
};
|
|
345
|
+
case "treeActions":
|
|
346
|
+
return { kind, source: PLACEHOLDER_LOC, definitions: {} };
|
|
347
|
+
case "tree":
|
|
348
|
+
return { kind, source: PLACEHOLDER_LOC, providerBody: PLACEHOLDER_BODY_LOC };
|
|
343
349
|
case "unknown":
|
|
344
350
|
return { kind, source: PLACEHOLDER_LOC, methodName: "x" };
|
|
345
351
|
case "usesApi":
|