@astrale-os/sdk 0.4.12 → 0.4.13

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 (158) 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/index.d.ts +1 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/linter/analyze.d.ts +6 -0
  19. package/dist/linter/analyze.d.ts.map +1 -0
  20. package/dist/linter/analyze.js +12 -0
  21. package/dist/linter/analyze.js.map +1 -0
  22. package/dist/linter/diagnostic.d.ts +32 -0
  23. package/dist/linter/diagnostic.d.ts.map +1 -0
  24. package/dist/linter/diagnostic.js +35 -0
  25. package/dist/linter/diagnostic.js.map +1 -0
  26. package/dist/linter/index.d.ts +10 -0
  27. package/dist/linter/index.d.ts.map +1 -0
  28. package/dist/linter/index.js +6 -0
  29. package/dist/linter/index.js.map +1 -0
  30. package/dist/linter/lint.d.ts +8 -0
  31. package/dist/linter/lint.d.ts.map +1 -0
  32. package/dist/linter/lint.js +26 -0
  33. package/dist/linter/lint.js.map +1 -0
  34. package/dist/linter/oxlint/config.d.ts +14 -0
  35. package/dist/linter/oxlint/config.d.ts.map +1 -0
  36. package/dist/linter/oxlint/config.js +18 -0
  37. package/dist/linter/oxlint/config.js.map +1 -0
  38. package/dist/linter/oxlint/plugin.d.ts +13 -0
  39. package/dist/linter/oxlint/plugin.d.ts.map +1 -0
  40. package/dist/linter/oxlint/plugin.js +14 -0
  41. package/dist/linter/oxlint/plugin.js.map +1 -0
  42. package/dist/linter/oxlint/run.d.ts +7 -0
  43. package/dist/linter/oxlint/run.d.ts.map +1 -0
  44. package/dist/linter/oxlint/run.js +122 -0
  45. package/dist/linter/oxlint/run.js.map +1 -0
  46. package/dist/linter/preflight.d.ts +2 -0
  47. package/dist/linter/preflight.d.ts.map +1 -0
  48. package/dist/linter/preflight.js +162 -0
  49. package/dist/linter/preflight.js.map +1 -0
  50. package/dist/linter/project.d.ts +12 -0
  51. package/dist/linter/project.d.ts.map +1 -0
  52. package/dist/linter/project.js +88 -0
  53. package/dist/linter/project.js.map +1 -0
  54. package/dist/linter/report.d.ts +4 -0
  55. package/dist/linter/report.d.ts.map +1 -0
  56. package/dist/linter/report.js +22 -0
  57. package/dist/linter/report.js.map +1 -0
  58. package/dist/linter/rules/catalog.d.ts +56 -0
  59. package/dist/linter/rules/catalog.d.ts.map +1 -0
  60. package/dist/linter/rules/catalog.js +53 -0
  61. package/dist/linter/rules/catalog.js.map +1 -0
  62. package/dist/linter/rules/core-has-no-async.d.ts +3 -0
  63. package/dist/linter/rules/core-has-no-async.d.ts.map +1 -0
  64. package/dist/linter/rules/core-has-no-async.js +43 -0
  65. package/dist/linter/rules/core-has-no-async.js.map +1 -0
  66. package/dist/linter/rules/core-is-pure.d.ts +3 -0
  67. package/dist/linter/rules/core-is-pure.d.ts.map +1 -0
  68. package/dist/linter/rules/core-is-pure.js +74 -0
  69. package/dist/linter/rules/core-is-pure.js.map +1 -0
  70. package/dist/linter/rules/no-nested-steps.d.ts +3 -0
  71. package/dist/linter/rules/no-nested-steps.d.ts.map +1 -0
  72. package/dist/linter/rules/no-nested-steps.js +32 -0
  73. package/dist/linter/rules/no-nested-steps.js.map +1 -0
  74. package/dist/linter/rules/no-read-inside-mutate.d.ts +3 -0
  75. package/dist/linter/rules/no-read-inside-mutate.d.ts.map +1 -0
  76. package/dist/linter/rules/no-read-inside-mutate.js +64 -0
  77. package/dist/linter/rules/no-read-inside-mutate.js.map +1 -0
  78. package/dist/linter/rules/one-remote-definition-per-file.d.ts +3 -0
  79. package/dist/linter/rules/one-remote-definition-per-file.d.ts.map +1 -0
  80. package/dist/linter/rules/one-remote-definition-per-file.js +23 -0
  81. package/dist/linter/rules/one-remote-definition-per-file.js.map +1 -0
  82. package/dist/linter/rules/oxlint-ast.d.ts +30 -0
  83. package/dist/linter/rules/oxlint-ast.d.ts.map +1 -0
  84. package/dist/linter/rules/oxlint-ast.js +104 -0
  85. package/dist/linter/rules/oxlint-ast.js.map +1 -0
  86. package/dist/linter/rules/oxlint-rule.d.ts +45 -0
  87. package/dist/linter/rules/oxlint-rule.d.ts.map +1 -0
  88. package/dist/linter/rules/oxlint-rule.js +2 -0
  89. package/dist/linter/rules/oxlint-rule.js.map +1 -0
  90. package/dist/linter/rules/project-rule.d.ts +10 -0
  91. package/dist/linter/rules/project-rule.d.ts.map +1 -0
  92. package/dist/linter/rules/project-rule.js +24 -0
  93. package/dist/linter/rules/project-rule.js.map +1 -0
  94. package/dist/linter/rules/thin-schema-composition-root.d.ts +3 -0
  95. package/dist/linter/rules/thin-schema-composition-root.d.ts.map +1 -0
  96. package/dist/linter/rules/thin-schema-composition-root.js +23 -0
  97. package/dist/linter/rules/thin-schema-composition-root.js.map +1 -0
  98. package/dist/linter/source.d.ts +24 -0
  99. package/dist/linter/source.d.ts.map +1 -0
  100. package/dist/linter/source.js +279 -0
  101. package/dist/linter/source.js.map +1 -0
  102. package/dist/linter/suppression.d.ts +3 -0
  103. package/dist/linter/suppression.d.ts.map +1 -0
  104. package/dist/linter/suppression.js +11 -0
  105. package/dist/linter/suppression.js.map +1 -0
  106. package/dist/method/context.d.ts +3 -1
  107. package/dist/method/context.d.ts.map +1 -1
  108. package/dist/server/auxiliary-routes.d.ts.map +1 -1
  109. package/dist/server/auxiliary-routes.js +4 -4
  110. package/dist/server/auxiliary-routes.js.map +1 -1
  111. package/dist/server/create.js +1 -1
  112. package/dist/server/create.js.map +1 -1
  113. package/dist/server/start.d.ts.map +1 -1
  114. package/dist/server/start.js.map +1 -1
  115. package/package.json +27 -7
  116. package/src/cli/run.ts +53 -2
  117. package/src/define/remote-function.ts +3 -1
  118. package/src/dispatch/dispatcher.ts +62 -69
  119. package/src/dispatch/execute.ts +3 -1
  120. package/src/index.ts +2 -2
  121. package/src/linter/analyze.ts +17 -0
  122. package/src/linter/diagnostic.ts +68 -0
  123. package/src/linter/docs/DIAGNOSTICS.md +47 -0
  124. package/src/linter/docs/JUDGMENT.md +81 -0
  125. package/src/linter/docs/PRINCIPLES.md +97 -0
  126. package/src/linter/docs/README.md +38 -0
  127. package/src/linter/docs/RULE-AUTHORING.md +144 -0
  128. package/src/linter/docs/RULES.md +153 -0
  129. package/src/linter/index.ts +14 -0
  130. package/src/linter/lint.ts +32 -0
  131. package/src/linter/oxlint/astrale-ox.d.ts +5 -0
  132. package/src/linter/oxlint/config.ts +19 -0
  133. package/src/linter/oxlint/plugin.js +16 -0
  134. package/src/linter/oxlint/run.ts +162 -0
  135. package/src/linter/preflight.ts +181 -0
  136. package/src/linter/project.ts +108 -0
  137. package/src/linter/report.ts +25 -0
  138. package/src/linter/rules/catalog.ts +67 -0
  139. package/src/linter/rules/core-has-no-async.js +43 -0
  140. package/src/linter/rules/core-is-pure.ts +103 -0
  141. package/src/linter/rules/no-nested-steps.js +37 -0
  142. package/src/linter/rules/no-read-inside-mutate.js +72 -0
  143. package/src/linter/rules/one-remote-definition-per-file.ts +29 -0
  144. package/src/linter/rules/oxlint-ast.js +110 -0
  145. package/src/linter/rules/oxlint-rule.ts +35 -0
  146. package/src/linter/rules/project-rule.ts +37 -0
  147. package/src/linter/rules/thin-schema-composition-root.ts +26 -0
  148. package/src/linter/source.ts +323 -0
  149. package/src/linter/suppression.ts +11 -0
  150. package/src/method/context.ts +3 -1
  151. package/src/server/auxiliary-routes.ts +9 -4
  152. package/src/server/create.ts +1 -1
  153. package/src/server/start.ts +2 -1
  154. package/dist/defer/index.d.ts +0 -24
  155. package/dist/defer/index.d.ts.map +0 -1
  156. package/dist/defer/index.js +0 -42
  157. package/dist/defer/index.js.map +0 -1
  158. 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'
