@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.
Files changed (190) hide show
  1. package/dist/cli/run.d.ts +8 -2
  2. package/dist/cli/run.d.ts.map +1 -1
  3. package/dist/cli/run.js +51 -1
  4. package/dist/cli/run.js.map +1 -1
  5. package/dist/define/remote-function.d.ts +3 -1
  6. package/dist/define/remote-function.d.ts.map +1 -1
  7. package/dist/define/remote-function.js.map +1 -1
  8. package/dist/dispatch/dispatcher.d.ts +10 -20
  9. package/dist/dispatch/dispatcher.d.ts.map +1 -1
  10. package/dist/dispatch/dispatcher.js +55 -54
  11. package/dist/dispatch/dispatcher.js.map +1 -1
  12. package/dist/dispatch/execute.d.ts +2 -1
  13. package/dist/dispatch/execute.d.ts.map +1 -1
  14. package/dist/dispatch/execute.js +1 -0
  15. package/dist/dispatch/execute.js.map +1 -1
  16. package/dist/domain/extend-functions.d.ts.map +1 -1
  17. package/dist/domain/extend-functions.js +2 -1
  18. package/dist/domain/extend-functions.js.map +1 -1
  19. package/dist/index.d.ts +3 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/linter/analyze.d.ts +6 -0
  24. package/dist/linter/analyze.d.ts.map +1 -0
  25. package/dist/linter/analyze.js +12 -0
  26. package/dist/linter/analyze.js.map +1 -0
  27. package/dist/linter/diagnostic.d.ts +32 -0
  28. package/dist/linter/diagnostic.d.ts.map +1 -0
  29. package/dist/linter/diagnostic.js +35 -0
  30. package/dist/linter/diagnostic.js.map +1 -0
  31. package/dist/linter/index.d.ts +10 -0
  32. package/dist/linter/index.d.ts.map +1 -0
  33. package/dist/linter/index.js +6 -0
  34. package/dist/linter/index.js.map +1 -0
  35. package/dist/linter/lint.d.ts +8 -0
  36. package/dist/linter/lint.d.ts.map +1 -0
  37. package/dist/linter/lint.js +26 -0
  38. package/dist/linter/lint.js.map +1 -0
  39. package/dist/linter/oxlint/config.d.ts +14 -0
  40. package/dist/linter/oxlint/config.d.ts.map +1 -0
  41. package/dist/linter/oxlint/config.js +18 -0
  42. package/dist/linter/oxlint/config.js.map +1 -0
  43. package/dist/linter/oxlint/plugin.d.ts +13 -0
  44. package/dist/linter/oxlint/plugin.d.ts.map +1 -0
  45. package/dist/linter/oxlint/plugin.js +14 -0
  46. package/dist/linter/oxlint/plugin.js.map +1 -0
  47. package/dist/linter/oxlint/run.d.ts +7 -0
  48. package/dist/linter/oxlint/run.d.ts.map +1 -0
  49. package/dist/linter/oxlint/run.js +122 -0
  50. package/dist/linter/oxlint/run.js.map +1 -0
  51. package/dist/linter/preflight.d.ts +2 -0
  52. package/dist/linter/preflight.d.ts.map +1 -0
  53. package/dist/linter/preflight.js +162 -0
  54. package/dist/linter/preflight.js.map +1 -0
  55. package/dist/linter/project.d.ts +18 -0
  56. package/dist/linter/project.d.ts.map +1 -0
  57. package/dist/linter/project.js +150 -0
  58. package/dist/linter/project.js.map +1 -0
  59. package/dist/linter/report.d.ts +4 -0
  60. package/dist/linter/report.d.ts.map +1 -0
  61. package/dist/linter/report.js +22 -0
  62. package/dist/linter/report.js.map +1 -0
  63. package/dist/linter/rules/catalog.d.ts +56 -0
  64. package/dist/linter/rules/catalog.d.ts.map +1 -0
  65. package/dist/linter/rules/catalog.js +53 -0
  66. package/dist/linter/rules/catalog.js.map +1 -0
  67. package/dist/linter/rules/core-has-no-async.d.ts +3 -0
  68. package/dist/linter/rules/core-has-no-async.d.ts.map +1 -0
  69. package/dist/linter/rules/core-has-no-async.js +43 -0
  70. package/dist/linter/rules/core-has-no-async.js.map +1 -0
  71. package/dist/linter/rules/core-is-pure.d.ts +3 -0
  72. package/dist/linter/rules/core-is-pure.d.ts.map +1 -0
  73. package/dist/linter/rules/core-is-pure.js +74 -0
  74. package/dist/linter/rules/core-is-pure.js.map +1 -0
  75. package/dist/linter/rules/no-nested-steps.d.ts +3 -0
  76. package/dist/linter/rules/no-nested-steps.d.ts.map +1 -0
  77. package/dist/linter/rules/no-nested-steps.js +32 -0
  78. package/dist/linter/rules/no-nested-steps.js.map +1 -0
  79. package/dist/linter/rules/no-read-inside-mutate.d.ts +3 -0
  80. package/dist/linter/rules/no-read-inside-mutate.d.ts.map +1 -0
  81. package/dist/linter/rules/no-read-inside-mutate.js +64 -0
  82. package/dist/linter/rules/no-read-inside-mutate.js.map +1 -0
  83. package/dist/linter/rules/one-remote-definition-per-file.d.ts +3 -0
  84. package/dist/linter/rules/one-remote-definition-per-file.d.ts.map +1 -0
  85. package/dist/linter/rules/one-remote-definition-per-file.js +23 -0
  86. package/dist/linter/rules/one-remote-definition-per-file.js.map +1 -0
  87. package/dist/linter/rules/oxlint-ast.d.ts +30 -0
  88. package/dist/linter/rules/oxlint-ast.d.ts.map +1 -0
  89. package/dist/linter/rules/oxlint-ast.js +104 -0
  90. package/dist/linter/rules/oxlint-ast.js.map +1 -0
  91. package/dist/linter/rules/oxlint-rule.d.ts +45 -0
  92. package/dist/linter/rules/oxlint-rule.d.ts.map +1 -0
  93. package/dist/linter/rules/oxlint-rule.js +2 -0
  94. package/dist/linter/rules/oxlint-rule.js.map +1 -0
  95. package/dist/linter/rules/project-rule.d.ts +10 -0
  96. package/dist/linter/rules/project-rule.d.ts.map +1 -0
  97. package/dist/linter/rules/project-rule.js +24 -0
  98. package/dist/linter/rules/project-rule.js.map +1 -0
  99. package/dist/linter/rules/thin-schema-composition-root.d.ts +3 -0
  100. package/dist/linter/rules/thin-schema-composition-root.d.ts.map +1 -0
  101. package/dist/linter/rules/thin-schema-composition-root.js +23 -0
  102. package/dist/linter/rules/thin-schema-composition-root.js.map +1 -0
  103. package/dist/linter/source.d.ts +24 -0
  104. package/dist/linter/source.d.ts.map +1 -0
  105. package/dist/linter/source.js +279 -0
  106. package/dist/linter/source.js.map +1 -0
  107. package/dist/linter/suppression.d.ts +3 -0
  108. package/dist/linter/suppression.d.ts.map +1 -0
  109. package/dist/linter/suppression.js +11 -0
  110. package/dist/linter/suppression.js.map +1 -0
  111. package/dist/method/context.d.ts +3 -1
  112. package/dist/method/context.d.ts.map +1 -1
  113. package/dist/server/auxiliary-routes.d.ts +5 -1
  114. package/dist/server/auxiliary-routes.d.ts.map +1 -1
  115. package/dist/server/auxiliary-routes.js +19 -10
  116. package/dist/server/auxiliary-routes.js.map +1 -1
  117. package/dist/server/create.js +1 -1
  118. package/dist/server/create.js.map +1 -1
  119. package/dist/server/index.d.ts +4 -2
  120. package/dist/server/index.d.ts.map +1 -1
  121. package/dist/server/index.js +2 -1
  122. package/dist/server/index.js.map +1 -1
  123. package/dist/server/service-entry.d.ts +32 -0
  124. package/dist/server/service-entry.d.ts.map +1 -0
  125. package/dist/server/service-entry.js +150 -0
  126. package/dist/server/service-entry.js.map +1 -0
  127. package/dist/server/start.d.ts.map +1 -1
  128. package/dist/server/start.js.map +1 -1
  129. package/dist/server/worker-entry.d.ts +7 -3
  130. package/dist/server/worker-entry.d.ts.map +1 -1
  131. package/dist/server/worker-entry.js +7 -1
  132. package/dist/server/worker-entry.js.map +1 -1
  133. package/dist/service/functions.d.ts +148 -0
  134. package/dist/service/functions.d.ts.map +1 -0
  135. package/dist/service/functions.js +115 -0
  136. package/dist/service/functions.js.map +1 -0
  137. package/dist/service/index.d.ts +3 -0
  138. package/dist/service/index.d.ts.map +1 -0
  139. package/dist/service/index.js +2 -0
  140. package/dist/service/index.js.map +1 -0
  141. package/package.json +27 -7
  142. package/src/cli/run.ts +53 -2
  143. package/src/define/remote-function.ts +3 -1
  144. package/src/dispatch/dispatcher.ts +62 -69
  145. package/src/dispatch/execute.ts +3 -1
  146. package/src/domain/extend-functions.ts +5 -1
  147. package/src/index.ts +20 -2
  148. package/src/linter/analyze.ts +17 -0
  149. package/src/linter/diagnostic.ts +68 -0
  150. package/src/linter/docs/DIAGNOSTICS.md +47 -0
  151. package/src/linter/docs/JUDGMENT.md +81 -0
  152. package/src/linter/docs/PRINCIPLES.md +97 -0
  153. package/src/linter/docs/README.md +38 -0
  154. package/src/linter/docs/RULE-AUTHORING.md +144 -0
  155. package/src/linter/docs/RULES.md +153 -0
  156. package/src/linter/index.ts +14 -0
  157. package/src/linter/lint.ts +32 -0
  158. package/src/linter/oxlint/astrale-ox.d.ts +5 -0
  159. package/src/linter/oxlint/config.ts +19 -0
  160. package/src/linter/oxlint/plugin.js +16 -0
  161. package/src/linter/oxlint/run.ts +162 -0
  162. package/src/linter/preflight.ts +181 -0
  163. package/src/linter/project.ts +190 -0
  164. package/src/linter/report.ts +25 -0
  165. package/src/linter/rules/catalog.ts +67 -0
  166. package/src/linter/rules/core-has-no-async.js +43 -0
  167. package/src/linter/rules/core-is-pure.ts +103 -0
  168. package/src/linter/rules/no-nested-steps.js +37 -0
  169. package/src/linter/rules/no-read-inside-mutate.js +72 -0
  170. package/src/linter/rules/one-remote-definition-per-file.ts +29 -0
  171. package/src/linter/rules/oxlint-ast.js +110 -0
  172. package/src/linter/rules/oxlint-rule.ts +35 -0
  173. package/src/linter/rules/project-rule.ts +37 -0
  174. package/src/linter/rules/thin-schema-composition-root.ts +26 -0
  175. package/src/linter/source.ts +323 -0
  176. package/src/linter/suppression.ts +11 -0
  177. package/src/method/context.ts +3 -1
  178. package/src/server/auxiliary-routes.ts +43 -11
  179. package/src/server/create.ts +1 -1
  180. package/src/server/index.ts +10 -2
  181. package/src/server/service-entry.ts +209 -0
  182. package/src/server/start.ts +2 -1
  183. package/src/server/worker-entry.ts +18 -5
  184. package/src/service/functions.ts +164 -0
  185. package/src/service/index.ts +18 -0
  186. package/dist/defer/index.d.ts +0 -24
  187. package/dist/defer/index.d.ts.map +0 -1
  188. package/dist/defer/index.js +0 -42
  189. package/dist/defer/index.js.map +0 -1
  190. 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
+ }
@@ -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 { buildCorsHeaders, type CorsConfig } from '@astrale-os/kernel-server'
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: AuxIdentityMap
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 = requireAuxIdentity('view', slug, identities.views[slug])
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 = requireAuxIdentity('remote function', slug, identities.remoteFunctions[slug])
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 = createHandlerLifecycle(executionLifetime(c))
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
- identity,
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) {
@@ -143,7 +143,7 @@ export function createRemoteServer<TDeps>(config: RemoteServerConfig<TDeps>): Re
143
143
  }
144
144
 
145
145
  const { app } = createKernelApp({
146
- kernel: dispatcher,
146
+ dispatcher,
147
147
  domain: config.domain.methods.map(toSdkContract),
148
148
  host: { url: config.url },
149
149
  jwks,
@@ -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 { WorkerEntry, WorkerEntryConfig } from './worker-entry.js'
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'