@astrale-os/sdk 0.4.12 → 0.4.14
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/cli/run.d.ts +8 -2
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +51 -1
- package/dist/cli/run.js.map +1 -1
- package/dist/define/remote-function.d.ts +3 -1
- package/dist/define/remote-function.d.ts.map +1 -1
- package/dist/define/remote-function.js.map +1 -1
- package/dist/dispatch/dispatcher.d.ts +10 -20
- package/dist/dispatch/dispatcher.d.ts.map +1 -1
- package/dist/dispatch/dispatcher.js +55 -54
- package/dist/dispatch/dispatcher.js.map +1 -1
- package/dist/dispatch/execute.d.ts +2 -1
- package/dist/dispatch/execute.d.ts.map +1 -1
- package/dist/dispatch/execute.js +1 -0
- package/dist/dispatch/execute.js.map +1 -1
- package/dist/domain/extend-functions.d.ts.map +1 -1
- package/dist/domain/extend-functions.js +2 -1
- package/dist/domain/extend-functions.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/linter/analyze.d.ts +6 -0
- package/dist/linter/analyze.d.ts.map +1 -0
- package/dist/linter/analyze.js +12 -0
- package/dist/linter/analyze.js.map +1 -0
- package/dist/linter/diagnostic.d.ts +32 -0
- package/dist/linter/diagnostic.d.ts.map +1 -0
- package/dist/linter/diagnostic.js +35 -0
- package/dist/linter/diagnostic.js.map +1 -0
- package/dist/linter/index.d.ts +10 -0
- package/dist/linter/index.d.ts.map +1 -0
- package/dist/linter/index.js +6 -0
- package/dist/linter/index.js.map +1 -0
- package/dist/linter/lint.d.ts +8 -0
- package/dist/linter/lint.d.ts.map +1 -0
- package/dist/linter/lint.js +26 -0
- package/dist/linter/lint.js.map +1 -0
- package/dist/linter/oxlint/config.d.ts +14 -0
- package/dist/linter/oxlint/config.d.ts.map +1 -0
- package/dist/linter/oxlint/config.js +18 -0
- package/dist/linter/oxlint/config.js.map +1 -0
- package/dist/linter/oxlint/plugin.d.ts +13 -0
- package/dist/linter/oxlint/plugin.d.ts.map +1 -0
- package/dist/linter/oxlint/plugin.js +14 -0
- package/dist/linter/oxlint/plugin.js.map +1 -0
- package/dist/linter/oxlint/run.d.ts +7 -0
- package/dist/linter/oxlint/run.d.ts.map +1 -0
- package/dist/linter/oxlint/run.js +122 -0
- package/dist/linter/oxlint/run.js.map +1 -0
- package/dist/linter/preflight.d.ts +2 -0
- package/dist/linter/preflight.d.ts.map +1 -0
- package/dist/linter/preflight.js +162 -0
- package/dist/linter/preflight.js.map +1 -0
- package/dist/linter/project.d.ts +18 -0
- package/dist/linter/project.d.ts.map +1 -0
- package/dist/linter/project.js +150 -0
- package/dist/linter/project.js.map +1 -0
- package/dist/linter/report.d.ts +4 -0
- package/dist/linter/report.d.ts.map +1 -0
- package/dist/linter/report.js +22 -0
- package/dist/linter/report.js.map +1 -0
- package/dist/linter/rules/catalog.d.ts +56 -0
- package/dist/linter/rules/catalog.d.ts.map +1 -0
- package/dist/linter/rules/catalog.js +53 -0
- package/dist/linter/rules/catalog.js.map +1 -0
- package/dist/linter/rules/core-has-no-async.d.ts +3 -0
- package/dist/linter/rules/core-has-no-async.d.ts.map +1 -0
- package/dist/linter/rules/core-has-no-async.js +43 -0
- package/dist/linter/rules/core-has-no-async.js.map +1 -0
- package/dist/linter/rules/core-is-pure.d.ts +3 -0
- package/dist/linter/rules/core-is-pure.d.ts.map +1 -0
- package/dist/linter/rules/core-is-pure.js +74 -0
- package/dist/linter/rules/core-is-pure.js.map +1 -0
- package/dist/linter/rules/no-nested-steps.d.ts +3 -0
- package/dist/linter/rules/no-nested-steps.d.ts.map +1 -0
- package/dist/linter/rules/no-nested-steps.js +32 -0
- package/dist/linter/rules/no-nested-steps.js.map +1 -0
- package/dist/linter/rules/no-read-inside-mutate.d.ts +3 -0
- package/dist/linter/rules/no-read-inside-mutate.d.ts.map +1 -0
- package/dist/linter/rules/no-read-inside-mutate.js +64 -0
- package/dist/linter/rules/no-read-inside-mutate.js.map +1 -0
- package/dist/linter/rules/one-remote-definition-per-file.d.ts +3 -0
- package/dist/linter/rules/one-remote-definition-per-file.d.ts.map +1 -0
- package/dist/linter/rules/one-remote-definition-per-file.js +23 -0
- package/dist/linter/rules/one-remote-definition-per-file.js.map +1 -0
- package/dist/linter/rules/oxlint-ast.d.ts +30 -0
- package/dist/linter/rules/oxlint-ast.d.ts.map +1 -0
- package/dist/linter/rules/oxlint-ast.js +104 -0
- package/dist/linter/rules/oxlint-ast.js.map +1 -0
- package/dist/linter/rules/oxlint-rule.d.ts +45 -0
- package/dist/linter/rules/oxlint-rule.d.ts.map +1 -0
- package/dist/linter/rules/oxlint-rule.js +2 -0
- package/dist/linter/rules/oxlint-rule.js.map +1 -0
- package/dist/linter/rules/project-rule.d.ts +10 -0
- package/dist/linter/rules/project-rule.d.ts.map +1 -0
- package/dist/linter/rules/project-rule.js +24 -0
- package/dist/linter/rules/project-rule.js.map +1 -0
- package/dist/linter/rules/thin-schema-composition-root.d.ts +3 -0
- package/dist/linter/rules/thin-schema-composition-root.d.ts.map +1 -0
- package/dist/linter/rules/thin-schema-composition-root.js +23 -0
- package/dist/linter/rules/thin-schema-composition-root.js.map +1 -0
- package/dist/linter/source.d.ts +24 -0
- package/dist/linter/source.d.ts.map +1 -0
- package/dist/linter/source.js +279 -0
- package/dist/linter/source.js.map +1 -0
- package/dist/linter/suppression.d.ts +3 -0
- package/dist/linter/suppression.d.ts.map +1 -0
- package/dist/linter/suppression.js +11 -0
- package/dist/linter/suppression.js.map +1 -0
- package/dist/method/context.d.ts +3 -1
- package/dist/method/context.d.ts.map +1 -1
- package/dist/server/auxiliary-routes.d.ts +5 -1
- package/dist/server/auxiliary-routes.d.ts.map +1 -1
- package/dist/server/auxiliary-routes.js +19 -10
- package/dist/server/auxiliary-routes.js.map +1 -1
- package/dist/server/create.js +1 -1
- package/dist/server/create.js.map +1 -1
- package/dist/server/index.d.ts +4 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/service-entry.d.ts +32 -0
- package/dist/server/service-entry.d.ts.map +1 -0
- package/dist/server/service-entry.js +150 -0
- package/dist/server/service-entry.js.map +1 -0
- package/dist/server/start.d.ts.map +1 -1
- package/dist/server/start.js.map +1 -1
- package/dist/server/worker-entry.d.ts +7 -3
- package/dist/server/worker-entry.d.ts.map +1 -1
- package/dist/server/worker-entry.js +7 -1
- package/dist/server/worker-entry.js.map +1 -1
- package/dist/service/functions.d.ts +148 -0
- package/dist/service/functions.d.ts.map +1 -0
- package/dist/service/functions.js +115 -0
- package/dist/service/functions.js.map +1 -0
- package/dist/service/index.d.ts +3 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/service/index.js +2 -0
- package/dist/service/index.js.map +1 -0
- package/package.json +27 -7
- package/src/cli/run.ts +53 -2
- package/src/define/remote-function.ts +3 -1
- package/src/dispatch/dispatcher.ts +62 -69
- package/src/dispatch/execute.ts +3 -1
- package/src/domain/extend-functions.ts +5 -1
- package/src/index.ts +20 -2
- package/src/linter/analyze.ts +17 -0
- package/src/linter/diagnostic.ts +68 -0
- package/src/linter/docs/DIAGNOSTICS.md +47 -0
- package/src/linter/docs/JUDGMENT.md +81 -0
- package/src/linter/docs/PRINCIPLES.md +97 -0
- package/src/linter/docs/README.md +38 -0
- package/src/linter/docs/RULE-AUTHORING.md +144 -0
- package/src/linter/docs/RULES.md +153 -0
- package/src/linter/index.ts +14 -0
- package/src/linter/lint.ts +32 -0
- package/src/linter/oxlint/astrale-ox.d.ts +5 -0
- package/src/linter/oxlint/config.ts +19 -0
- package/src/linter/oxlint/plugin.js +16 -0
- package/src/linter/oxlint/run.ts +162 -0
- package/src/linter/preflight.ts +181 -0
- package/src/linter/project.ts +190 -0
- package/src/linter/report.ts +25 -0
- package/src/linter/rules/catalog.ts +67 -0
- package/src/linter/rules/core-has-no-async.js +43 -0
- package/src/linter/rules/core-is-pure.ts +103 -0
- package/src/linter/rules/no-nested-steps.js +37 -0
- package/src/linter/rules/no-read-inside-mutate.js +72 -0
- package/src/linter/rules/one-remote-definition-per-file.ts +29 -0
- package/src/linter/rules/oxlint-ast.js +110 -0
- package/src/linter/rules/oxlint-rule.ts +35 -0
- package/src/linter/rules/project-rule.ts +37 -0
- package/src/linter/rules/thin-schema-composition-root.ts +26 -0
- package/src/linter/source.ts +323 -0
- package/src/linter/suppression.ts +11 -0
- package/src/method/context.ts +3 -1
- package/src/server/auxiliary-routes.ts +43 -11
- package/src/server/create.ts +1 -1
- package/src/server/index.ts +10 -2
- package/src/server/service-entry.ts +209 -0
- package/src/server/start.ts +2 -1
- package/src/server/worker-entry.ts +18 -5
- package/src/service/functions.ts +164 -0
- package/src/service/index.ts +18 -0
- package/dist/defer/index.d.ts +0 -24
- package/dist/defer/index.d.ts.map +0 -1
- package/dist/defer/index.js +0 -42
- package/dist/defer/index.js.map +0 -1
- package/src/defer/index.ts +0 -64
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
export type SourceToken = {
|
|
2
|
+
kind: 'identifier' | 'string' | 'punctuation'
|
|
3
|
+
value: string
|
|
4
|
+
offset: number
|
|
5
|
+
length: number
|
|
6
|
+
line: number
|
|
7
|
+
column: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type SourceImport = {
|
|
11
|
+
specifier: string
|
|
12
|
+
token: SourceToken
|
|
13
|
+
bindings: ReadonlyMap<string, string>
|
|
14
|
+
namespace?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type SourceFile = {
|
|
18
|
+
path: string
|
|
19
|
+
relativePath: string
|
|
20
|
+
text: string
|
|
21
|
+
tokens: readonly SourceToken[]
|
|
22
|
+
imports: readonly SourceImport[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const identifierStart = /[A-Za-z_$]/
|
|
26
|
+
const identifierPart = /[A-Za-z0-9_$]/
|
|
27
|
+
|
|
28
|
+
export function scanSource(path: string, relativePath: string, text: string): SourceFile {
|
|
29
|
+
const tokens = tokenize(text)
|
|
30
|
+
return { path, relativePath, text, tokens, imports: parseImports(tokens) }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function tokenize(text: string): SourceToken[] {
|
|
34
|
+
const tokens: SourceToken[] = []
|
|
35
|
+
let offset = 0
|
|
36
|
+
let line = 1
|
|
37
|
+
let column = 1
|
|
38
|
+
|
|
39
|
+
const advance = (): string => {
|
|
40
|
+
const char = text[offset++]!
|
|
41
|
+
if (char === '\n') {
|
|
42
|
+
line++
|
|
43
|
+
column = 1
|
|
44
|
+
} else {
|
|
45
|
+
column++
|
|
46
|
+
}
|
|
47
|
+
return char
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
while (offset < text.length) {
|
|
51
|
+
const char = text[offset]!
|
|
52
|
+
if (/\s/.test(char)) {
|
|
53
|
+
advance()
|
|
54
|
+
continue
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (char === '/' && text[offset + 1] === '/') {
|
|
58
|
+
advance()
|
|
59
|
+
advance()
|
|
60
|
+
while (offset < text.length && text[offset] !== '\n') advance()
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (char === '/' && text[offset + 1] === '*') {
|
|
65
|
+
advance()
|
|
66
|
+
advance()
|
|
67
|
+
while (offset < text.length) {
|
|
68
|
+
if (text[offset] === '*' && text[offset + 1] === '/') {
|
|
69
|
+
advance()
|
|
70
|
+
advance()
|
|
71
|
+
break
|
|
72
|
+
}
|
|
73
|
+
advance()
|
|
74
|
+
}
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (char === "'" || char === '"') {
|
|
79
|
+
const quote = char
|
|
80
|
+
const start = offset
|
|
81
|
+
const startLine = line
|
|
82
|
+
const startColumn = column
|
|
83
|
+
advance()
|
|
84
|
+
let value = ''
|
|
85
|
+
while (offset < text.length) {
|
|
86
|
+
const current = advance()
|
|
87
|
+
if (current === '\\') {
|
|
88
|
+
if (offset < text.length) value += decodeEscape(advance())
|
|
89
|
+
continue
|
|
90
|
+
}
|
|
91
|
+
if (current === quote) break
|
|
92
|
+
value += current
|
|
93
|
+
}
|
|
94
|
+
tokens.push({
|
|
95
|
+
kind: 'string',
|
|
96
|
+
value,
|
|
97
|
+
offset: start,
|
|
98
|
+
length: offset - start,
|
|
99
|
+
line: startLine,
|
|
100
|
+
column: startColumn,
|
|
101
|
+
})
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (char === '`') {
|
|
106
|
+
advance()
|
|
107
|
+
while (offset < text.length) {
|
|
108
|
+
const current = advance()
|
|
109
|
+
if (current === '\\') {
|
|
110
|
+
if (offset < text.length) advance()
|
|
111
|
+
continue
|
|
112
|
+
}
|
|
113
|
+
if (current === '`') break
|
|
114
|
+
}
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (identifierStart.test(char)) {
|
|
119
|
+
const start = offset
|
|
120
|
+
const startLine = line
|
|
121
|
+
const startColumn = column
|
|
122
|
+
let value = advance()
|
|
123
|
+
while (offset < text.length && identifierPart.test(text[offset]!)) value += advance()
|
|
124
|
+
tokens.push({
|
|
125
|
+
kind: 'identifier',
|
|
126
|
+
value,
|
|
127
|
+
offset: start,
|
|
128
|
+
length: offset - start,
|
|
129
|
+
line: startLine,
|
|
130
|
+
column: startColumn,
|
|
131
|
+
})
|
|
132
|
+
continue
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
tokens.push({
|
|
136
|
+
kind: 'punctuation',
|
|
137
|
+
value: char,
|
|
138
|
+
offset,
|
|
139
|
+
length: 1,
|
|
140
|
+
line,
|
|
141
|
+
column,
|
|
142
|
+
})
|
|
143
|
+
advance()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return tokens
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function decodeEscape(char: string): string {
|
|
150
|
+
switch (char) {
|
|
151
|
+
case 'n':
|
|
152
|
+
return '\n'
|
|
153
|
+
case 'r':
|
|
154
|
+
return '\r'
|
|
155
|
+
case 't':
|
|
156
|
+
return '\t'
|
|
157
|
+
default:
|
|
158
|
+
return char
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function parseImports(tokens: readonly SourceToken[]): SourceImport[] {
|
|
163
|
+
const imports: SourceImport[] = []
|
|
164
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
165
|
+
const token = tokens[index]!
|
|
166
|
+
if (token.kind !== 'identifier' || (token.value !== 'import' && token.value !== 'export')) {
|
|
167
|
+
continue
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (token.value === 'import' && tokens[index + 1]?.value === '(') {
|
|
171
|
+
const specifier = tokens[index + 2]
|
|
172
|
+
if (specifier?.kind === 'string') {
|
|
173
|
+
imports.push({ specifier: specifier.value, token, bindings: new Map() })
|
|
174
|
+
}
|
|
175
|
+
continue
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const end = statementEnd(tokens, index + 1)
|
|
179
|
+
let specifierIndex = -1
|
|
180
|
+
for (let cursor = index + 1; cursor < end; cursor++) {
|
|
181
|
+
const candidate = tokens[cursor]!
|
|
182
|
+
if (candidate.kind === 'string') specifierIndex = cursor
|
|
183
|
+
}
|
|
184
|
+
if (specifierIndex < 0) continue
|
|
185
|
+
|
|
186
|
+
const specifier = tokens[specifierIndex]!
|
|
187
|
+
const bindings = new Map<string, string>()
|
|
188
|
+
let namespace: string | undefined
|
|
189
|
+
if (token.value === 'import') {
|
|
190
|
+
const fromIndex = findToken(tokens, index + 1, specifierIndex, 'from')
|
|
191
|
+
const bindingsEnd = fromIndex >= 0 ? fromIndex : specifierIndex
|
|
192
|
+
const open = findToken(tokens, index + 1, bindingsEnd, '{')
|
|
193
|
+
if (open >= 0) {
|
|
194
|
+
const close = findMatching(tokens, open, '{', '}', bindingsEnd)
|
|
195
|
+
if (close >= 0) parseNamedBindings(tokens, open + 1, close, bindings)
|
|
196
|
+
}
|
|
197
|
+
const star = findToken(tokens, index + 1, bindingsEnd, '*')
|
|
198
|
+
if (star >= 0 && tokens[star + 1]?.value === 'as') namespace = tokens[star + 2]?.value
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
imports.push({
|
|
202
|
+
specifier: specifier.value,
|
|
203
|
+
token,
|
|
204
|
+
bindings,
|
|
205
|
+
...(namespace ? { namespace } : {}),
|
|
206
|
+
})
|
|
207
|
+
index = Math.max(index, specifierIndex)
|
|
208
|
+
}
|
|
209
|
+
return imports
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function statementEnd(tokens: readonly SourceToken[], start: number): number {
|
|
213
|
+
let depth = 0
|
|
214
|
+
let sawSpecifier = false
|
|
215
|
+
for (let index = start; index < tokens.length; index++) {
|
|
216
|
+
const token = tokens[index]!
|
|
217
|
+
if (token.value === ';' && depth === 0) return index
|
|
218
|
+
if (token.value === '{' || token.value === '(' || token.value === '[') depth++
|
|
219
|
+
if (token.value === '}' || token.value === ')' || token.value === ']') depth--
|
|
220
|
+
if (token.kind === 'string' && depth === 0) sawSpecifier = true
|
|
221
|
+
const next = tokens[index + 1]
|
|
222
|
+
if (sawSpecifier && depth === 0 && next && next.line > token.line) return index + 1
|
|
223
|
+
}
|
|
224
|
+
return tokens.length
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function findToken(
|
|
228
|
+
tokens: readonly SourceToken[],
|
|
229
|
+
start: number,
|
|
230
|
+
end: number,
|
|
231
|
+
value: string,
|
|
232
|
+
): number {
|
|
233
|
+
for (let index = start; index < end; index++) {
|
|
234
|
+
if (tokens[index]?.value === value) return index
|
|
235
|
+
}
|
|
236
|
+
return -1
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function findMatching(
|
|
240
|
+
tokens: readonly SourceToken[],
|
|
241
|
+
start: number,
|
|
242
|
+
open: string,
|
|
243
|
+
close: string,
|
|
244
|
+
end: number,
|
|
245
|
+
): number {
|
|
246
|
+
let depth = 0
|
|
247
|
+
for (let index = start; index < end; index++) {
|
|
248
|
+
if (tokens[index]?.value === open) depth++
|
|
249
|
+
if (tokens[index]?.value === close && --depth === 0) return index
|
|
250
|
+
}
|
|
251
|
+
return -1
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function parseNamedBindings(
|
|
255
|
+
tokens: readonly SourceToken[],
|
|
256
|
+
start: number,
|
|
257
|
+
end: number,
|
|
258
|
+
bindings: Map<string, string>,
|
|
259
|
+
): void {
|
|
260
|
+
let index = start
|
|
261
|
+
while (index < end) {
|
|
262
|
+
if (tokens[index]?.value === ',' || tokens[index]?.value === 'type') {
|
|
263
|
+
index++
|
|
264
|
+
continue
|
|
265
|
+
}
|
|
266
|
+
const imported = tokens[index]
|
|
267
|
+
if (!imported || imported.kind !== 'identifier') {
|
|
268
|
+
index++
|
|
269
|
+
continue
|
|
270
|
+
}
|
|
271
|
+
if (tokens[index + 1]?.value === 'as' && tokens[index + 2]?.kind === 'identifier') {
|
|
272
|
+
bindings.set(imported.value, tokens[index + 2]!.value)
|
|
273
|
+
index += 3
|
|
274
|
+
continue
|
|
275
|
+
}
|
|
276
|
+
bindings.set(imported.value, imported.value)
|
|
277
|
+
index++
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function importedCalls(
|
|
282
|
+
file: SourceFile,
|
|
283
|
+
modules: ReadonlySet<string>,
|
|
284
|
+
names: ReadonlySet<string>,
|
|
285
|
+
): SourceToken[] {
|
|
286
|
+
const locals = new Set<string>()
|
|
287
|
+
const namespaces = new Set<string>()
|
|
288
|
+
for (const sourceImport of file.imports) {
|
|
289
|
+
if (!modules.has(sourceImport.specifier)) continue
|
|
290
|
+
for (const name of names) {
|
|
291
|
+
const local = sourceImport.bindings.get(name)
|
|
292
|
+
if (local) locals.add(local)
|
|
293
|
+
}
|
|
294
|
+
if (sourceImport.namespace) namespaces.add(sourceImport.namespace)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const calls: SourceToken[] = []
|
|
298
|
+
for (let index = 0; index < file.tokens.length; index++) {
|
|
299
|
+
const token = file.tokens[index]!
|
|
300
|
+
if (token.kind !== 'identifier') continue
|
|
301
|
+
if (locals.has(token.value) && callFollows(file.tokens, index)) {
|
|
302
|
+
calls.push(token)
|
|
303
|
+
continue
|
|
304
|
+
}
|
|
305
|
+
if (
|
|
306
|
+
namespaces.has(token.value) &&
|
|
307
|
+
file.tokens[index + 1]?.value === '.' &&
|
|
308
|
+
names.has(file.tokens[index + 2]?.value ?? '') &&
|
|
309
|
+
callFollows(file.tokens, index + 2)
|
|
310
|
+
) {
|
|
311
|
+
calls.push(file.tokens[index + 2]!)
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return calls
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function callFollows(tokens: readonly SourceToken[], index: number): boolean {
|
|
318
|
+
const next = tokens[index + 1]
|
|
319
|
+
if (next?.value === '(') return true
|
|
320
|
+
if (next?.value !== '<') return false
|
|
321
|
+
const close = findMatching(tokens, index + 1, '<', '>', tokens.length)
|
|
322
|
+
return close >= 0 && tokens[close + 1]?.value === '('
|
|
323
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SourceFile } from './source.js'
|
|
2
|
+
|
|
3
|
+
const suppression = /^\s*\/\/\s*astrale-disable-next-line\s+(.+?)\s+--\s+\S.*$/
|
|
4
|
+
|
|
5
|
+
export function isSuppressed(file: SourceFile, line: number, id: string): boolean {
|
|
6
|
+
if (line <= 1) return false
|
|
7
|
+
const previous = file.text.split(/\r?\n/)[line - 2]
|
|
8
|
+
const match = previous ? suppression.exec(previous) : null
|
|
9
|
+
if (!match) return false
|
|
10
|
+
return match[1]!.split(/[\s,]+/).includes(id)
|
|
11
|
+
}
|
package/src/method/context.ts
CHANGED
|
@@ -13,10 +13,10 @@ import type { FnMap, SchemaBoundView } from '@astrale-os/kernel-client'
|
|
|
13
13
|
import type { BoundClientSessionView } from '@astrale-os/kernel-client/session'
|
|
14
14
|
import type { AuthContext } from '@astrale-os/kernel-core'
|
|
15
15
|
import type { Schema } from '@astrale-os/kernel-dsl'
|
|
16
|
+
import type { Defer, Sleep } from '@astrale-os/kernel-server'
|
|
16
17
|
|
|
17
18
|
import type { FunctionContextApi } from '../auth/function-context.js'
|
|
18
19
|
import type { DomainAuthority } from '../auth/issuer-mint.js'
|
|
19
|
-
import type { Defer } from '../defer/index.js'
|
|
20
20
|
import type { Step } from '../step/index.js'
|
|
21
21
|
|
|
22
22
|
export type { GraphApi } from '@astrale-os/kernel-client/graph'
|
|
@@ -130,4 +130,6 @@ export interface RemoteContext<
|
|
|
130
130
|
step: Step
|
|
131
131
|
/** Run independent work without making its completion part of this result. */
|
|
132
132
|
defer: Defer
|
|
133
|
+
/** Pause execution for a relative duration. Durable hosts may persist the wait. */
|
|
134
|
+
sleep: Sleep
|
|
133
135
|
}
|
|
@@ -36,7 +36,12 @@ import {
|
|
|
36
36
|
type FunctionBinding,
|
|
37
37
|
type AuthPolicy,
|
|
38
38
|
} from '@astrale-os/kernel-api/routed'
|
|
39
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
buildCorsHeaders,
|
|
41
|
+
createCapabilityLifecycle,
|
|
42
|
+
honoCapabilityHost,
|
|
43
|
+
type CorsConfig,
|
|
44
|
+
} from '@astrale-os/kernel-server'
|
|
40
45
|
|
|
41
46
|
import type { RemoteIdentityConfig } from '../auth/identity.js'
|
|
42
47
|
import type { AnyRemoteFunctionDef } from '../define/remote-function.js'
|
|
@@ -46,7 +51,6 @@ import type { AuxIdentityMap } from '../dispatch/identity.js'
|
|
|
46
51
|
import { makeFunctionContext } from '../auth/function-context.js'
|
|
47
52
|
import { makeDomainAuthority } from '../auth/issuer-mint.js'
|
|
48
53
|
import { resolveInboundAuth } from '../auth/resolve.js'
|
|
49
|
-
import { createHandlerLifecycle, executionLifetime } from '../defer/index.js'
|
|
50
54
|
import { runAuthorize } from '../dispatch/authorize.js'
|
|
51
55
|
import { SdkResultValidationError, SdkValidationError } from '../dispatch/errors.js'
|
|
52
56
|
import { validateParams, validateResult } from '../dispatch/validate.js'
|
|
@@ -66,7 +70,13 @@ export type AuxiliaryRoutesConfig<TDeps> = {
|
|
|
66
70
|
* one per RemoteFunction. Build via `buildAuxIdentityMap(compiled, key, issuer)`
|
|
67
71
|
* from `sdk/src/dispatch/identity.ts`.
|
|
68
72
|
*/
|
|
69
|
-
identities
|
|
73
|
+
identities?: AuxIdentityMap
|
|
74
|
+
/** Resolve an identity lazily. Service-hosted Functions use this to read the
|
|
75
|
+
* Function node registered during deploy and reuse the Service signing key. */
|
|
76
|
+
resolveIdentity?: (
|
|
77
|
+
kind: 'view' | 'remoteFunction',
|
|
78
|
+
slug: string,
|
|
79
|
+
) => RemoteIdentityConfig | Promise<RemoteIdentityConfig>
|
|
70
80
|
/**
|
|
71
81
|
* CORS policy applied to every mounted route: per-route `app.options(...)`
|
|
72
82
|
* preflight and `Access-Control-Allow-*` headers on success + error
|
|
@@ -86,6 +96,7 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
|
|
|
86
96
|
remoteFunctionBindings,
|
|
87
97
|
deps,
|
|
88
98
|
identities,
|
|
99
|
+
resolveIdentity,
|
|
89
100
|
cors,
|
|
90
101
|
} = config
|
|
91
102
|
|
|
@@ -96,7 +107,7 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
|
|
|
96
107
|
for (const [slug, def] of Object.entries(views)) {
|
|
97
108
|
const binding = viewBindings[slug]
|
|
98
109
|
if (!binding || !def.render) continue
|
|
99
|
-
const identity =
|
|
110
|
+
const identity = identityResolver('view', slug, identities?.views[slug], resolveIdentity)
|
|
100
111
|
mountEntry({
|
|
101
112
|
app,
|
|
102
113
|
binding,
|
|
@@ -108,7 +119,7 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
|
|
|
108
119
|
// Views are transport-only (iframe HTML/redirect). SERVER-rendered
|
|
109
120
|
// views that read the graph use `ctx.fn.kernel()`: the view's own
|
|
110
121
|
// identity, with narrow grants.
|
|
111
|
-
run: async ({ c, params, auth, kernel }) => {
|
|
122
|
+
run: async ({ c, params, auth, kernel, identity }) => {
|
|
112
123
|
const inboundIss = auth?.credential?.verified?.iss as string | undefined
|
|
113
124
|
const kernelUrl = kernel?.default ?? inboundIss
|
|
114
125
|
const ctx = {
|
|
@@ -133,7 +144,12 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
|
|
|
133
144
|
for (const [slug, def] of Object.entries(remoteFunctions)) {
|
|
134
145
|
const binding = remoteFunctionBindings[slug]
|
|
135
146
|
if (!binding) continue
|
|
136
|
-
const identity =
|
|
147
|
+
const identity = identityResolver(
|
|
148
|
+
'remoteFunction',
|
|
149
|
+
slug,
|
|
150
|
+
identities?.remoteFunctions[slug],
|
|
151
|
+
resolveIdentity,
|
|
152
|
+
)
|
|
137
153
|
mountEntry({
|
|
138
154
|
app,
|
|
139
155
|
binding,
|
|
@@ -142,7 +158,7 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
|
|
|
142
158
|
auth: def.auth,
|
|
143
159
|
identity,
|
|
144
160
|
corsHeaders,
|
|
145
|
-
run: async ({ c, auth, kernel }) => {
|
|
161
|
+
run: async ({ c, auth, kernel, identity }) => {
|
|
146
162
|
const input = await readRemoteFunctionInput(c)
|
|
147
163
|
const validation = validateParams(def.inputSchema, input.params)
|
|
148
164
|
if (!validation.ok) {
|
|
@@ -172,7 +188,7 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
|
|
|
172
188
|
}),
|
|
173
189
|
}
|
|
174
190
|
if (def.authorize) await runAuthorize(def.authorize, ctx)
|
|
175
|
-
const lifecycle =
|
|
191
|
+
const lifecycle = createCapabilityLifecycle(honoCapabilityHost(c))
|
|
176
192
|
const result = await (async () => {
|
|
177
193
|
try {
|
|
178
194
|
return await def.execute({
|
|
@@ -180,7 +196,8 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
|
|
|
180
196
|
step: createInlineStep({
|
|
181
197
|
scope: { kind: 'function', ref: `function.${slug}`, slug },
|
|
182
198
|
}),
|
|
183
|
-
defer: lifecycle.defer,
|
|
199
|
+
defer: lifecycle.capabilities.defer,
|
|
200
|
+
sleep: lifecycle.capabilities.sleep,
|
|
184
201
|
})
|
|
185
202
|
} finally {
|
|
186
203
|
await lifecycle.settle()
|
|
@@ -222,11 +239,24 @@ function requireAuxIdentity(
|
|
|
222
239
|
)
|
|
223
240
|
}
|
|
224
241
|
|
|
242
|
+
function identityResolver(
|
|
243
|
+
kind: 'view' | 'remoteFunction',
|
|
244
|
+
slug: string,
|
|
245
|
+
identity: RemoteIdentityConfig | undefined,
|
|
246
|
+
resolveIdentity: AuxiliaryRoutesConfig<unknown>['resolveIdentity'],
|
|
247
|
+
): () => Promise<RemoteIdentityConfig> {
|
|
248
|
+
if (resolveIdentity) return async () => resolveIdentity(kind, slug)
|
|
249
|
+
const label = kind === 'view' ? 'view' : 'remote function'
|
|
250
|
+
const required = requireAuxIdentity(label, slug, identity)
|
|
251
|
+
return async () => required
|
|
252
|
+
}
|
|
253
|
+
|
|
225
254
|
type RunArgs = {
|
|
226
255
|
c: Context
|
|
227
256
|
params: Record<string, string>
|
|
228
257
|
auth: AuthContext | null
|
|
229
258
|
kernel: BoundClientSessionView<FnMap> | null
|
|
259
|
+
identity: RemoteIdentityConfig
|
|
230
260
|
}
|
|
231
261
|
|
|
232
262
|
type MountEntryArgs = {
|
|
@@ -236,7 +266,7 @@ type MountEntryArgs = {
|
|
|
236
266
|
defaultMethod: 'GET' | 'POST'
|
|
237
267
|
run: (args: RunArgs) => Promise<Response>
|
|
238
268
|
auth?: AuthPolicy
|
|
239
|
-
identity: RemoteIdentityConfig
|
|
269
|
+
identity: () => Promise<RemoteIdentityConfig>
|
|
240
270
|
corsHeaders: Record<string, string>
|
|
241
271
|
}
|
|
242
272
|
|
|
@@ -291,10 +321,11 @@ function mountEntry(args: MountEntryArgs): void {
|
|
|
291
321
|
if (value !== undefined) pathParams[name] = decodeURIComponent(value)
|
|
292
322
|
}
|
|
293
323
|
|
|
324
|
+
const resolvedIdentity = await identity()
|
|
294
325
|
const { auth: resolvedAuth, kernel } = await resolveInboundAuth(
|
|
295
326
|
stripBearerPrefix(c.req.header('authorization') ?? ''),
|
|
296
327
|
auth,
|
|
297
|
-
|
|
328
|
+
resolvedIdentity,
|
|
298
329
|
)
|
|
299
330
|
|
|
300
331
|
const response = await run({
|
|
@@ -302,6 +333,7 @@ function mountEntry(args: MountEntryArgs): void {
|
|
|
302
333
|
params: { ...hostParams, ...pathParams },
|
|
303
334
|
auth: resolvedAuth,
|
|
304
335
|
kernel,
|
|
336
|
+
identity: resolvedIdentity,
|
|
305
337
|
})
|
|
306
338
|
return applyCorsToResponse(response, corsHeaders)
|
|
307
339
|
} catch (err) {
|
package/src/server/create.ts
CHANGED
|
@@ -143,7 +143,7 @@ export function createRemoteServer<TDeps>(config: RemoteServerConfig<TDeps>): Re
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
const { app } = createKernelApp({
|
|
146
|
-
|
|
146
|
+
dispatcher,
|
|
147
147
|
domain: config.domain.methods.map(toSdkContract),
|
|
148
148
|
host: { url: config.url },
|
|
149
149
|
jwks,
|
package/src/server/index.ts
CHANGED
|
@@ -4,8 +4,16 @@ export type { RemoteServer, RemoteServerHandle } from './handle.js'
|
|
|
4
4
|
export { derivePublicJwk } from './jwks.js'
|
|
5
5
|
export { requireEnv } from './require-env.js'
|
|
6
6
|
export { canonicalizeServingUrl } from './serving-url.js'
|
|
7
|
-
export { assets, createWorkerEntry } from './worker-entry.js'
|
|
8
|
-
export type {
|
|
7
|
+
export { assets, createAppWorkerEntry, createWorkerEntry } from './worker-entry.js'
|
|
8
|
+
export type {
|
|
9
|
+
AppWorkerEntryConfig,
|
|
10
|
+
Fetcher,
|
|
11
|
+
WorkerApp,
|
|
12
|
+
WorkerEntry,
|
|
13
|
+
WorkerEntryConfig,
|
|
14
|
+
} from './worker-entry.js'
|
|
9
15
|
export { domainWorkerEntry } from './domain-entry.js'
|
|
10
16
|
export type { DomainWorkerEntryConfig } from './domain-entry.js'
|
|
17
|
+
export { serviceWorkerEntry } from './service-entry.js'
|
|
18
|
+
export type { ServiceWorkerEntryConfig, ServiceWorkerIdentityEnv } from './service-entry.js'
|
|
11
19
|
export { startNodeServer } from './start.js'
|