@@ -172,7 +176,7 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
172
176
  }),
173
177
  }
174
178
  if (def.authorize) await runAuthorize(def.authorize, ctx)
175
- const lifecycle = createHandlerLifecycle(executionLifetime(c))
179
+ const lifecycle = createCapabilityLifecycle(honoCapabilityHost(c))
176
180
  const result = await (async () => {
177
181
  try {
178
182
  return await def.execute({
@@ -180,7 +184,8 @@ export function mountAuxiliaryRoutes<TDeps>(config: AuxiliaryRoutesConfig<TDeps>
180
184
  step: createInlineStep({
181
185
  scope: { kind: 'function', ref: `function.${slug}`, slug },
182
186
  }),
183
- defer: lifecycle.defer,
187
+ defer: lifecycle.capabilities.defer,
188
+ sleep: lifecycle.capabilities.sleep,
184
189
  })
185
190
  } finally {
186
191
  await lifecycle.settle()
@@ -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,
@@ -6,6 +6,7 @@
6
6
  * Cloudflare, consumers go through `server.app.fetch` directly.
7
7
  */
8
8
 
9
+ import type { serve as nodeServe } from '@hono/node-server'
9
10
  import type { Hono } from 'hono'
10
11
 
11
12
  import type { RemoteServerHandle } from './handle.js'
@@ -15,7 +16,7 @@ export async function startNodeServer(app: Hono, port = 3000): Promise<RemoteSer
15
16
  // bundles (wrangler/esbuild) traverse it even though they never call it. A
16
17
  // literal import() would make them resolve the optional peer and fail.
17
18
  const nodeServerModule = '@hono/node-server'
18
- const { serve } = (await import(nodeServerModule)) as typeof import('@hono/node-server')
19
+ const { serve } = (await import(nodeServerModule)) as { serve: typeof nodeServe }
19
20
  // oxlint-disable-next-line no-explicit-any
20
21
  const server = serve({ fetch: app.fetch, port }) as any
21
22
 
@@ -1,24 +0,0 @@
1
- import type { Context } from 'hono';
2
- /** Internal structural host capability; not part of the author-facing SDK contract. */
3
- export type HandlerExecutionLifetime = {
4
- waitUntil(promise: Promise<unknown>): void;
5
- };
6
- /**
7
- * Work detached from the current handler result.
8
- *
9
- * The callback is deliberately lazy: the SDK registers the resulting task with
10
- * the active execution backend before it starts. Current request-based hosts
11
- * extend their request lifetime; future durable backends may provide stronger
12
- * execution guarantees without changing the author-facing API.
13
- */
14
- export type DeferredTask = () => void | PromiseLike<unknown>;
15
- export type Defer = (task: DeferredTask) => void;
16
- export type HandlerLifecycle = {
17
- defer: Defer;
18
- settle(): Promise<void>;
19
- };
20
- /** Bind detached work to the host, with a safe blocking fallback for other runtimes. */
21
- export declare function createHandlerLifecycle(lifetime?: HandlerExecutionLifetime): HandlerLifecycle;
22
- /** Hono omits its execution context in Node and in lightweight unit tests. */
23
- export declare function executionLifetime(c: Context): HandlerExecutionLifetime | undefined;
24
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/defer/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAEnC,uFAAuF;AACvF,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;CAC3C,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;AAE5D,MAAM,MAAM,KAAK,GAAG,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAA;AAEhD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACxB,CAAA;AAED,wFAAwF;AACxF,wBAAgB,sBAAsB,CAAC,QAAQ,CAAC,EAAE,wBAAwB,GAAG,gBAAgB,CA4B5F;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,GAAG,wBAAwB,GAAG,SAAS,CAOlF"}
@@ -1,42 +0,0 @@
1
- /** Bind detached work to the host, with a safe blocking fallback for other runtimes. */
2
- export function createHandlerLifecycle(lifetime) {
3
- const pending = [];
4
- return {
5
- defer(task) {
6
- const promise = Promise.resolve().then(task);
7
- if (lifetime)
8
- lifetime.waitUntil(promise);
9
- else
10
- pending.push(promise);
11
- },
12
- async settle() {
13
- if (lifetime || pending.length === 0)
14
- return;
15
- // A deferred task may register more work before it settles. Drain until
16
- // the lifecycle is stable so non-request runtimes never abandon children.
17
- let cursor = 0;
18
- while (cursor < pending.length) {
19
- const batch = pending.slice(cursor);
20
- cursor = pending.length;
21
- const results = await Promise.allSettled(batch);
22
- for (const result of results) {
23
- if (result.status === 'rejected') {
24
- // oxlint-disable-next-line no-console
25
- console.error('[sdk] deferred task failed:', result.reason);
26
- }
27
- }
28
- }
29
- },
30
- };
31
- }
32
- /** Hono omits its execution context in Node and in lightweight unit tests. */
33
- export function executionLifetime(c) {
34
- try {
35
- const host = c.executionCtx;
36
- return { waitUntil: (promise) => host.waitUntil(promise) };
37
- }
38
- catch {
39
- return undefined;
40
- }
41
- }
42
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/defer/index.ts"],"names":[],"mappings":"AAwBA,wFAAwF;AACxF,MAAM,UAAU,sBAAsB,CAAC,QAAmC;IACxE,MAAM,OAAO,GAAuB,EAAE,CAAA;IAEtC,OAAO;QACL,KAAK,CAAC,IAAI;YACR,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAC5C,IAAI,QAAQ;gBAAE,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;;gBACpC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC5B,CAAC;QACD,KAAK,CAAC,MAAM;YACV,IAAI,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAM;YAE5C,wEAAwE;YACxE,0EAA0E;YAC1E,IAAI,MAAM,GAAG,CAAC,CAAA;YACd,OAAO,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;gBAC/B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;gBACnC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;gBACvB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;gBAC/C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;oBAC7B,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;wBACjC,sCAAsC;wBACtC,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;oBAC7D,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,iBAAiB,CAAC,CAAU;IAC1C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,CAAC,YAAY,CAAA;QAC3B,OAAO,EAAE,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAA;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC"}
@@ -1,64 +0,0 @@
1
- import type { Context } from 'hono'
2
-
3
- /** Internal structural host capability; not part of the author-facing SDK contract. */
4
- export type HandlerExecutionLifetime = {
5
- waitUntil(promise: Promise<unknown>): void
6
- }
7
-
8
- /**
9
- * Work detached from the current handler result.
10
- *
11
- * The callback is deliberately lazy: the SDK registers the resulting task with
12
- * the active execution backend before it starts. Current request-based hosts
13
- * extend their request lifetime; future durable backends may provide stronger
14
- * execution guarantees without changing the author-facing API.
15
- */
16
- export type DeferredTask = () => void | PromiseLike<unknown>
17
-
18
- export type Defer = (task: DeferredTask) => void
19
-
20
- export type HandlerLifecycle = {
21
- defer: Defer
22
- settle(): Promise<void>
23
- }
24
-
25
- /** Bind detached work to the host, with a safe blocking fallback for other runtimes. */
26
- export function createHandlerLifecycle(lifetime?: HandlerExecutionLifetime): HandlerLifecycle {
27
- const pending: Promise<unknown>[] = []
28
-
29
- return {
30
- defer(task) {
31
- const promise = Promise.resolve().then(task)
32
- if (lifetime) lifetime.waitUntil(promise)
33
- else pending.push(promise)
34
- },
35
- async settle() {
36
- if (lifetime || pending.length === 0) return
37
-
38
- // A deferred task may register more work before it settles. Drain until
39
- // the lifecycle is stable so non-request runtimes never abandon children.
40
- let cursor = 0
41
- while (cursor < pending.length) {
42
- const batch = pending.slice(cursor)
43
- cursor = pending.length
44
- const results = await Promise.allSettled(batch)
45
- for (const result of results) {
46
- if (result.status === 'rejected') {
47
- // oxlint-disable-next-line no-console
48
- console.error('[sdk] deferred task failed:', result.reason)
49
- }
50
- }
51
- }
52
- },
53
- }
54
- }
55
-
56
- /** Hono omits its execution context in Node and in lightweight unit tests. */
57
- export function executionLifetime(c: Context): HandlerExecutionLifetime | undefined {
58
- try {
59
- const host = c.executionCtx
60
- return { waitUntil: (promise) => host.waitUntil(promise) }
61
- } catch {
62
- return undefined
63
- }
64
- }