@xanots/xanoscript 0.0.1

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,249 @@
1
+ interface KindValue {
2
+ getKind(): string;
3
+ getType(): string;
4
+ getName(): string | null;
5
+ toString(): string;
6
+ }
7
+
8
+ interface Kind {
9
+ getKind(): string;
10
+ getLabels(): string[];
11
+ getType(): string;
12
+ getSchemaType(): unknown;
13
+ isNullable(): boolean;
14
+ parse(data: unknown): KindValue;
15
+ }
16
+
17
+ /**
18
+ * The mutable render flags the engine keeps in CoroutineState / statics.
19
+ *
20
+ * The PHP encoder reads a handful of process-wide switches while rendering:
21
+ * verbose (legacy `!var "x"` tags), the object-literal indent counter, the
22
+ * quote/multiline modes the parser honours, the forced-multiline switch, the
23
+ * pipe registry currently in effect, and the transform context stack. One
24
+ * emit call owns this state for its duration; `withRenderState` snapshots and
25
+ * restores it so nested or sequential emits never leak flags into each other.
26
+ */
27
+ type PipeMode = "pipes" | "filters" | "aggregates";
28
+ interface RenderState {
29
+ /** the engine's `isVerbose()` — legacy tagged rendering. Production output is minimal. */
30
+ verbose: boolean;
31
+ /** `Script.INDEX` — nesting depth while wrapping object/array literals. */
32
+ index: number;
33
+ /** `Parser.QUOTE_MODE` — force a quote character for text (tests only). */
34
+ quoteMode: '"' | "'" | null;
35
+ /** `Parser.QUOTE_MULTILINE` — allow `"""` blocks. */
36
+ multilineQuotes: boolean;
37
+ /** `Parser.TICK_MULTILINE` — allow ```` ``` ```` blocks. */
38
+ multilineTicks: boolean;
39
+ /** `MultiLineValue.FORCE` — always emit block syntax for multi-line text. */
40
+ multilineForce: boolean;
41
+ /** `Transform::$PIPE_MODE` — which registry resolves pipe display names. */
42
+ pipeMode: PipeMode;
43
+ /** `Transform::$CONTEXT` — the object being encoded (mocks resolve test names off it). */
44
+ contextStack: unknown[];
45
+ }
46
+
47
+ /** the engine — export-time switches. */
48
+ type Feature = "guid" | "workspaceEnv" | "tableItems";
49
+ /** A name → id map, or a lazy loader for one. */
50
+ type NameMap = Record<string, unknown> | (() => Record<string, unknown>);
51
+ /** The id → name maps the emitter resolves references through. */
52
+ interface ScriptMaps {
53
+ function?: Record<string, unknown>;
54
+ workflowTest?: Record<string, unknown>;
55
+ app?: Record<string, unknown>;
56
+ /** Keyed `name|verb|app.id`. */
57
+ query?: Record<string, unknown>;
58
+ dbo?: Record<string, unknown>;
59
+ addon?: Record<string, unknown>;
60
+ task?: Record<string, unknown>;
61
+ trigger?: Record<string, unknown>;
62
+ tool?: Record<string, unknown>;
63
+ toolset?: Record<string, unknown>;
64
+ middleware?: Record<string, unknown>;
65
+ /** v1 realtime channels, keyed by pattern. */
66
+ channel?: Record<string, unknown>;
67
+ realtimeServer?: Record<string, unknown>;
68
+ /** v2 channels, keyed `server|path`. */
69
+ v2Channel?: Record<string, unknown>;
70
+ /** Keyed `name|channel.id`. */
71
+ message?: Record<string, unknown>;
72
+ action?: Record<string, unknown>;
73
+ actionPackage?: Record<string, unknown>;
74
+ }
75
+ declare class ScriptContext {
76
+ private kinds;
77
+ private aliases;
78
+ private features;
79
+ private maps;
80
+ /** Every reference a render could not name — the diagnostic `compile --format xs` reports. */
81
+ private unresolved;
82
+ private readonly pipes;
83
+ constructor();
84
+ private registerKindAssign;
85
+ private registerKindStatic;
86
+ private registerKindSchema;
87
+ registerKind(kind: Kind): void;
88
+ setAlias(kind: string, alias: string): void;
89
+ getAlias(alias: string): string | null;
90
+ getKind(kind: string): Kind;
91
+ /** Kind names carrying ALL the given labels, in registration order. */
92
+ getKinds(labels?: string[] | string): string[];
93
+ getFeature(feature: Feature): boolean;
94
+ setFeature(feature: Feature, value: boolean): void;
95
+ setFeatures(features: Record<Feature, boolean>): void;
96
+ registerMap(name: keyof ScriptMaps, map: NameMap): void;
97
+ /**
98
+ * Per-map reverse index (`String(id or alias)` → first name carrying it),
99
+ * built on first lookup and dropped when the map is replaced. A multidoc
100
+ * resolves one reference per statement against maps holding every object of
101
+ * the workspace, so the plain scan is O(references × objects); the index is
102
+ * the same answer in O(1) for the string/number ids every map carries. The
103
+ * loose scan remains the fallback for the rest ("05" is 5 to the engine).
104
+ */
105
+ private reverse;
106
+ private reverseIndex;
107
+ /** The map key whose value (or alias) equals `id`, by index first, then by the engine's loose compare. */
108
+ private keyForId;
109
+ registerMaps(maps: ScriptMaps): void;
110
+ getMap(name: keyof ScriptMaps): Record<string, unknown>;
111
+ /** The references no map could name since the last call, oldest first; clears the record. */
112
+ takeUnresolved(): Array<{
113
+ label: string;
114
+ id: unknown;
115
+ }>;
116
+ /**
117
+ * A map value is the object's id, or a list of aliases for it: an SDK bundle
118
+ * references objects by guid while a pulled workspace uses numeric ids, so a
119
+ * row that carries both registers both and resolves from either.
120
+ */
121
+ private idToName;
122
+ mapIdToFunctionName(id: unknown): string;
123
+ mapIdToWorkflowTestName(id: unknown): string;
124
+ mapIdToAppName(id: unknown, doThrow?: boolean): string;
125
+ mapIdToQuery(id: unknown): string;
126
+ parseQuery(query: string): {
127
+ name: string;
128
+ verb: string;
129
+ appId: string;
130
+ };
131
+ mapIdToDboName(id: unknown, doThrow?: boolean): string;
132
+ mapIdToAddonName(id: unknown): string;
133
+ mapIdToTaskName(id: unknown): string;
134
+ mapIdToTriggerName(id: unknown): string;
135
+ mapIdToToolName(id: unknown): string;
136
+ mapIdToToolsetName(id: unknown, doThrow?: boolean): string;
137
+ mapIdToMiddlewareName(id: unknown): string;
138
+ mapIdToChannelName(id: unknown, doThrow?: boolean): string;
139
+ mapIdToRealtimeServerName(id: unknown, doThrow?: boolean): string;
140
+ v2ChannelMapKey(serverName: unknown, channelPath: unknown): string;
141
+ mapIdToV2ChannelName(id: unknown, doThrow?: boolean): string;
142
+ mapIdToV2ChannelServerName(id: unknown, doThrow?: boolean): string;
143
+ actionNameOf(id: unknown): string;
144
+ actionPackageNameOf(id: unknown): string;
145
+ /** `setV2Channel`: key by `{serverName}|{path}` resolving the server through the server map. */
146
+ setV2Channel(map: Record<string, unknown>, item: {
147
+ id: unknown;
148
+ name: string;
149
+ server?: {
150
+ id?: unknown;
151
+ };
152
+ }): void;
153
+ getPipeDisplay(mode: PipeMode, name: string): string | null;
154
+ getPipeInternalNames(mode: PipeMode): string[];
155
+ }
156
+
157
+ type Row = Record<string, unknown>;
158
+ interface MultidocSections {
159
+ workspace?: Row;
160
+ dbo?: Row[];
161
+ function?: Row[];
162
+ middleware?: Row[];
163
+ app?: Row[];
164
+ query?: Row[];
165
+ task?: Row[];
166
+ addon?: Row[];
167
+ toolset?: Row[];
168
+ tool?: Row[];
169
+ trigger?: Row[];
170
+ workflow_test?: Row[];
171
+ realtime_server?: Row[];
172
+ channel?: Row[];
173
+ message?: Row[];
174
+ microservice?: Row[];
175
+ [key: string]: unknown;
176
+ }
177
+ interface MultidocOptions {
178
+ /** Which document groups to emit; everything defaults to on. */
179
+ include?: Partial<Record<"table" | "function" | "query" | "task" | "addon" | "middleware" | "ai" | "action" | "workflow_test" | "trigger" | "realtime" | "realtime_v2" | "microservice", boolean>>;
180
+ /** Engine export features: `guid` lines, workspace `env`, table `items` (records). */
181
+ features?: Partial<Record<Feature, boolean>>;
182
+ /** Extra name → id entries laid over the maps derived from the payload (a reference to an object outside it). */
183
+ maps?: ScriptMaps;
184
+ /** Reuse a prepared context. */
185
+ context?: ScriptContext;
186
+ }
187
+ declare function emitWorkspaceMultidoc(input: MultidocSections | {
188
+ payload: MultidocSections;
189
+ }, options?: MultidocOptions): string;
190
+
191
+ /** A document's header facts, as the CLI reads them off the text. */
192
+ interface ParsedDocument {
193
+ apiGroup?: string;
194
+ canonical?: string;
195
+ /** Owning channel path for realtime v2 `message` documents. */
196
+ channel?: string;
197
+ content: string;
198
+ guid?: string;
199
+ name: string;
200
+ /** Owning realtime_server name for realtime v2 `channel` documents. */
201
+ server?: string;
202
+ type: string;
203
+ verb?: string;
204
+ }
205
+ /** A document's resolved location relative to the tree root, plus its content. */
206
+ interface PlacedDocument {
207
+ content: string;
208
+ /** POSIX-style path relative to the root, e.g. `api/pdf/documents_GET.xs`. */
209
+ relPath: string;
210
+ }
211
+ /**
212
+ * Parse one document's type, name and the references its placement needs.
213
+ * Leading `//` comment lines are skipped to find the declaration.
214
+ */
215
+ declare function parseDocument(content: string): ParsedDocument | null;
216
+ /** Split a multidoc into parsed documents, skipping empties and unparseable fragments. */
217
+ declare function splitMultidoc(blob: string): ParsedDocument[];
218
+ /**
219
+ * Where each document of a batch lands: `resolveDocumentPath` plus the `.xs`
220
+ * extension, with a `_N` suffix on a second document that would take a name
221
+ * already used in the same directory (`say.xs`, then `say_2.xs`).
222
+ */
223
+ declare function placeDocuments(documents: ParsedDocument[]): PlacedDocument[];
224
+ /** A multidoc as the files `xano workspace pull` would write for it. */
225
+ declare function placeMultidoc(multidoc: string): PlacedDocument[];
226
+ /**
227
+ * The top-level directories the layout can write into, derived from the
228
+ * placement rules themselves (each type placed with and without the references
229
+ * that change its home), so a writer that owns the tree prunes every directory
230
+ * a document could have landed in.
231
+ */
232
+ declare function layoutRoots(): string[];
233
+
234
+ interface EmitOptions {
235
+ /** id → name maps for every cross-reference the object makes. */
236
+ maps?: ScriptMaps;
237
+ /** Engine export features (`guid` emits guid lines, etc.). */
238
+ features?: Partial<Record<Feature, boolean>>;
239
+ /** Render flags; the defaults are what the engine's export path uses. */
240
+ render?: Partial<Omit<RenderState, "index" | "contextStack" | "pipeMode">>;
241
+ /** Reuse a prepared context (its maps stay registered across calls). */
242
+ context?: ScriptContext;
243
+ }
244
+ /** A context with the vendored engine kinds loaded; cached because the registry is large. */
245
+ declare function createScriptContext(maps?: ScriptMaps, features?: Partial<Record<Feature, boolean>>): ScriptContext;
246
+ /** Render one object (`schema:function`, `schema:query`, a statement kind, …) to XanoScript. */
247
+ declare function emitXanoScript(kind: string, data: unknown, options?: EmitOptions): string;
248
+
249
+ export { type EmitOptions, type Feature, type MultidocOptions, type MultidocSections, type ParsedDocument, type PlacedDocument, ScriptContext, type ScriptMaps, createScriptContext, emitWorkspaceMultidoc, emitXanoScript, layoutRoots, parseDocument, placeDocuments, placeMultidoc, splitMultidoc };
package/dist/index.js ADDED
@@ -0,0 +1,67 @@
1
+ import {
2
+ ScriptContext,
3
+ emitWorkspaceMultidoc,
4
+ layoutRoots,
5
+ parseDocument,
6
+ placeDocuments,
7
+ placeMultidoc,
8
+ processConvert,
9
+ splitMultidoc,
10
+ withRenderState,
11
+ withScriptContext
12
+ } from "./chunk-677U72GF.js";
13
+
14
+ // src/index.ts
15
+ var shared = null;
16
+ function createScriptContext(maps, features) {
17
+ const ctx = new ScriptContext();
18
+ if (maps) ctx.registerMaps(maps);
19
+ if (features) {
20
+ for (const [k, v] of Object.entries(features)) ctx.setFeature(k, Boolean(v));
21
+ }
22
+ return ctx;
23
+ }
24
+ function contextFor(o) {
25
+ if (o.context) {
26
+ if (o.maps) o.context.registerMaps(o.maps);
27
+ if (o.features) o.context.setFeatures({ guid: false, workspaceEnv: false, tableItems: false, ...o.features });
28
+ return o.context;
29
+ }
30
+ if (o.maps) return createScriptContext(o.maps, { guid: false, workspaceEnv: false, tableItems: false, ...o.features ?? {} });
31
+ if (!shared) shared = new ScriptContext();
32
+ shared.setFeatures({ guid: false, workspaceEnv: false, tableItems: false, ...o.features ?? {} });
33
+ return shared;
34
+ }
35
+ function emitXanoScript(kind, data, options = {}) {
36
+ const ctx = contextFor(options);
37
+ const out = withScriptContext(
38
+ ctx,
39
+ () => withRenderState(
40
+ {
41
+ verbose: false,
42
+ quoteMode: null,
43
+ multilineQuotes: true,
44
+ multilineTicks: true,
45
+ multilineForce: false,
46
+ pipeMode: "pipes",
47
+ index: 0,
48
+ ...options.render ?? {}
49
+ },
50
+ () => processConvert(kind, data).output
51
+ )
52
+ );
53
+ if (!options.context) ctx.takeUnresolved();
54
+ return out;
55
+ }
56
+ export {
57
+ ScriptContext,
58
+ createScriptContext,
59
+ emitWorkspaceMultidoc,
60
+ emitXanoScript,
61
+ layoutRoots,
62
+ parseDocument,
63
+ placeDocuments,
64
+ placeMultidoc,
65
+ splitMultidoc
66
+ };
67
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,30 @@
1
+ import { ToolchainPlugin } from '@xanots/sdk/plugin';
2
+
3
+ /** The range as `package.json` spells it, so one edit cannot move without the other. */
4
+ declare const PEER_RANGE = ">=0.0.29 <0.1.0";
5
+ /**
6
+ * Refuse, by name and before any work, an SDK this module does not understand.
7
+ *
8
+ * The install-time peer warning cannot carry this: the peer is declared
9
+ * non-optional precisely so npm prints one, but a warning during `npm install`
10
+ * is read by nobody three weeks later, and the failure it describes is SILENT —
11
+ * an older SDK has no toolchain loader at all, so the hook simply never fires
12
+ * and the tree quietly stops being written. A hook that DID fire on a version
13
+ * outside the range is the case this catches: the shapes it is handed are not
14
+ * the shapes it was written against.
15
+ *
16
+ * An unparsable version, or one carrying a PRERELEASE suffix, is allowed
17
+ * through. The SDK reads the version from its own `package.json`, and a source
18
+ * checkout reads `0.0.0-dev` — refusing that would break exactly the linked
19
+ * development setup KTD5 asks for. The cost is that a prerelease of an
20
+ * out-of-range SDK is not caught, which is the trade npm's own ranges make.
21
+ */
22
+ declare function assertSdkVersion(sdkVersion: string): void;
23
+ /**
24
+ * The default export the SDK's loader reads. `kind` is repeated from the
25
+ * manifest so the two must agree — a mismatch means one of them is stale, and
26
+ * guessing which would run code the manifest did not describe.
27
+ */
28
+ declare const plugin: ToolchainPlugin;
29
+
30
+ export { PEER_RANGE, assertSdkVersion, plugin as default };