@tiaforge-ui/sinter 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +132 -2
- package/dist/cache.d.ts +5 -0
- package/dist/cache.js +95 -0
- package/dist/check/index.d.ts +82 -0
- package/dist/check/index.js +156 -0
- package/dist/check/openapi-types.d.ts +6 -0
- package/dist/check/openapi-types.js +84 -0
- package/dist/check/shadow.d.ts +47 -0
- package/dist/check/shadow.js +245 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +67 -0
- package/dist/compiler/check-unit.d.ts +97 -0
- package/dist/compiler/check-unit.js +3 -0
- package/dist/compiler/compile.d.ts +62 -0
- package/dist/compiler/compile.js +1133 -0
- package/dist/compiler/diagnostic-docs.d.ts +10 -0
- package/dist/compiler/diagnostic-docs.js +137 -0
- package/dist/compiler/diagnostics.d.ts +56 -0
- package/dist/compiler/diagnostics.js +76 -0
- package/dist/compiler/expr.d.ts +89 -0
- package/dist/compiler/expr.js +838 -0
- package/dist/compiler/index.d.ts +21 -0
- package/dist/compiler/index.js +106 -0
- package/dist/compiler/openapi.d.ts +40 -0
- package/dist/compiler/openapi.js +177 -0
- package/dist/compiler/vocabulary.d.ts +12 -0
- package/dist/compiler/vocabulary.js +38 -0
- package/dist/dev.d.ts +10 -0
- package/dist/dev.js +92 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +4 -0
- package/dist/ir.d.ts +127 -0
- package/dist/ir.js +121 -0
- package/dist/lsp/quickfix.d.ts +15 -0
- package/dist/lsp/quickfix.js +58 -0
- package/dist/lsp/rpc.d.ts +31 -0
- package/dist/lsp/rpc.js +120 -0
- package/dist/lsp/server.d.ts +36 -0
- package/dist/lsp/server.js +281 -0
- package/dist/lsp/spec-location.d.ts +9 -0
- package/dist/lsp/spec-location.js +42 -0
- package/dist/lsp/typescript.d.ts +77 -0
- package/dist/lsp/typescript.js +242 -0
- package/dist/lsp/workspace.d.ts +56 -0
- package/dist/lsp/workspace.js +227 -0
- package/dist/optimistic.d.ts +3 -0
- package/dist/optimistic.js +15 -0
- package/dist/paginate.d.ts +3 -0
- package/dist/paginate.js +184 -0
- package/dist/persist.d.ts +3 -0
- package/dist/persist.js +55 -0
- package/dist/project.d.ts +36 -0
- package/dist/project.js +100 -0
- package/dist/runtime/app.d.ts +125 -0
- package/dist/runtime/app.js +1183 -0
- package/dist/runtime/config.d.ts +53 -0
- package/dist/runtime/config.js +118 -0
- package/dist/runtime/env.d.ts +18 -0
- package/dist/runtime/env.js +23 -0
- package/dist/runtime/helpers.d.ts +9 -0
- package/dist/runtime/helpers.js +55 -0
- package/dist/runtime/request.d.ts +23 -0
- package/dist/runtime/request.js +93 -0
- package/dist/runtime/security.d.ts +12 -0
- package/dist/runtime/security.js +34 -0
- package/dist/runtime/store.d.ts +95 -0
- package/dist/runtime/store.js +142 -0
- package/dist/sse.d.ts +7 -0
- package/dist/sse.js +206 -0
- package/dist/testing/index.d.ts +71 -0
- package/dist/testing/index.js +345 -0
- package/dist/vite.d.ts +11 -0
- package/dist/vite.js +94 -0
- package/dist/ws.d.ts +18 -0
- package/dist/ws.js +57 -0
- package/docs/diagnostics.md +273 -0
- package/html-custom-data.json +234 -0
- package/llms.txt +531 -0
- package/package.json +90 -4
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Code } from './diagnostics.js';
|
|
2
|
+
export interface DiagnosticDoc {
|
|
3
|
+
/** a message as the compiler prints it */
|
|
4
|
+
example: string;
|
|
5
|
+
/** what went wrong, in a sentence or two */
|
|
6
|
+
means: string;
|
|
7
|
+
/** what to change */
|
|
8
|
+
fix: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const DIAGNOSTIC_DOCS: Record<Code, DiagnosticDoc>;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
export const DIAGNOSTIC_DOCS = {
|
|
2
|
+
SN101: {
|
|
3
|
+
example: '`data-sn-txt` is not a sinter attribute. Did you mean `data-sn-text`?',
|
|
4
|
+
means: 'An attribute starts with `data-sn-` but is not one sinter knows, usually a typo.',
|
|
5
|
+
fix: 'Use the suggested name. The attributes are listed in `llms.txt` and, in VS Code, completed from `html-custom-data.json`.',
|
|
6
|
+
},
|
|
7
|
+
SN102: {
|
|
8
|
+
example: '`data-sn-for` must be on a `<template>`',
|
|
9
|
+
means: 'The attribute or element is valid, but not here: lists and conditionals render from a `<template>`, `data-sn-path` belongs on `<html>`, `<sn-event>` inside `<sn-sync>`, and options such as `data-sn-debounce` need a method attribute on the same element.',
|
|
10
|
+
fix: 'Move it to where the message says, wrapping the content in a `<template>` if needed.',
|
|
11
|
+
},
|
|
12
|
+
SN103: {
|
|
13
|
+
example: 'a `data-sn-for` over primitive values needs `data-sn-key`',
|
|
14
|
+
means: 'sinter keys list rows by the resource key or by object identity. Strings and numbers have neither, so rows could not be matched on update.',
|
|
15
|
+
fix: 'Add `data-sn-key` with an expression that is unique per row, such as the value itself: `data-sn-key="tag"`.',
|
|
16
|
+
},
|
|
17
|
+
SN104: {
|
|
18
|
+
example: '`data-sn-model` does not support `type="file"`; submit the form instead',
|
|
19
|
+
means: 'Two-way binding has no meaningful value for this input type.',
|
|
20
|
+
fix: 'Submit the form with a method attribute (`data-sn-post`, …) on it; a form with a file input is sent as `FormData`.',
|
|
21
|
+
},
|
|
22
|
+
SN201: {
|
|
23
|
+
example: '`contcts` is not defined here. Did you mean `contacts`?',
|
|
24
|
+
means: 'An expression uses a name that is not a loop variable, state field, resource, magic name, helper or JavaScript global in this scope.',
|
|
25
|
+
fix: 'Fix the typo, declare the name in `data-sn-state`, or export it from the helpers module. Loop variables are only visible inside their `<template>`.',
|
|
26
|
+
},
|
|
27
|
+
SN202: {
|
|
28
|
+
example: '`window` is a browser API; expressions may use JavaScript, not the browser. Use an island',
|
|
29
|
+
means: 'Expressions may use JavaScript built-ins but not browser APIs such as `window`, `document`, `fetch` or timers, so pages stay safe under a strict CSP and testable.',
|
|
30
|
+
fix: 'Move the browser code into a helper (for a pure function) or an island (a custom element that owns imperative work).',
|
|
31
|
+
},
|
|
32
|
+
SN203: {
|
|
33
|
+
example: 'value expressions cannot assign; move this to `data-sn-on-*`',
|
|
34
|
+
means: 'A binding that produces a value (`text`, `attr`, `show`, `for`, …) tried to change state, which would run on every render. Path parameters of `$url` are read-only everywhere.',
|
|
35
|
+
fix: 'Do the assignment in an event handler (`data-sn-on-click`) or in an action\'s `then`.',
|
|
36
|
+
},
|
|
37
|
+
SN204: {
|
|
38
|
+
example: '`data-sn-optimistic` may only call `patch`, `upsert`, `remove`',
|
|
39
|
+
means: 'An optimistic change is undone when the request fails, so it may only use the store functions that sinter can roll back.',
|
|
40
|
+
fix: 'Express the change with `patch`, `upsert` or `remove`; set other state in `then`.',
|
|
41
|
+
},
|
|
42
|
+
SN205: {
|
|
43
|
+
example: '`m.is_read = true` mutates a resource. Use `patch(\'messages\', m, {is_read: true})`',
|
|
44
|
+
means: 'Resource data is read-only in expressions: it mirrors the server, and a direct write would be lost on the next refresh or event.',
|
|
45
|
+
fix: 'Use the `patch()` call from the message, or copy the item with `structuredClone(c)` to edit it in state.',
|
|
46
|
+
},
|
|
47
|
+
SN206: {
|
|
48
|
+
example: '`m.inbox_titel` is not exported by namespace `m`. Did you mean `inbox_title`?',
|
|
49
|
+
means: 'A helper namespace (for example paraglide messages) has no export of that name.',
|
|
50
|
+
fix: 'Use the suggested name, or add the export to the namespace module.',
|
|
51
|
+
},
|
|
52
|
+
SN207: {
|
|
53
|
+
example: '`onclick` cannot be bound with `data-sn-attr`; use `data-sn-on-click`',
|
|
54
|
+
means: 'The binding could run script or inject markup from data: event-handler attributes, `style`, `srcdoc`, `innerHTML`, bindings on `<script>` and similar elements, URL templates that do not start with a literal path, or persisted password fields.',
|
|
55
|
+
fix: 'Use the safe equivalent the message names: `data-sn-on-*` for events, `data-sn-style` for styles, a literal `/path` at the start of a URL template.',
|
|
56
|
+
},
|
|
57
|
+
SN208: {
|
|
58
|
+
example: 'Property \'nmae\' does not exist on type \'Contact\'. (TS2339)',
|
|
59
|
+
means: 'TypeScript found a type error in an expression. Reported by `sinter check`, `sinter({ typecheck: true })` and the editor integration.',
|
|
60
|
+
fix: 'Fix the expression, or the type: `type` on `<sn-resource>`, the OpenAPI spec, or a JSDoc cast in `data-sn-state`.',
|
|
61
|
+
},
|
|
62
|
+
SN301: {
|
|
63
|
+
example: 'resource `mesages` is not declared on this page',
|
|
64
|
+
means: 'A store call, `$meta` or embedded data (`data-sn-data`) names a resource that no `<sn-resource>` on the page or its partials declares.',
|
|
65
|
+
fix: 'Fix the name or add the `<sn-resource>` element.',
|
|
66
|
+
},
|
|
67
|
+
SN302: {
|
|
68
|
+
example: 'resource `contacts` is declared twice',
|
|
69
|
+
means: 'Two `<sn-resource>` elements on one page share a name, often once in the page and once in a partial.',
|
|
70
|
+
fix: 'Rename one, or remove the duplicate.',
|
|
71
|
+
},
|
|
72
|
+
SN303: {
|
|
73
|
+
example: '`$meta.contacts.loadng` is not a status field. Did you mean `loading`?',
|
|
74
|
+
means: '`$meta.<resource>` has a fixed set of fields: `loading`, `error`, `loadedAt`, `pending`, `total`, and with `paginate` also `page`, `pages`, `from`, `to`, `hasPrev`, `hasNext`.',
|
|
75
|
+
fix: 'Use the suggested field. A pagination field needs `paginate` on the resource.',
|
|
76
|
+
},
|
|
77
|
+
SN304: {
|
|
78
|
+
example: 'keyset pagination cannot jump to a page number. Use `paginate="offset"`',
|
|
79
|
+
means: 'Keyset (cursor) pagination only knows the next and previous page, so `page(\'name\', 7)` has nothing to jump to.',
|
|
80
|
+
fix: 'Use `paginate="offset"` if the API supports offsets, or only step with `next`, `prev` and `first`.',
|
|
81
|
+
},
|
|
82
|
+
SN305: {
|
|
83
|
+
example: 'partial `./partials/contact-dialg.html` not found or outside the project root',
|
|
84
|
+
means: 'The `src` of `<sn-include>` must be a literal path, relative to the including file, that resolves inside the project root.',
|
|
85
|
+
fix: 'Fix the path. Partials outside the root are refused on purpose.',
|
|
86
|
+
},
|
|
87
|
+
SN306: {
|
|
88
|
+
example: '`a.html` → `b.html` → `a.html` includes itself',
|
|
89
|
+
means: 'Partials include each other in a cycle.',
|
|
90
|
+
fix: 'Move the shared part into a third partial that neither includes back.',
|
|
91
|
+
},
|
|
92
|
+
SN307: {
|
|
93
|
+
example: 'OpenAPI spec `api.yml` is not an OpenAPI 3 document (needs `openapi: 3.x` and `paths`)',
|
|
94
|
+
means: 'A spec given to `sinter({ openapi })` is missing, unreadable, or not OpenAPI 3.',
|
|
95
|
+
fix: 'Fix the path in the Vite config, or convert a Swagger 2 spec to OpenAPI 3.',
|
|
96
|
+
},
|
|
97
|
+
SN308: {
|
|
98
|
+
example: 'the response of `GET /articles` has no `articls`. Did you mean `articles`?',
|
|
99
|
+
means: 'The resource matched an operation in the OpenAPI spec, but its `items` or `total` path is not in the response schema.',
|
|
100
|
+
fix: 'Fix the path in `items` or `total`, or the spec if the API changed.',
|
|
101
|
+
},
|
|
102
|
+
SN401: {
|
|
103
|
+
example: '`data-sn-for` takes `item in list` or `(item, i) in list`',
|
|
104
|
+
means: 'An attribute value has the wrong form: a JavaScript syntax error (the column points inside the attribute), or a value outside what the attribute accepts, such as `paginate` or `data-sn-concurrency`.',
|
|
105
|
+
fix: 'Fix the value as the message says. Handlers are statements separated by `;`; value bindings are a single expression.',
|
|
106
|
+
},
|
|
107
|
+
SN501: {
|
|
108
|
+
example: 'resource `addressbooks` is declared but never read',
|
|
109
|
+
means: 'The page fetches a resource that nothing on it uses, which costs a request on every load.',
|
|
110
|
+
fix: 'Remove the `<sn-resource>`, or use it.',
|
|
111
|
+
},
|
|
112
|
+
SN502: {
|
|
113
|
+
example: '`Date.now()` in a binding never updates. Use `$now`',
|
|
114
|
+
means: 'The binding reads the time or a random number once and is never re-rendered by it.',
|
|
115
|
+
fix: 'Use `$now`, which sinter updates, or compute the value in a handler.',
|
|
116
|
+
},
|
|
117
|
+
SN503: {
|
|
118
|
+
example: '`id` bound from data can shadow globals other scripts read (DOM clobbering)',
|
|
119
|
+
means: 'An `id` or `name` set from server data can create a global that overrides one another script relies on.',
|
|
120
|
+
fix: 'Prefix the value (`\'contact-\' + c.id`) or use a `data-*` attribute instead.',
|
|
121
|
+
},
|
|
122
|
+
SN504: {
|
|
123
|
+
example: 'resource `contacts` has no type: expressions using it are not checked',
|
|
124
|
+
means: 'The type checker has no type for the resource, so expressions that use it are not checked. Reported by `sinter check` only.',
|
|
125
|
+
fix: 'Add `type="Contact[]"` with a type from `src/sinter.types.ts`, configure an OpenAPI spec that matches `src`, or write `type="any"` to opt out.',
|
|
126
|
+
},
|
|
127
|
+
SN505: {
|
|
128
|
+
example: '`GET /articles` declares no query parameter `tagg`. Did you mean `tag`?',
|
|
129
|
+
means: 'A literal query parameter in `src` is not declared by the matched OpenAPI operation, usually a typo the server would ignore.',
|
|
130
|
+
fix: 'Use the suggested name, or add the parameter to the spec.',
|
|
131
|
+
},
|
|
132
|
+
SN506: {
|
|
133
|
+
example: 'event `contact_updated` has no type: expressions reading its payload are not checked',
|
|
134
|
+
means: 'An `<sn-event>` reads its payload but has no `type`, so those expressions are not checked. Reported by `sinter check` only.',
|
|
135
|
+
fix: 'Add `type` with a payload type from `src/sinter.types.ts`, or `type="any"` to opt out.',
|
|
136
|
+
},
|
|
137
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type Severity = 'error' | 'warning';
|
|
2
|
+
export interface Diagnostic {
|
|
3
|
+
severity: Severity;
|
|
4
|
+
code: string;
|
|
5
|
+
message: string;
|
|
6
|
+
file: string;
|
|
7
|
+
line: number;
|
|
8
|
+
col: number;
|
|
9
|
+
hint?: string;
|
|
10
|
+
/** what an editor underlines, when it is more than the name at `line:col` (a whole attribute, a tag name) */
|
|
11
|
+
span?: {
|
|
12
|
+
line: number;
|
|
13
|
+
col: number;
|
|
14
|
+
endLine: number;
|
|
15
|
+
endCol: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare const CODES: {
|
|
19
|
+
readonly SN101: 'unknown-attribute';
|
|
20
|
+
readonly SN102: 'wrong-element';
|
|
21
|
+
readonly SN103: 'missing-key';
|
|
22
|
+
readonly SN104: 'unsupported-model';
|
|
23
|
+
readonly SN201: 'unknown-identifier';
|
|
24
|
+
readonly SN202: 'forbidden-global';
|
|
25
|
+
readonly SN203: 'side-effect';
|
|
26
|
+
readonly SN204: 'optimistic-scope';
|
|
27
|
+
readonly SN205: 'resource-mutation';
|
|
28
|
+
readonly SN206: 'unknown-namespace-member';
|
|
29
|
+
readonly SN207: 'unsafe-sink';
|
|
30
|
+
readonly SN208: 'type-error';
|
|
31
|
+
readonly SN301: 'unknown-resource';
|
|
32
|
+
readonly SN302: 'duplicate-resource';
|
|
33
|
+
readonly SN303: 'unknown-meta-field';
|
|
34
|
+
readonly SN304: 'keyset-jump';
|
|
35
|
+
readonly SN305: 'include-not-found';
|
|
36
|
+
readonly SN306: 'include-cycle';
|
|
37
|
+
readonly SN307: 'openapi-invalid';
|
|
38
|
+
readonly SN308: 'unknown-schema-field';
|
|
39
|
+
readonly SN401: 'parse-error';
|
|
40
|
+
readonly SN501: 'unused-resource';
|
|
41
|
+
readonly SN502: 'non-reactive-value';
|
|
42
|
+
readonly SN503: 'data-bound-id';
|
|
43
|
+
readonly SN504: 'untyped-resource';
|
|
44
|
+
readonly SN505: 'unknown-query-param';
|
|
45
|
+
readonly SN506: 'untyped-event';
|
|
46
|
+
};
|
|
47
|
+
export type Code = keyof typeof CODES;
|
|
48
|
+
export declare const DOCS_URL = "https://github.com/tiaforge/sinter/blob/main/docs/diagnostics.md";
|
|
49
|
+
/** Link to a code's section of `docs/diagnostics.md` (GitHub's heading anchor). */
|
|
50
|
+
export declare function docsUrl(code: string): string;
|
|
51
|
+
export declare function severityOf(code: Code): Severity;
|
|
52
|
+
/** Formats a diagnostic the way the terminal and the testing module print it. */
|
|
53
|
+
export declare function formatDiagnostic(d: Diagnostic): string;
|
|
54
|
+
/** The closest candidate within a small edit distance, for "did you mean" messages. */
|
|
55
|
+
export declare function suggest(name: string, candidates: Iterable<string>): string | undefined;
|
|
56
|
+
export declare function didYouMean(name: string, candidates: Iterable<string>): string;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const CODES = {
|
|
2
|
+
SN101: 'unknown-attribute',
|
|
3
|
+
SN102: 'wrong-element',
|
|
4
|
+
SN103: 'missing-key',
|
|
5
|
+
SN104: 'unsupported-model',
|
|
6
|
+
SN201: 'unknown-identifier',
|
|
7
|
+
SN202: 'forbidden-global',
|
|
8
|
+
SN203: 'side-effect',
|
|
9
|
+
SN204: 'optimistic-scope',
|
|
10
|
+
SN205: 'resource-mutation',
|
|
11
|
+
SN206: 'unknown-namespace-member',
|
|
12
|
+
SN207: 'unsafe-sink',
|
|
13
|
+
SN208: 'type-error',
|
|
14
|
+
SN301: 'unknown-resource',
|
|
15
|
+
SN302: 'duplicate-resource',
|
|
16
|
+
SN303: 'unknown-meta-field',
|
|
17
|
+
SN304: 'keyset-jump',
|
|
18
|
+
SN305: 'include-not-found',
|
|
19
|
+
SN306: 'include-cycle',
|
|
20
|
+
SN307: 'openapi-invalid',
|
|
21
|
+
SN308: 'unknown-schema-field',
|
|
22
|
+
SN401: 'parse-error',
|
|
23
|
+
SN501: 'unused-resource',
|
|
24
|
+
SN502: 'non-reactive-value',
|
|
25
|
+
SN503: 'data-bound-id',
|
|
26
|
+
SN504: 'untyped-resource',
|
|
27
|
+
SN505: 'unknown-query-param',
|
|
28
|
+
SN506: 'untyped-event',
|
|
29
|
+
};
|
|
30
|
+
export const DOCS_URL = 'https://github.com/tiaforge/sinter/blob/main/docs/diagnostics.md';
|
|
31
|
+
/** Link to a code's section of `docs/diagnostics.md` (GitHub's heading anchor). */
|
|
32
|
+
export function docsUrl(code) {
|
|
33
|
+
const name = CODES[code];
|
|
34
|
+
return `${DOCS_URL}#${code.toLowerCase()}${name ? `-${name}` : ''}`;
|
|
35
|
+
}
|
|
36
|
+
export function severityOf(code) {
|
|
37
|
+
return code >= 'SN500' ? 'warning' : 'error';
|
|
38
|
+
}
|
|
39
|
+
/** Formats a diagnostic the way the terminal and the testing module print it. */
|
|
40
|
+
export function formatDiagnostic(d) {
|
|
41
|
+
const name = CODES[d.code] ?? '';
|
|
42
|
+
let s = `${d.file}:${d.line}:${d.col} ${d.severity} ${d.code} ${name}: ${d.message}`;
|
|
43
|
+
if (d.hint)
|
|
44
|
+
s += `\n hint: ${d.hint}`;
|
|
45
|
+
return s + `\n docs: ${docsUrl(d.code)}`;
|
|
46
|
+
}
|
|
47
|
+
function distance(a, b) {
|
|
48
|
+
const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
49
|
+
for (let i = 1; i <= a.length; i++) {
|
|
50
|
+
let diag = prev[0];
|
|
51
|
+
prev[0] = i;
|
|
52
|
+
for (let j = 1; j <= b.length; j++) {
|
|
53
|
+
const tmp = prev[j];
|
|
54
|
+
prev[j] = Math.min(prev[j] + 1, prev[j - 1] + 1, diag + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
55
|
+
diag = tmp;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return prev[b.length];
|
|
59
|
+
}
|
|
60
|
+
/** The closest candidate within a small edit distance, for "did you mean" messages. */
|
|
61
|
+
export function suggest(name, candidates) {
|
|
62
|
+
let best;
|
|
63
|
+
let bestD = Math.max(2, Math.floor(name.length / 3)) + 1;
|
|
64
|
+
for (const c of candidates) {
|
|
65
|
+
const d = distance(name.toLowerCase(), c.toLowerCase());
|
|
66
|
+
if (d < bestD) {
|
|
67
|
+
best = c;
|
|
68
|
+
bestD = d;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return best;
|
|
72
|
+
}
|
|
73
|
+
export function didYouMean(name, candidates) {
|
|
74
|
+
const s = suggest(name, candidates);
|
|
75
|
+
return s ? ` Did you mean \`${s}\`?` : '';
|
|
76
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as acorn from 'acorn';
|
|
2
|
+
import { type Code } from './diagnostics.js';
|
|
3
|
+
type Node = acorn.AnyNode & {
|
|
4
|
+
start: number;
|
|
5
|
+
end: number;
|
|
6
|
+
};
|
|
7
|
+
export type Mode = 'value' | 'handler' | 'optimistic';
|
|
8
|
+
export interface LoopEntry {
|
|
9
|
+
name: string;
|
|
10
|
+
kind: 'item' | 'index';
|
|
11
|
+
/** item over a state path: its path with the instance index (`$s1.draft.emails.{$i3}`) */
|
|
12
|
+
alias: string[] | null;
|
|
13
|
+
/** item rooted at a resource */
|
|
14
|
+
resource: string | null;
|
|
15
|
+
/** index: the item's alias, so `list[i]` over the same list becomes the row's own element */
|
|
16
|
+
of?: string[] | null;
|
|
17
|
+
}
|
|
18
|
+
export interface StateEntry {
|
|
19
|
+
sid: number;
|
|
20
|
+
names: Set<string>;
|
|
21
|
+
}
|
|
22
|
+
export interface ResourceInfo {
|
|
23
|
+
name: string;
|
|
24
|
+
key: string[] | null;
|
|
25
|
+
paginate: string | null;
|
|
26
|
+
used: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface Scope {
|
|
29
|
+
loops: LoopEntry[];
|
|
30
|
+
states: StateEntry[];
|
|
31
|
+
}
|
|
32
|
+
export interface PageInfo {
|
|
33
|
+
resources: Map<string, ResourceInfo>;
|
|
34
|
+
helpers: Set<string>;
|
|
35
|
+
namespaces: Map<string, Set<string>>;
|
|
36
|
+
/** imports used by emitted code */
|
|
37
|
+
usedHelpers: Set<string>;
|
|
38
|
+
usedNs: Map<string, Set<string>>;
|
|
39
|
+
usesNow: boolean;
|
|
40
|
+
/** `:name` segments of the page's `data-sn-path` patterns */
|
|
41
|
+
pathParams: Set<string>;
|
|
42
|
+
}
|
|
43
|
+
export interface ExprOptions {
|
|
44
|
+
mode: Mode;
|
|
45
|
+
/** magic names allowed here besides the everywhere ones */
|
|
46
|
+
magic?: string[];
|
|
47
|
+
/** free identifiers resolve to fields of `$.$p` (sn-event, cursor) */
|
|
48
|
+
payload?: boolean;
|
|
49
|
+
/** parse as `{...}` object literal; returns its entries */
|
|
50
|
+
object?: 'literal-keys' | 'any-keys' | 'state';
|
|
51
|
+
report: (code: Code, message: string, offset: number, hint?: string) => void;
|
|
52
|
+
}
|
|
53
|
+
export interface ObjectEntry {
|
|
54
|
+
key: string;
|
|
55
|
+
code: string;
|
|
56
|
+
deps: string[];
|
|
57
|
+
offset: number;
|
|
58
|
+
literal: boolean;
|
|
59
|
+
}
|
|
60
|
+
export interface ExprResult {
|
|
61
|
+
ok: boolean;
|
|
62
|
+
code: string;
|
|
63
|
+
deps: string[];
|
|
64
|
+
writes: string[];
|
|
65
|
+
usesEl: boolean;
|
|
66
|
+
/** reads a loop index variable */
|
|
67
|
+
usesIndex: boolean;
|
|
68
|
+
entries?: ObjectEntry[];
|
|
69
|
+
/** the expression is a plain member path */
|
|
70
|
+
path: string[] | null;
|
|
71
|
+
/** resource the expression is rooted at */
|
|
72
|
+
resource: string | null;
|
|
73
|
+
ast: Node | null;
|
|
74
|
+
/** names resolved to payload fields (`payload` mode) */
|
|
75
|
+
payloadNames?: string[];
|
|
76
|
+
}
|
|
77
|
+
export declare const ALL_MAGIC: string[];
|
|
78
|
+
export declare const STORE_FNS: string[];
|
|
79
|
+
export declare const BUILTIN_HELPERS: string[];
|
|
80
|
+
export declare const META_FIELDS: string[];
|
|
81
|
+
export declare const GLOBALS: Set<string>;
|
|
82
|
+
interface Edit {
|
|
83
|
+
start: number;
|
|
84
|
+
end: number;
|
|
85
|
+
text: string;
|
|
86
|
+
}
|
|
87
|
+
export declare function analyze(src: string, scope: Scope, page: PageInfo, opts: ExprOptions): ExprResult;
|
|
88
|
+
export declare function applyEdits(src: string, edits: Edit[]): string;
|
|
89
|
+
export {};
|