@prestack/thread 0.0.1 → 0.0.2

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 (150) hide show
  1. package/dist/core/actor.ts +20 -0
  2. package/dist/core/asking.d.ts +0 -1
  3. package/dist/core/asking.js +0 -1
  4. package/dist/core/asking.ts +1342 -0
  5. package/dist/core/attach.js +2 -2
  6. package/dist/core/attach.ts +585 -0
  7. package/dist/core/chain.js +8 -1
  8. package/dist/core/chain.ts +148 -0
  9. package/dist/core/cli.js +97 -18
  10. package/dist/core/cli.ts +2004 -0
  11. package/dist/core/condition.ts +227 -0
  12. package/dist/core/descend.d.ts +1 -0
  13. package/dist/core/descend.js +36 -0
  14. package/dist/core/descend.ts +1001 -0
  15. package/dist/core/finding.ts +38 -0
  16. package/dist/core/generators/meta.js +6 -0
  17. package/dist/core/generators/meta.ts +1006 -0
  18. package/dist/core/init.ts +170 -0
  19. package/dist/core/invoked.ts +47 -0
  20. package/dist/core/round.ts +57 -0
  21. package/dist/core/rows.ts +369 -0
  22. package/dist/core/scope.ts +18 -0
  23. package/dist/core/thread.ts +29 -0
  24. package/dist/core/tree.ts +67 -0
  25. package/dist/core/types.ts +350 -0
  26. package/dist/derivation/attach.js +1 -1
  27. package/dist/derivation/attach.ts +68 -0
  28. package/dist/derivation/generators/build.ts +232 -0
  29. package/dist/derivation/generators/check.ts +118 -0
  30. package/dist/derivation/generators/derivation.ts +347 -0
  31. package/dist/derivation/generators/determinism.ts +145 -0
  32. package/dist/derivation/generators/release.js +19 -9
  33. package/dist/derivation/generators/release.ts +213 -0
  34. package/dist/derivation/makers/release.ts +135 -0
  35. package/dist/derivation/release.ts +23 -0
  36. package/dist/integrations/attach.js +2 -2
  37. package/dist/integrations/attach.ts +207 -0
  38. package/dist/integrations/generators/read.ts +127 -0
  39. package/dist/integrations/generators/secret.ts +289 -0
  40. package/dist/integrations/generators/target.js +16 -6
  41. package/dist/integrations/generators/target.ts +944 -0
  42. package/dist/integrations/generators/vault.js +46 -14
  43. package/dist/integrations/generators/vault.ts +212 -0
  44. package/dist/integrations/{sdk.js → module.js} +4 -4
  45. package/dist/integrations/module.ts +189 -0
  46. package/dist/integrations/secret.ts +50 -0
  47. package/dist/integrations/target.d.ts +3 -1
  48. package/dist/integrations/target.js +14 -11
  49. package/dist/integrations/target.ts +340 -0
  50. package/dist/integrations/vault.d.ts +2 -0
  51. package/dist/integrations/vault.js +4 -2
  52. package/dist/integrations/vault.ts +136 -0
  53. package/dist/integrity/attach.ts +234 -0
  54. package/dist/integrity/check-threads.js +2 -26
  55. package/dist/integrity/check-threads.ts +166 -0
  56. package/dist/integrity/constraint.ts +1202 -0
  57. package/dist/integrity/generators/compiler.ts +161 -0
  58. package/dist/integrity/generators/gate.ts +201 -0
  59. package/dist/integrity/generators/issues.ts +160 -0
  60. package/dist/integrity/generators/publish.js +14 -5
  61. package/dist/integrity/generators/publish.ts +247 -0
  62. package/dist/integrity/generators/rule.ts +271 -0
  63. package/dist/integrity/generators/seal.ts +117 -0
  64. package/dist/integrity/generators/trial.ts +320 -0
  65. package/dist/integrity/lint.ts +227 -0
  66. package/dist/integrity/lsp/bin.ts +42 -0
  67. package/dist/integrity/lsp/diagnostics.ts +92 -0
  68. package/dist/integrity/lsp/findings.ts +63 -0
  69. package/dist/integrity/lsp/lint.ts +140 -0
  70. package/dist/integrity/lsp/protocol.ts +78 -0
  71. package/dist/integrity/lsp/server.ts +224 -0
  72. package/dist/integrity/sealed.js +2 -2
  73. package/dist/integrity/sealed.ts +56 -0
  74. package/dist/integrity/slopnator.ts +350 -0
  75. package/dist/integrity/slopnators/sl_broken_lineage.ts +36 -0
  76. package/dist/integrity/slopnators/sl_declared_elsewhere.ts +39 -0
  77. package/dist/integrity/slopnators/sl_derived_drift.ts +98 -0
  78. package/dist/integrity/slopnators/sl_hardcoded_style.ts +55 -0
  79. package/dist/integrity/slopnators/sl_inert_island.ts +91 -0
  80. package/dist/integrity/slopnators/sl_inert_slopnator.ts +93 -0
  81. package/dist/integrity/slopnators/sl_missing_row.ts +182 -0
  82. package/dist/integrity/slopnators/sl_no_barrel.ts +48 -0
  83. package/dist/integrity/slopnators/sl_no_child_process.ts +74 -0
  84. package/dist/integrity/slopnators/sl_no_comment.ts +43 -0
  85. package/dist/integrity/slopnators/sl_no_dispatch_bypass.ts +328 -0
  86. package/dist/integrity/slopnators/sl_no_import_cycle.ts +50 -0
  87. package/dist/integrity/slopnators/sl_no_such_command.ts +64 -0
  88. package/dist/integrity/slopnators/sl_only_takes_a_literal.ts +63 -0
  89. package/dist/integrity/slopnators/sl_secret_literal.ts +100 -0
  90. package/dist/integrity/slopnators/sl_stale_path.ts +94 -0
  91. package/dist/integrity/slopnators/sl_twice_meant.ts +31 -0
  92. package/dist/integrity/slopnators/sl_twice_parented.ts +37 -0
  93. package/dist/integrity/slopnators/sl_undeclared_crossing.ts +87 -0
  94. package/dist/integrity/slopnators/sl_undeclared_destination.ts +65 -0
  95. package/dist/integrity/slopnators/sl_undeclared_element.ts +44 -0
  96. package/dist/integrity/slopnators/sl_undeclared_node.ts +69 -0
  97. package/dist/integrity/slopnators/sl_undeclared_operation.ts +147 -0
  98. package/dist/integrity/slopnators/sl_undeclared_variant.ts +148 -0
  99. package/dist/integrity/slopnators/sl_undescribed.ts +65 -0
  100. package/dist/integrity/slopnators/sl_ungoverned_island.ts +61 -0
  101. package/dist/integrity/slopnators/sl_unkept_rows.ts +111 -0
  102. package/dist/integrity/slopnators/sl_unnamed_island.ts +41 -0
  103. package/dist/integrity/slopnators/sl_unpaired_member.ts +131 -0
  104. package/dist/integrity/slopnators/sl_unpaired_slopnator.ts +58 -0
  105. package/dist/integrity/slopnators/sl_unprefixed_member.ts +136 -0
  106. package/dist/integrity/slopnators/sl_unseamed_reach.ts +79 -0
  107. package/dist/integrity/slopnators/sl_unthreaded.ts +419 -0
  108. package/dist/integrity/slopnators/sl_untranslated_key.ts +164 -0
  109. package/dist/integrity/slopnators/sl_unversioned.ts +36 -0
  110. package/dist/integrity/sweep.ts +307 -0
  111. package/dist/knowledge/attach.ts +350 -0
  112. package/dist/knowledge/generators/docsgen.ts +174 -0
  113. package/dist/knowledge/generators/glossary.ts +73 -0
  114. package/dist/knowledge/generators/skills.ts +263 -0
  115. package/dist/knowledge/glossary.ts +6 -0
  116. package/dist/operations/access.ts +88 -0
  117. package/dist/operations/attach.ts +475 -0
  118. package/dist/operations/capabilities.ts +65 -0
  119. package/dist/operations/cost.ts +58 -0
  120. package/dist/operations/dispatch.ts +348 -0
  121. package/dist/operations/error-results.ts +31 -0
  122. package/dist/operations/errors/catalog.ts +168 -0
  123. package/dist/operations/errors/classes.ts +51 -0
  124. package/dist/operations/errors/errors.ts +314 -0
  125. package/dist/operations/generators/capability.js +29 -19
  126. package/dist/operations/generators/capability.ts +704 -0
  127. package/dist/operations/generators/compile.js +20 -1
  128. package/dist/operations/generators/compile.ts +643 -0
  129. package/dist/operations/generators/grant.ts +336 -0
  130. package/dist/operations/generators/operation.ts +972 -0
  131. package/dist/operations/generators/permits.ts +223 -0
  132. package/dist/operations/generators/seed.ts +347 -0
  133. package/dist/operations/generators/trial.js +4 -1
  134. package/dist/operations/generators/trial.ts +529 -0
  135. package/dist/operations/http.ts +164 -0
  136. package/dist/operations/makers/capability.ts +166 -0
  137. package/dist/operations/makers/trial.ts +520 -0
  138. package/dist/operations/memory.ts +470 -0
  139. package/dist/operations/metering.ts +328 -0
  140. package/dist/operations/nodes.ts +54 -0
  141. package/dist/operations/operations.ts +32 -0
  142. package/dist/operations/queue-policy.ts +10 -0
  143. package/dist/operations/result.ts +28 -0
  144. package/dist/operations/seams.d.ts +4 -4
  145. package/dist/operations/seams.ts +364 -0
  146. package/dist/operations/store.ts +29 -0
  147. package/package.json +8 -1
  148. package/dist/integrations/generators/sdk.d.ts +0 -1
  149. package/dist/integrations/generators/sdk.js +0 -148
  150. /package/dist/integrations/{sdk.d.ts → module.d.ts} +0 -0
