@spexcode/spec-core 0.6.2 → 0.6.3
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/anchors.d.ts +94 -0
- package/dist/anchors.js +730 -0
- package/dist/git.d.ts +166 -0
- package/dist/git.js +2736 -0
- package/dist/graph.d.ts +34 -0
- package/dist/graph.js +237 -0
- package/dist/graphDelta.d.ts +45 -0
- package/dist/graphDelta.js +84 -0
- package/dist/harness-identity.d.ts +31 -0
- package/dist/harness-identity.js +20 -0
- package/dist/identity-presets.d.ts +152 -0
- package/dist/identity-presets.js +132 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +19 -0
- package/dist/layout.d.ts +183 -0
- package/dist/layout.js +548 -0
- package/dist/process-identity.d.ts +37 -0
- package/dist/process-identity.js +214 -0
- package/dist/project-identity.d.ts +12 -0
- package/dist/project-identity.js +71 -0
- package/dist/project-store.d.ts +3 -0
- package/dist/project-store.js +14 -0
- package/dist/resilience.d.ts +2 -0
- package/dist/resilience.js +40 -0
- package/dist/review/index.d.ts +3 -0
- package/{src → dist}/review/index.js +3 -3
- package/dist/review/reviewFilters.d.ts +77 -0
- package/dist/review/reviewFilters.js +308 -0
- package/dist/review/reviewQuery.d.ts +66 -0
- package/dist/review/reviewQuery.js +180 -0
- package/dist/review/session.d.ts +4 -0
- package/dist/review/session.js +8 -0
- package/dist/reviewSnapshot.d.ts +15 -0
- package/dist/reviewSnapshot.js +12 -0
- package/dist/root-lru.d.ts +4 -0
- package/{src/root-lru.ts → dist/root-lru.js} +26 -30
- package/dist/specs.d.ts +117 -0
- package/dist/specs.js +489 -0
- package/package.json +19 -8
- package/src/anchors.ts +0 -728
- package/src/git.ts +0 -2556
- package/src/graph.ts +0 -251
- package/src/harness-identity.ts +0 -26
- package/src/identity-presets.d.ts +0 -13
- package/src/identity-presets.js +0 -138
- package/src/index.ts +0 -20
- package/src/layout.ts +0 -637
- package/src/process-identity.ts +0 -207
- package/src/project-identity.ts +0 -73
- package/src/project-store.ts +0 -17
- package/src/resilience.ts +0 -41
- package/src/review/reviewFilters.js +0 -324
- package/src/review/reviewQuery.js +0 -174
- package/src/review/session.js +0 -13
- package/src/reviewSnapshot.ts +0 -28
- package/src/specs.ts +0 -498
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
// The ONE token-query engine ([[review-query]]) — pure JS, no React/DOM. Both review ListViews, the
|
|
2
|
-
// route layer's legacy replay, and any consumer minting canonical eval/issue addresses import from HERE:
|
|
3
|
-
// the visible query TEXT is the single source of truth, and every tab/menu/autocomplete is only a
|
|
4
|
-
// BUILDER that rewrites tokens in it (GitHub-measured semantics).
|
|
5
|
-
|
|
6
|
-
export const ISSUE_QUERY_DEFAULT = 'is:issue state:open'
|
|
7
|
-
export const EVAL_QUERY_DEFAULT = 'is:eval'
|
|
8
|
-
// the session doors' scoped-list address: the default view, scoped — the text shows exactly that.
|
|
9
|
-
export const scopedEvalQuery = (sessionId) => setToken(EVAL_QUERY_DEFAULT, 'scope', sessionId)
|
|
10
|
-
// the aggregate score/count doors' address ([[eval-score-badge]]): the default view, node-filtered.
|
|
11
|
-
export const nodeEvalQuery = (nodeId) => setToken(EVAL_QUERY_DEFAULT, 'node', nodeId)
|
|
12
|
-
|
|
13
|
-
const KEY_RE = /^([A-Za-z][A-Za-z0-9-]*):(.*)$/s
|
|
14
|
-
const unquote = (v) => (v.length >= 2 && v.startsWith('"') && v.endsWith('"') ? v.slice(1, -1) : v)
|
|
15
|
-
export const quoteValue = (v) => (/\s/.test(String(v)) ? `"${v}"` : String(v))
|
|
16
|
-
|
|
17
|
-
// segment scan preserving EVERY character — whitespace runs and tokens (a `"` swallows spaces until it
|
|
18
|
-
// closes), each with [start,end) offsets — so the aria-hidden highlight overlay mirrors the input
|
|
19
|
-
// glyph-for-glyph and the autocomplete can find the token under the caret.
|
|
20
|
-
export function scanQuery(text) {
|
|
21
|
-
const s = String(text ?? '')
|
|
22
|
-
const out = []
|
|
23
|
-
let i = 0
|
|
24
|
-
while (i < s.length) {
|
|
25
|
-
let j = i
|
|
26
|
-
if (/\s/.test(s[i])) {
|
|
27
|
-
while (j < s.length && /\s/.test(s[j])) j++
|
|
28
|
-
out.push({ ws: true, raw: s.slice(i, j), start: i, end: j })
|
|
29
|
-
} else {
|
|
30
|
-
while (j < s.length && !/\s/.test(s[j])) {
|
|
31
|
-
if (s[j] === '"') {
|
|
32
|
-
j++
|
|
33
|
-
while (j < s.length && s[j] !== '"') j++
|
|
34
|
-
if (j < s.length) j++
|
|
35
|
-
} else j++
|
|
36
|
-
}
|
|
37
|
-
const raw = s.slice(i, j)
|
|
38
|
-
const m = KEY_RE.exec(raw)
|
|
39
|
-
out.push(m
|
|
40
|
-
? { ws: false, raw, start: i, end: j, key: m[1].toLowerCase(), value: unquote(m[2]) }
|
|
41
|
-
: { ws: false, raw, start: i, end: j, key: null, value: unquote(raw) })
|
|
42
|
-
}
|
|
43
|
-
i = j
|
|
44
|
-
}
|
|
45
|
-
return out
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export const tokenize = (text) => scanQuery(text).filter((seg) => !seg.ws)
|
|
49
|
-
export const serialize = (tokens) => tokens.map((t) => t.raw).join(' ')
|
|
50
|
-
export const normalizeQuery = (text) => serialize(tokenize(text))
|
|
51
|
-
export const sameQuery = (a, b) => normalizeQuery(a) === normalizeQuery(b)
|
|
52
|
-
|
|
53
|
-
// duplicate qualifiers: the LAST occurrence wins; bare words all apply.
|
|
54
|
-
export const effectiveTokens = (tokens) => {
|
|
55
|
-
const last = new Map()
|
|
56
|
-
for (const t of tokens) if (t.key != null) last.set(t.key, t)
|
|
57
|
-
return tokens.filter((t) => t.key == null || last.get(t.key) === t)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export const readToken = (text, key) => {
|
|
61
|
-
const tokens = tokenize(text)
|
|
62
|
-
for (let i = tokens.length - 1; i >= 0; i--) if (tokens[i].key === key) return tokens[i].value
|
|
63
|
-
return ''
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// token SURGERY: rewrite the key at its first position, drop later duplicates, append when absent,
|
|
67
|
-
// remove the key entirely on an empty value. Every other token — known or not — survives verbatim.
|
|
68
|
-
export function setToken(text, key, value) {
|
|
69
|
-
const next = value == null || String(value) === ''
|
|
70
|
-
? null
|
|
71
|
-
: { ws: false, raw: `${key}:${quoteValue(value)}`, key, value: String(value) }
|
|
72
|
-
const out = []
|
|
73
|
-
let placed = false
|
|
74
|
-
for (const t of tokenize(text)) {
|
|
75
|
-
if (t.key === key) {
|
|
76
|
-
if (next && !placed) { out.push(next); placed = true }
|
|
77
|
-
continue
|
|
78
|
-
}
|
|
79
|
-
out.push(t)
|
|
80
|
-
}
|
|
81
|
-
if (next && !placed) out.push(next)
|
|
82
|
-
return serialize(out)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// MATCHING is deliberately NOT here: this module owns text — scan/serialize/surgery/suggestions and the
|
|
86
|
-
// canonical-address discipline — while the conjunctive field matching (including the unknown-qualifier
|
|
87
|
-
// IMPOSSIBLE state) lives in the ONE [[review-filters]] engine, reached through its tokenFilterState
|
|
88
|
-
// bridge. A second predicate here would be the exact fork the fusion removed.
|
|
89
|
-
|
|
90
|
-
// canonical address discipline: the default view is the BARE page address; any other state is exactly
|
|
91
|
-
// ?q=<raw text>. An emptied submit falls back to the default (→ bare).
|
|
92
|
-
export const queryParam = (text, defaultText) => {
|
|
93
|
-
const trimmed = String(text ?? '').trim()
|
|
94
|
-
if (!trimmed || sameQuery(trimmed, defaultText)) return null
|
|
95
|
-
return { q: trimmed }
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// A query/filter action resets by omitting page; a PAGINATION action passes a page (including 1) and
|
|
99
|
-
// therefore records GitHub's explicit page=1 history form. The distinction is the action, not equivalence.
|
|
100
|
-
export const reviewRouteQuery = (text, defaultText, page = null) => {
|
|
101
|
-
const query = queryParam(text, defaultText) || {}
|
|
102
|
-
if (page != null) query.page = String(page)
|
|
103
|
-
return Object.keys(query).length ? query : null
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// LEGACY structured params → token pairs. `kind=all` maps to nothing: the evidence default IS all.
|
|
107
|
-
const LEGACY_PARAMS = {
|
|
108
|
-
state: (v) => ['state', v],
|
|
109
|
-
concluded: (v) => (v === '1' ? ['state', 'closed'] : null),
|
|
110
|
-
ok: (v) => (v === '1' ? ['state', 'reviewed'] : null),
|
|
111
|
-
verdict: (v) => ['verdict', v],
|
|
112
|
-
freshness: (v) => ['freshness', v],
|
|
113
|
-
kind: (v) => (v === 'all' ? null : ['evidence', v]),
|
|
114
|
-
store: (v) => ['store', v],
|
|
115
|
-
author: (v) => ['author', v],
|
|
116
|
-
node: (v) => ['node', v],
|
|
117
|
-
filer: (v) => ['filer', v],
|
|
118
|
-
live: (v) => (v === '1' ? ['session', 'present'] : null),
|
|
119
|
-
session: (v) => ['scope', v],
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export const hasLegacyParams = (query) =>
|
|
123
|
-
Object.keys(LEGACY_PARAMS).some((k) => query?.[k] != null && query[k] !== '')
|
|
124
|
-
|
|
125
|
-
// the legacy free q was ONE substring search — it must replay as ONE text token. Quote it whenever the
|
|
126
|
-
// tokenizer would read it as anything else: spaces (several words), a colon (q=drift:check would become
|
|
127
|
-
// an unknown qualifier and match zero), or a stray quote (which would swallow neighbours).
|
|
128
|
-
const freeTextToken = (v) => (/[\s:"]/.test(v) ? `"${v.replace(/"/g, '')}"` : v)
|
|
129
|
-
|
|
130
|
-
// a legacy LIST address replays as the FULL visible state: the page's default tokens with each legacy
|
|
131
|
-
// param surgically applied (live=1→session:present, session=<id>→scope:<id>, ok=1→state:reviewed,
|
|
132
|
-
// kind→evidence:), the free-text q appended as ONE bare/phrase token preserving the old
|
|
133
|
-
// single-substring search. Returns null when nothing legacy is present.
|
|
134
|
-
export function legacyQueryText(defaultText, query) {
|
|
135
|
-
if (!hasLegacyParams(query)) return null
|
|
136
|
-
let text = defaultText
|
|
137
|
-
for (const [param, toPair] of Object.entries(LEGACY_PARAMS)) {
|
|
138
|
-
const v = query[param]
|
|
139
|
-
if (v == null || v === '') continue
|
|
140
|
-
const pair = toPair(String(v))
|
|
141
|
-
if (pair) text = setToken(text, pair[0], pair[1])
|
|
142
|
-
}
|
|
143
|
-
const free = String(query.q ?? '').trim()
|
|
144
|
-
if (free) text = `${text} ${freeTextToken(free)}`.trim()
|
|
145
|
-
return text
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// inline autocomplete at the caret — client-side and BOUNDED. A bare prefix completes qualifier KEYS
|
|
149
|
-
// (insert `key:`, keep typing); a `key:prefix` completes VALUES from the page-supplied candidate list
|
|
150
|
-
// only (data-derived; scope = sessions on the current board), capped at 8. Everything else stays
|
|
151
|
-
// hand-typable and submits verbatim.
|
|
152
|
-
export function suggestAt(text, caret, keys = [], values = {}) {
|
|
153
|
-
const s = String(text ?? '')
|
|
154
|
-
const at = Math.max(0, Math.min(caret ?? s.length, s.length))
|
|
155
|
-
const seg = scanQuery(s).find((g) => !g.ws && g.start < at && at <= g.end)
|
|
156
|
-
if (!seg) return { start: at, end: at, items: [] }
|
|
157
|
-
const typed = s.slice(seg.start, at)
|
|
158
|
-
const m = KEY_RE.exec(typed)
|
|
159
|
-
if (!m) {
|
|
160
|
-
const w = typed.toLowerCase()
|
|
161
|
-
if (!w || w.includes('"')) return { start: seg.start, end: seg.end, items: [] }
|
|
162
|
-
const items = keys.filter((k) => k.startsWith(w)).slice(0, 8)
|
|
163
|
-
.map((k) => ({ type: 'key', key: k, insert: `${k}:` }))
|
|
164
|
-
return { start: seg.start, end: seg.end, items }
|
|
165
|
-
}
|
|
166
|
-
const key = m[1].toLowerCase()
|
|
167
|
-
const prefix = m[2].replace(/^"/, '').replace(/"$/, '').toLowerCase()
|
|
168
|
-
const pool = values[key] || []
|
|
169
|
-
const items = pool
|
|
170
|
-
.filter((c) => String(c.value).toLowerCase().startsWith(prefix) && String(c.value).toLowerCase() !== prefix)
|
|
171
|
-
.slice(0, 8)
|
|
172
|
-
.map((c) => ({ type: 'value', key, value: String(c.value), label: c.label || null, insert: `${key}:${quoteValue(c.value)} ` }))
|
|
173
|
-
return { start: seg.start, end: seg.end, items }
|
|
174
|
-
}
|
package/src/review/session.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export const sessionPresent = (sessions, id) => {
|
|
2
|
-
const session = id ? (sessions || []).find((item) => item.id === id) : null
|
|
3
|
-
return session || null
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export const sessionHeadline = (session) =>
|
|
7
|
-
session?.title || session?.headline || session?.name || session?.activity || session?.note
|
|
8
|
-
|| session?.promptPreview || session?.node || session?.raw?.title || session?.branch || session?.id
|
|
9
|
-
|
|
10
|
-
export const sessionHandle = (session) =>
|
|
11
|
-
session?.label || session?.name || session?.node || session?.title || session?.branch || session?.id
|
|
12
|
-
|
|
13
|
-
export const sessionTitle = sessionHeadline
|
package/src/reviewSnapshot.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export type ReviewEvalNode = {
|
|
2
|
-
id: string
|
|
3
|
-
hue?: number
|
|
4
|
-
scenarios: any[]
|
|
5
|
-
evals: any[]
|
|
6
|
-
readings: any[]
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type ReviewSnapshot = {
|
|
10
|
-
issues: any[]
|
|
11
|
-
evalNodes: ReviewEvalNode[]
|
|
12
|
-
forgeRevision: number
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
let current: ReviewSnapshot | null = null
|
|
16
|
-
|
|
17
|
-
export function publishReviewSnapshot(snapshot: ReviewSnapshot): void {
|
|
18
|
-
current = snapshot
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function readReviewSnapshot(): ReviewSnapshot {
|
|
22
|
-
if (!current) throw new Error('review snapshot is unavailable before the first successful graph build')
|
|
23
|
-
return current
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function hasReviewSnapshot(): boolean {
|
|
27
|
-
return current !== null
|
|
28
|
-
}
|