@octaviaflow/flow-rules 0.7.0 → 0.8.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.
@@ -0,0 +1,92 @@
1
+ /**
2
+ * ONE answer to "what does this step's config look like?"
3
+ *
4
+ * THE PROBLEM THIS EXISTS TO END. Every action carried its shape in two places
5
+ * that never spoke to each other:
6
+ *
7
+ * index.ts `inputSchema.fields` — what `describe_action_type` reports to an
8
+ * agent and what the config fence enforces;
9
+ * config.ts `XxxConfig.schema` + `validate()` — what the panel actually binds
10
+ * and what the platform's own validation checks.
11
+ *
12
+ * `index.ts` never imported `config.ts`, so they drifted. Measured:
13
+ * - `transform` declared 2 input fields; its panel binds 13.
14
+ * - `variables` declared 0; the real key is `fields` — an agent guessed
15
+ * `variables`, was told "no fields defined", and gave up on the action.
16
+ * - `slack_message` declared `message`; the panel stores `text`; the engine
17
+ * accepts either — so an agent wrote `message`, Slack received it, and the
18
+ * panel showed an empty box. Its own `config.ts` had said all along:
19
+ * "Canonical message body: `text`. Legacy alias: `message`."
20
+ * - On beta, 71 of 131 real config keys were undeclared.
21
+ *
22
+ * The agent was fed the stale half of the catalog and did exactly what it was
23
+ * told. So this module makes `config.ts` the source of truth: every consumer
24
+ * that needs a step's shape — the fence, `describe_action_type`, the editor —
25
+ * reads `contractFieldsFor(definition)`, which is `inputSchema` (kept for its
26
+ * labels, descriptions and option lists) UNIONED with everything the contract
27
+ * declares. A key the contract knows can no longer be "undeclared".
28
+ *
29
+ * And `contractDrift()` is a TEST: any `inputSchema` key the contract does not
30
+ * recognise fails the build. That is what would have caught `message`.
31
+ */
32
+ import type { ActionDefinition, FieldConfig } from "../catalog/types";
33
+ /** The vocabulary the `config.ts` schema maps already use. Not extended. */
34
+ export type ContractFieldType = "string" | "text" | "select" | "json" | "connection" | "number" | "boolean" | "array" | "object" | "url" | "expression" | "code" | "none";
35
+ /**
36
+ * A field as the `config.ts` maps actually write it. `type` is the vocabulary
37
+ * above, but the maps are plain object literals (so TypeScript sees `string`),
38
+ * and several carry `description`/`min` for the panel. The vocabulary is
39
+ * enforced at derivation — an unknown type becomes a String field and is
40
+ * flagged by `contractVocabularyDrift` — rather than by making 22 files cast.
41
+ */
42
+ export interface ContractField {
43
+ type: string;
44
+ required?: boolean;
45
+ options?: ReadonlyArray<string | number>;
46
+ [extra: string]: unknown;
47
+ }
48
+ export interface ConfigContract {
49
+ schema: Readonly<Record<string, ContractField>>;
50
+ /** The platform's own check, exactly as the panel runs it. */
51
+ validate?: (config: Record<string, unknown>) => {
52
+ valid: boolean;
53
+ errors: string[];
54
+ };
55
+ }
56
+ /**
57
+ * Every action that has a `config.ts`. Keyed by action id.
58
+ *
59
+ * Fourteen actions have no contract yet (group, ai_agent, idp_extract, the
60
+ * file-format and storage actions, connector_trigger). For those,
61
+ * `contractFor` returns undefined and consumers fall back to `inputSchema`
62
+ * alone — which is exactly what they did before, so nothing regresses. Adding a
63
+ * contract for one of them is a `config.ts` plus one line here.
64
+ */
65
+ export declare const CONFIG_CONTRACTS: Readonly<Record<string, ConfigContract>>;
66
+ export declare function contractFor(actionId: string | undefined): ConfigContract | undefined;
67
+ /** Keys the contract declares — `none`-typed placeholders excluded. */
68
+ export declare function declaredKeys(actionId: string | undefined): string[];
69
+ /**
70
+ * Keys the EDITOR persists for itself — `_outputSchema`, `_lastOutput`,
71
+ * `_outputSchemaHash`, `sampledColumnSchemas`-style caches. They are derived,
72
+ * never authored, and a value in one of them is a snapshot the editor will
73
+ * overwrite. Nothing outside the editor should write them, and an agent that
74
+ * does is writing a lie the next open will erase.
75
+ */
76
+ export declare function isEditorPrivateKey(key: string): boolean;
77
+ /**
78
+ * What a step ACCEPTS: `inputSchema.fields` — kept, because it carries labels,
79
+ * descriptions and option lists worth showing — unioned with every contract
80
+ * key it forgot. The single thing the fence, `describe_action_type` and the
81
+ * editor should read.
82
+ */
83
+ export declare function contractFieldsFor(definition: ActionDefinition): FieldConfig[];
84
+ /**
85
+ * `inputSchema` keys the contract does NOT know. A non-empty result is a bug:
86
+ * `describe_action_type` is advertising a key the platform does not store under
87
+ * that name. `slack_message.message` was one. The test over the whole catalog
88
+ * fails on any of these, so it cannot happen quietly again.
89
+ */
90
+ export declare function contractDrift(definition: ActionDefinition): string[];
91
+ /** Contract fields whose `type` is not in the vocabulary — a typo, not a new kind. */
92
+ export declare function contractVocabularyDrift(actionId: string): string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octaviaflow/flow-rules",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "The flow action catalog and the rules engine behind Flow Doctor \u2014 one definition of what a step accepts and what makes a flow valid, shared by the editor and the server",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",