@@ -0,0 +1,63 @@
1
+ export interface At {
2
+ readonly line: number
3
+ readonly column: number
4
+ readonly endLine?: number
5
+ readonly endColumn?: number
6
+ }
7
+
8
+ export interface Finding {
9
+ readonly severity: 'error' | 'warning'
10
+
11
+ readonly check: string
12
+ readonly detail: string
13
+
14
+ readonly file: string
15
+
16
+ readonly at?: At
17
+
18
+ readonly anchor?: string
19
+
20
+ readonly suggestions?: readonly string[]
21
+ }
22
+
23
+ export function detailOf(finding: Finding): string {
24
+ const offered = finding.suggestions ?? []
25
+ return offered.length === 0 ? finding.detail : `${finding.detail} ${offered.join(' ')}`
26
+ }
27
+
28
+ const DECLARED_ID = /\b[a-z]{2,6}_[a-z][a-z0-9_]*\b/g
29
+
30
+ export function anchorIn(detail: string): string | undefined {
31
+ const candidates = [
32
+ ...(detail.match(DECLARED_ID) ?? []),
33
+ ...[...detail.matchAll(/"([a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_*]+)+)"/g)].map((m) => m[1] as string),
34
+ ]
35
+ if (candidates.length === 0) return undefined
36
+ return candidates.reduce((longest, one) => (one.length > longest.length ? one : longest))
37
+ }
38
+
39
+ export function locate(text: string, anchor: string): At | undefined {
40
+ const lines = text.split('\n')
41
+ for (const [index, line] of lines.entries()) {
42
+ const column = line.indexOf(anchor)
43
+ if (column === -1) continue
44
+ return {
45
+ line: index + 1,
46
+ column: column + 1,
47
+ endLine: index + 1,
48
+ endColumn: column + 1 + anchor.length,
49
+ }
50
+ }
51
+ return undefined
52
+ }
53
+
54
+ export function headOf(text: string): At {
55
+ const first = text.split('\n')[0] ?? ''
56
+ return { line: 1, column: 1, endLine: 1, endColumn: Math.max(first.length, 1) + 1 }
57
+ }
58
+
59
+ export function rangeOf(finding: Finding, text: string): At {
60
+ if (finding.at !== undefined) return finding.at
61
+ const anchor = finding.anchor ?? anchorIn(finding.detail)
62
+ return (anchor === undefined ? undefined : locate(text, anchor)) ?? headOf(text)
63
+ }
@@ -0,0 +1,140 @@
1
+ import { spawn } from 'node:child_process'
2
+ import { existsSync } from 'node:fs'
3
+ import { extname, join } from 'node:path'
4
+
5
+ import { theRules } from '../lint.ts'
6
+
7
+ import type { Finding } from './findings.ts'
8
+
9
+ interface EslintMessage {
10
+ readonly ruleId?: string | null
11
+ readonly severity: number
12
+ readonly message: string
13
+ readonly line?: number
14
+ readonly column?: number
15
+ readonly endLine?: number
16
+ readonly endColumn?: number
17
+
18
+ readonly suggestions?: readonly { readonly desc?: string }[]
19
+ }
20
+
21
+ interface EslintFile {
22
+ readonly messages?: readonly EslintMessage[]
23
+ }
24
+
25
+ const LINTABLE = new Set(['.ts', '.tsx', '.mts', '.cts', '.js', '.mjs', '.cjs', '.jsx'])
26
+
27
+ export const isLintable = (relative: string): boolean => LINTABLE.has(extname(relative))
28
+
29
+ export const LINT_CONFIG = 'eslint.config.js'
30
+
31
+ export class LintUnavailableError extends Error {
32
+ constructor(detail: string) {
33
+ super(
34
+ `ESLint could not be run over this repository: ${detail}. This server reports what the build ` +
35
+ 'computes, so a lint pass that answered "no problems" because it never ran would be ' +
36
+ 'indistinguishable from a clean file — which is the one thing it must never be.',
37
+ )
38
+ this.name = 'LintUnavailableError'
39
+ }
40
+ }
41
+
42
+ function binaryIn(root: string): string | undefined {
43
+ const local = join(root, 'node_modules', '.bin', 'eslint')
44
+ return existsSync(local) ? local : undefined
45
+ }
46
+
47
+ async function ruleFindings(relative: string, text: string): Promise<readonly Finding[]> {
48
+ const rules = theRules()
49
+ return rules(relative, text).map((violation) => ({
50
+ severity: 'error' as const,
51
+ check: violation.rule,
52
+
53
+ detail: violation.bans ?? violation.message,
54
+ file: relative,
55
+ at: { line: violation.line, column: violation.column },
56
+ suggestions: [
57
+ ...(violation.instead === undefined ? [] : [violation.instead]),
58
+ ...(violation.right === undefined ? [] : [`Write: ${violation.right}`]),
59
+ ],
60
+ }))
61
+ }
62
+
63
+ const suggestionsOf = (message: EslintMessage): readonly string[] =>
64
+ (message.suggestions ?? []).map((one) => one.desc).filter((desc): desc is string => desc !== undefined)
65
+
66
+ export async function lintFindings(root: string, relative: string, text: string): Promise<readonly Finding[]> {
67
+ if (!isLintable(relative)) return []
68
+
69
+ if (!existsSync(join(root, LINT_CONFIG))) return await ruleFindings(relative, text)
70
+
71
+ const binary = binaryIn(root)
72
+ if (binary === undefined) {
73
+ return [
74
+ {
75
+ severity: 'error',
76
+ check: 'lint:unavailable',
77
+ detail:
78
+ `${LINT_CONFIG} is here and node_modules/.bin/eslint is not, so none of this repository's ` +
79
+ 'rules are being checked while you type. Install the dev dependencies.',
80
+ file: LINT_CONFIG,
81
+ },
82
+ ]
83
+ }
84
+
85
+ const report = await runEslint(binary, root, relative, text)
86
+
87
+ return report.flatMap((file) =>
88
+ (file.messages ?? []).map((message) => ({
89
+ severity: message.severity === 2 ? ('error' as const) : ('warning' as const),
90
+ check: message.ruleId ?? 'lint',
91
+ detail: message.message,
92
+ file: relative,
93
+ at: {
94
+ line: message.line ?? 1,
95
+ column: message.column ?? 1,
96
+ ...(message.endLine === undefined ? {} : { endLine: message.endLine }),
97
+ ...(message.endColumn === undefined ? {} : { endColumn: message.endColumn }),
98
+ },
99
+ ...(suggestionsOf(message).length === 0 ? {} : { suggestions: suggestionsOf(message) }),
100
+ })),
101
+ )
102
+ }
103
+
104
+ function runEslint(
105
+ binary: string,
106
+ root: string,
107
+ relative: string,
108
+ text: string,
109
+ ): Promise<readonly EslintFile[]> {
110
+ return new Promise((resolve, reject) => {
111
+
112
+ const child = spawn(binary, ['--format', 'json', '--no-warn-ignored', '--stdin', '--stdin-filename', relative], {
113
+ cwd: root,
114
+ shell: false,
115
+ })
116
+
117
+ let out = ''
118
+ let err = ''
119
+ child.stdout.on('data', (chunk: Buffer) => (out += chunk.toString()))
120
+ child.stderr.on('data', (chunk: Buffer) => (err += chunk.toString()))
121
+ child.on('error', (error) => reject(new LintUnavailableError(error.message)))
122
+ child.on('close', () => {
123
+
124
+ let parsed: unknown
125
+ try {
126
+ parsed = JSON.parse(out)
127
+ } catch {
128
+ reject(new LintUnavailableError(err.trim() === '' ? `it wrote ${out.length} bytes of non-JSON` : err.trim()))
129
+ return
130
+ }
131
+ if (!Array.isArray(parsed)) {
132
+ reject(new LintUnavailableError('its JSON report was not an array of files'))
133
+ return
134
+ }
135
+ resolve(parsed as readonly EslintFile[])
136
+ })
137
+
138
+ child.stdin.end(text)
139
+ })
140
+ }
@@ -0,0 +1,78 @@
1
+
2
+
3
+ export interface RpcMessage {
4
+ readonly jsonrpc: '2.0'
5
+ readonly id?: number | string | null
6
+ readonly method?: string
7
+ readonly params?: unknown
8
+ readonly result?: unknown
9
+ readonly error?: { readonly code: number; readonly message: string }
10
+ }
11
+
12
+ const HEADER_END = '\r\n\r\n'
13
+
14
+ export const METHOD_NOT_FOUND = -32601
15
+
16
+ export function encode(message: RpcMessage): Buffer {
17
+ const body = Buffer.from(JSON.stringify(message), 'utf8')
18
+ return Buffer.concat([Buffer.from(`Content-Length: ${body.length}${HEADER_END}`, 'ascii'), body])
19
+ }
20
+
21
+ export class MalformedMessageError extends Error {
22
+ constructor(detail: string) {
23
+ super(
24
+ `the editor sent something this server cannot parse: ${detail}. ` +
25
+ 'A language server that skipped it would answer the next request against a stream it no ' +
26
+ 'longer understands, which reads as a file with no problems.',
27
+ )
28
+ this.name = 'MalformedMessageError'
29
+ }
30
+ }
31
+
32
+ export class MessageBuffer {
33
+ #pending: Buffer = Buffer.alloc(0)
34
+
35
+ push(chunk: Buffer): readonly RpcMessage[] {
36
+ this.#pending = Buffer.concat([this.#pending, chunk])
37
+
38
+ const found: RpcMessage[] = []
39
+ for (;;) {
40
+ const split = this.#pending.indexOf(HEADER_END, 0, 'ascii')
41
+ if (split === -1) return found
42
+
43
+ const header = this.#pending.subarray(0, split).toString('ascii')
44
+ const length = contentLength(header)
45
+ const start = split + HEADER_END.length
46
+
47
+ if (this.#pending.length < start + length) return found
48
+
49
+ const body = this.#pending.subarray(start, start + length).toString('utf8')
50
+ this.#pending = this.#pending.subarray(start + length)
51
+
52
+ let parsed: unknown
53
+ try {
54
+ parsed = JSON.parse(body)
55
+ } catch {
56
+ throw new MalformedMessageError(`a ${length}-byte body that is not JSON`)
57
+ }
58
+ if (typeof parsed !== 'object' || parsed === null) {
59
+ throw new MalformedMessageError(`a ${length}-byte body that is not a JSON object`)
60
+ }
61
+ found.push(parsed as RpcMessage)
62
+ }
63
+ }
64
+ }
65
+
66
+ function contentLength(header: string): number {
67
+ for (const line of header.split('\r\n')) {
68
+ const [name, value] = line.split(':', 2)
69
+
70
+ if (name?.trim().toLowerCase() !== 'content-length') continue
71
+ const length = Number.parseInt((value ?? '').trim(), 10)
72
+ if (!Number.isInteger(length) || length < 0) {
73
+ throw new MalformedMessageError(`Content-Length: ${(value ?? '').trim()}`)
74
+ }
75
+ return length
76
+ }
77
+ throw new MalformedMessageError(`a header block with no Content-Length: ${JSON.stringify(header)}`)
78
+ }
@@ -0,0 +1,224 @@
1
+ import { relative as relativeTo, resolve } from 'node:path'
2
+ import { fileURLToPath, pathToFileURL } from 'node:url'
3
+
4
+ import { named } from '../../core/scope.ts'
5
+ import { report, SOURCE_NAME, type Diagnostic, type DiagnosticSource } from './diagnostics.ts'
6
+ import { METHOD_NOT_FOUND, type RpcMessage } from './protocol.ts'
7
+
8
+ const SYNC_FULL = 1
9
+
10
+ export interface ServerOptions {
11
+
12
+ readonly root: string
13
+
14
+ readonly sourceFor: (root: string) => DiagnosticSource
15
+ send(message: RpcMessage): void
16
+
17
+ settle?(): Promise<void>
18
+ }
19
+
20
+ interface TextDocumentIdentifier {
21
+ readonly uri?: unknown
22
+ }
23
+
24
+ interface DidOpenParams {
25
+ readonly textDocument?: { readonly uri?: unknown; readonly text?: unknown }
26
+ }
27
+
28
+ interface DidChangeParams {
29
+ readonly textDocument?: TextDocumentIdentifier
30
+ readonly contentChanges?: readonly { readonly text?: unknown }[]
31
+ }
32
+
33
+ interface InitializeParams {
34
+ readonly rootUri?: unknown
35
+ readonly rootPath?: unknown
36
+ readonly workspaceFolders?: readonly { readonly uri?: unknown }[] | null
37
+ }
38
+
39
+ export class LspServer {
40
+ #root: string
41
+ readonly #options: ServerOptions
42
+
43
+ readonly #open = new Map<string, string>()
44
+
45
+ #published = new Set<string>()
46
+
47
+ #running = false
48
+ #again = false
49
+ #shutdownRequested = false
50
+ #exited = false
51
+
52
+ constructor(options: ServerOptions) {
53
+ this.#options = options
54
+ this.#root = options.root
55
+ }
56
+
57
+ get exited(): boolean {
58
+ return this.#exited
59
+ }
60
+
61
+ get exitCode(): number {
62
+ return this.#shutdownRequested ? 0 : 1
63
+ }
64
+
65
+ async handle(message: RpcMessage): Promise<void> {
66
+
67
+ if (message.method === undefined) return
68
+
69
+ switch (message.method) {
70
+ case 'initialize':
71
+ this.#root = rootIn(message.params as InitializeParams | undefined) ?? this.#root
72
+ this.#reply(message.id, {
73
+ capabilities: {
74
+ textDocumentSync: { openClose: true, change: SYNC_FULL, save: true },
75
+
76
+ diagnosticProvider: {
77
+ identifier: SOURCE_NAME,
78
+
79
+ interFileDependencies: true,
80
+ workspaceDiagnostics: false,
81
+ },
82
+ },
83
+ serverInfo: { name: named('integrity') },
84
+ })
85
+ return
86
+
87
+ case 'initialized':
88
+
89
+ return
90
+
91
+ case 'shutdown':
92
+ this.#shutdownRequested = true
93
+ this.#reply(message.id, null)
94
+ return
95
+
96
+ case 'exit':
97
+ this.#exited = true
98
+ return
99
+
100
+ case 'textDocument/didOpen': {
101
+ const params = message.params as DidOpenParams | undefined
102
+ const path = this.#pathOf(params?.textDocument?.uri)
103
+ if (path === undefined) return
104
+ this.#open.set(path, typeof params?.textDocument?.text === 'string' ? params.textDocument.text : '')
105
+ await this.#recompute()
106
+ return
107
+ }
108
+
109
+ case 'textDocument/didChange': {
110
+ const params = message.params as DidChangeParams | undefined
111
+ const path = this.#pathOf(params?.textDocument?.uri)
112
+ if (path === undefined) return
113
+
114
+ const latest = params?.contentChanges?.at(-1)?.text
115
+ if (typeof latest === 'string') this.#open.set(path, latest)
116
+ await this.#recompute()
117
+ return
118
+ }
119
+
120
+ case 'textDocument/didSave':
121
+ await this.#recompute()
122
+ return
123
+
124
+ case 'textDocument/didClose': {
125
+ const path = this.#pathOf((message.params as DidOpenParams | undefined)?.textDocument?.uri)
126
+ if (path === undefined) return
127
+ this.#open.delete(path)
128
+ await this.#recompute()
129
+ return
130
+ }
131
+
132
+ case 'textDocument/diagnostic': {
133
+ const path = this.#pathOf((message.params as DidOpenParams | undefined)?.textDocument?.uri)
134
+ const computed = await report(this.#sourceForRoot(), this.#open, this.#published)
135
+ this.#published = new Set([...computed].filter(([, list]) => list.length > 0).map(([file]) => file))
136
+ this.#reply(message.id, {
137
+ kind: 'full',
138
+ items: path === undefined ? [] : (computed.get(path) ?? []),
139
+ })
140
+ return
141
+ }
142
+
143
+ default:
144
+
145
+ if (message.id !== undefined && message.id !== null) {
146
+ this.#options.send({
147
+ jsonrpc: '2.0',
148
+ id: message.id,
149
+ error: { code: METHOD_NOT_FOUND, message: `${message.method} is not implemented by ${SOURCE_NAME}` },
150
+ })
151
+ }
152
+ }
153
+ }
154
+
155
+ #sourceForRoot(): DiagnosticSource {
156
+ return this.#options.sourceFor(this.#root)
157
+ }
158
+
159
+ #reply(id: RpcMessage['id'], result: unknown): void {
160
+ if (id === undefined || id === null) return
161
+ this.#options.send({ jsonrpc: '2.0', id, result })
162
+ }
163
+
164
+ async #recompute(): Promise<void> {
165
+ if (this.#running) {
166
+ this.#again = true
167
+ return
168
+ }
169
+
170
+ this.#running = true
171
+ try {
172
+ do {
173
+ this.#again = false
174
+ await (this.#options.settle?.() ?? sleep(200))
175
+ const computed = await report(this.#sourceForRoot(), this.#open, this.#published)
176
+
177
+ const nonEmpty = new Set<string>()
178
+ for (const [path, diagnostics] of computed) {
179
+ this.#publish(path, diagnostics)
180
+ if (diagnostics.length > 0) nonEmpty.add(path)
181
+ }
182
+ this.#published = nonEmpty
183
+ } while (this.#again)
184
+ } finally {
185
+ this.#running = false
186
+ }
187
+ }
188
+
189
+ #publish(path: string, diagnostics: readonly Diagnostic[]): void {
190
+ this.#options.send({
191
+ jsonrpc: '2.0',
192
+ method: 'textDocument/publishDiagnostics',
193
+ params: { uri: pathToFileURL(resolve(this.#root, path)).href, diagnostics },
194
+ })
195
+ }
196
+
197
+ #pathOf(uri: unknown): string | undefined {
198
+ if (typeof uri !== 'string' || !uri.startsWith('file://')) return undefined
199
+ let absolute: string
200
+ try {
201
+ absolute = fileURLToPath(uri)
202
+ } catch {
203
+ return undefined
204
+ }
205
+ const path = relativeTo(this.#root, absolute).split('\\').join('/')
206
+
207
+ return path.startsWith('..') || path.startsWith('/') ? undefined : path
208
+ }
209
+ }
210
+
211
+ const sleep = (ms: number): Promise<void> => new Promise((done) => setTimeout(done, ms))
212
+
213
+ function rootIn(params: InitializeParams | undefined): string | undefined {
214
+ const folder = params?.workspaceFolders?.[0]?.uri
215
+ for (const uri of [folder, params?.rootUri]) {
216
+ if (typeof uri !== 'string' || !uri.startsWith('file://')) continue
217
+ try {
218
+ return fileURLToPath(uri)
219
+ } catch {
220
+ continue
221
+ }
222
+ }
223
+ return typeof params?.rootPath === 'string' ? params.rootPath : undefined
224
+ }
@@ -1,11 +1,11 @@
1
1
  export const SEALED = {
2
2
  'attach.js': 'e879e49449a3b90118335ab4d2ea6666956da92203438fac7b88ff3a65a79103',
3
- 'check-threads.js': '1754d3acf4e5ea45ce11bf62b1404d59fa027eb22450e253e303acd13f75f6d8',
3
+ 'check-threads.js': '3c943a890af508bfeb0071974ab74ff3f90ff3ce5e7cd1ffa2e2580c6214b3e2',
4
4
  'constraint.js': 'c6eebad19fc11c42c095d82f5658c204f322d136bcbaab98aa9fc06dfa625afb',
5
5
  'generators/compiler.js': 'c6db73bfd31513d8334ef5cd063fb5f12b89c56739ff0f2b73249252d1817cdc',
6
6
  'generators/gate.js': 'f865f469f13b9e3b2eb120bd233b3e286c0df30a4cdf15cd20be4cb995854f88',
7
7
  'generators/issues.js': '21cd7349d3375f15cb8b1a0c31714ab645b021b952275046b56357fba68478b8',
8
- 'generators/publish.js': 'dcbeaf744dd786e8a606fa9d79fb4d3e3cfa6ae1a1432261547f1e4ef186b81e',
8
+ 'generators/publish.js': 'f19114019854ace16fd5ca94c42cbffd838894ce9e7679d6afd4cf26e2bdddd5',
9
9
  'generators/rule.js': '82bdfefbf394528fa4e7994a60eb423b3188277571937e161648f72666728aa4',
10
10
  'generators/seal.js': '39fb8ab5c95b9c0fd1d3d38090785b2e1890653bb1d0454868426b67b28abe3a',
11
11
  'generators/trial.js': 'a2ef8fda83bb2894ae088a0d7887a4e49b9693fa1a7e1345879aaa63041914b0',
@@ -0,0 +1,56 @@
1
+ export const SEALED: Readonly<Record<string, string>> = {
2
+ 'attach.js': 'e879e49449a3b90118335ab4d2ea6666956da92203438fac7b88ff3a65a79103',
3
+ 'check-threads.js': '3c943a890af508bfeb0071974ab74ff3f90ff3ce5e7cd1ffa2e2580c6214b3e2',
4
+ 'constraint.js': 'c6eebad19fc11c42c095d82f5658c204f322d136bcbaab98aa9fc06dfa625afb',
5
+ 'generators/compiler.js': 'c6db73bfd31513d8334ef5cd063fb5f12b89c56739ff0f2b73249252d1817cdc',
6
+ 'generators/gate.js': 'f865f469f13b9e3b2eb120bd233b3e286c0df30a4cdf15cd20be4cb995854f88',
7
+ 'generators/issues.js': '21cd7349d3375f15cb8b1a0c31714ab645b021b952275046b56357fba68478b8',
8
+ 'generators/publish.js': 'f19114019854ace16fd5ca94c42cbffd838894ce9e7679d6afd4cf26e2bdddd5',
9
+ 'generators/rule.js': '82bdfefbf394528fa4e7994a60eb423b3188277571937e161648f72666728aa4',
10
+ 'generators/seal.js': '39fb8ab5c95b9c0fd1d3d38090785b2e1890653bb1d0454868426b67b28abe3a',
11
+ 'generators/trial.js': 'a2ef8fda83bb2894ae088a0d7887a4e49b9693fa1a7e1345879aaa63041914b0',
12
+ 'lint.js': '63f9957725a94498d7e5a49544da343c5f2e1b3b9c2778000dae4a4f39036ef9',
13
+ 'lsp/bin.js': 'b71d5da40c137dbe87083ad29bf1a54e30a190113b8811602263f4c5a4d5a11c',
14
+ 'lsp/diagnostics.js': '0fd15477b4df60e5ec065bb1a85c5daab6b9b433e730cb836b8ddc747554e0b9',
15
+ 'lsp/findings.js': '72f7d80645c0941926a1cc029417fed93eca87e7d53bf2d032f4ef647fb1f168',
16
+ 'lsp/lint.js': '159b07ffa100a62b2cd445d621606ae0d6b68aacc342cb5fdb3a2f4cadd7f508',
17
+ 'lsp/protocol.js': 'd0ad8650307bf563bd058dc2bbbbc0ec9d4fd6dc2c9120bdc261d57c076ddc5d',
18
+ 'lsp/server.js': 'a9f76280e395a47521973a14f3769c4df9e8c1153d29dedc535f56276e5a2e2e',
19
+ 'slopnator.js': '804adba20c269c1134fd09e075d354db73b79cfd00c4b4ce66c1e40279670905',
20
+ 'slopnators/sl_broken_lineage.js': '6d0ee15127166d9792b365e8a4623b5770a57e2ff92c2accf196afbb85052112',
21
+ 'slopnators/sl_declared_elsewhere.js': '394da91ac84369217b4a02bc078ab98f62bf6d522042d84bec82b9705d621751',
22
+ 'slopnators/sl_derived_drift.js': 'f7fb0e26cd6bff9dfe7392e1b1593a51741a6f6fac408bdafbb783e04141cf2f',
23
+ 'slopnators/sl_hardcoded_style.js': 'a2b006990bc33150733645f3c17942ff497e7b3a6565af1331263df36d3b5e0a',
24
+ 'slopnators/sl_inert_island.js': '0900de73ab7439b02a3321a6459e53882cfba6267dc8ef45d630ebe40f4bfa84',
25
+ 'slopnators/sl_inert_slopnator.js': 'e3c35cfa2b094e1e2fd6824b75138cf44e8edb5ec396375d67e47897d84c45d7',
26
+ 'slopnators/sl_missing_row.js': 'd942940388a873bebeff72d93cdb5fef20ba88e4fe29c1aa68e2f42389714a4a',
27
+ 'slopnators/sl_no_barrel.js': '7df2a763d4b4ab16d945341b844271380b25774491bfa9c204dc252910951bc6',
28
+ 'slopnators/sl_no_child_process.js': '24d4f89a1493c95db12e92a34dc4281a422226f638a47c795f26447090e056a1',
29
+ 'slopnators/sl_no_comment.js': '4c4b9b502d6b33ed00d653d0b190d8f95b02bc5ef10da87f97913ca425f59f40',
30
+ 'slopnators/sl_no_dispatch_bypass.js': '1def29fca350861b7185129180edd4727199e0f2bba92a0e0a8c0af437cbb382',
31
+ 'slopnators/sl_no_import_cycle.js': '7ab70e4c182e88f73b4af75cddef9655a49996b8c70fcc2dcebd5f4aeb5fef92',
32
+ 'slopnators/sl_no_such_command.js': 'fe037c2ad6891fd3c0338dd84c7007110c0bcd272a12bd8f1cb85ec779df7a8b',
33
+ 'slopnators/sl_only_takes_a_literal.js': '3d5074f9c6a9bb581c80af8aa6d4a6793e29ac93c793367a67326acc1612c8fe',
34
+ 'slopnators/sl_secret_literal.js': 'bc3987627f3b2892eec169d775771b6309d47b2c93e98c1bccad3f3ed5c5d2b6',
35
+ 'slopnators/sl_stale_path.js': '3cc4050cb9ca2875afbb69c818d20d06e82f36a762ec48bd033c342a7023ff8a',
36
+ 'slopnators/sl_twice_meant.js': '6fc5aaf29b635335bd4963931475e7334110439f8305737786c01be39f81230c',
37
+ 'slopnators/sl_twice_parented.js': 'caa590bc4c11dbebe971f5a611a1712a529cc5a2900f37ba43cfa6b67a0a722b',
38
+ 'slopnators/sl_undeclared_crossing.js': '47e41a38334a4b5a3eb0616d675dc46c5e4d6ce1138710b4470acc276e6ddec5',
39
+ 'slopnators/sl_undeclared_destination.js': '3bbf07029a31eb31fa6556b686d067aae0cd5bf9487fe6ca1896984a55970a5c',
40
+ 'slopnators/sl_undeclared_element.js': '2aa469ffdd58f104936904cbee2c95cb38182a2ac1057e0caa4db3d85e81331c',
41
+ 'slopnators/sl_undeclared_node.js': '84c069a7d1bdec3d3c950ad40e016c129f280796b63a196a7866232e14995443',
42
+ 'slopnators/sl_undeclared_operation.js': 'bc3fa4efc83bf5641020373d0f1506856c6272c3d59e4c4d261e3076f69a155e',
43
+ 'slopnators/sl_undeclared_variant.js': 'e41ec11c9f4baf52dbea1aa7828238e5738de609ea47cdcac793cbe6198df770',
44
+ 'slopnators/sl_undescribed.js': '15c6139a4afc3a4dc1a6196ec1acc2af5066a3330343074f6e2b321291cdbbd0',
45
+ 'slopnators/sl_ungoverned_island.js': '555ae51adf994c5fce4ce51701af7d022be8824c001eeb132403fd6342a12a49',
46
+ 'slopnators/sl_unkept_rows.js': '8abd67e8b8347a6dd9abd20fb7719d4f10553d6f329001a34ccc3e063d448e1d',
47
+ 'slopnators/sl_unnamed_island.js': '0c92c5b6fdf59fb97448f24d72b883cd64317c23280316e043f993e7b75ac1da',
48
+ 'slopnators/sl_unpaired_member.js': 'aafba4f829ef038289176b6e5ea6622494eba1982268d8d52b44f8c9889943e9',
49
+ 'slopnators/sl_unpaired_slopnator.js': '2fa5b8c3c8b6a8a71466086f1d3ae32ea16fd22c49b113225e3c9a7e76eda45e',
50
+ 'slopnators/sl_unprefixed_member.js': '67640331089c55ac59e7950e3c919144740309fb4a91cf0af87c41edb761f75d',
51
+ 'slopnators/sl_unseamed_reach.js': 'd27a64c85b221e7c5bac99ab9d36955ad4945520d2b9e5ec75927574ca454cda',
52
+ 'slopnators/sl_unthreaded.js': 'b686212f3515eb2769030979f7141d236677c9f3c1802e436f254ea5410a2361',
53
+ 'slopnators/sl_untranslated_key.js': '224e80a4f34ce760502ee256347c9191e8d103e8b0ff58ef995c10111ddf2854',
54
+ 'slopnators/sl_unversioned.js': 'df73b3186e42e6b58e7fb3fd48c3146a2d3902a337861a6ccb52f433a569e392',
55
+ 'sweep.js': '2402ba042f87e693bf785d8e81b40870dd2fec539a70a9593d7ecba767dcda2c',
56
+ }