@anokye-labs/kbexplorer-engine 0.1.0
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/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/build-manifest-BT0sY84J.d.ts +239 -0
- package/dist/build-manifest-BkgZUL0E.d.cts +239 -0
- package/dist/chunk-ASLGNOYV.js +220 -0
- package/dist/chunk-BA526FDQ.js +16 -0
- package/dist/chunk-DJ2DBEEK.js +424 -0
- package/dist/chunk-FJ4GTN4U.js +973 -0
- package/dist/chunk-HXAS3BSD.js +187 -0
- package/dist/chunk-IWEURXYH.js +273 -0
- package/dist/chunk-JNQVSNLC.js +55 -0
- package/dist/chunk-MW4BLXVK.js +72 -0
- package/dist/chunk-NOGBKE7C.js +351 -0
- package/dist/chunk-XVI6CBSX.js +412 -0
- package/dist/chunk-YTMIERYM.js +67 -0
- package/dist/github-api-source-KV5HZARH.js +4 -0
- package/dist/index.cjs +6218 -0
- package/dist/index.d.cts +1396 -0
- package/dist/index.d.ts +1396 -0
- package/dist/index.js +2892 -0
- package/dist/node-wasm-VSXBOCPD.js +9 -0
- package/dist/plugin-loader-K4VU6ODV.js +2 -0
- package/dist/repo-data-0JvFdLGv.d.cts +461 -0
- package/dist/repo-data-0JvFdLGv.d.ts +461 -0
- package/dist/sources.cjs +1191 -0
- package/dist/sources.d.cts +194 -0
- package/dist/sources.d.ts +194 -0
- package/dist/sources.js +357 -0
- package/dist/sqlite-graph-store-NH2NHL2B.js +2 -0
- package/dist/sqlite-runtime-CysPjjzX.d.cts +96 -0
- package/dist/sqlite-runtime-CysPjjzX.d.ts +96 -0
- package/dist/store-orchestrator-337XWIXO.js +5 -0
- package/dist/store.cjs +1112 -0
- package/dist/store.d.cts +37 -0
- package/dist/store.d.ts +37 -0
- package/dist/store.js +7 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anokye Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# @anokye-labs/kbexplorer-engine
|
|
2
|
+
|
|
3
|
+
The runtime-agnostic engine for kbexplorer: it turns a knowledge-base source
|
|
4
|
+
(a manifest, the GitHub API, or a local directory) into a computed `KBGraph`,
|
|
5
|
+
and provides a small scriptable query API over that graph. The core (`.`) entry
|
|
6
|
+
stays portable across Node and browser-like environments; Node-specific pieces
|
|
7
|
+
(like `FileSystemSource`) live behind the `./sources` subpath.
|
|
8
|
+
|
|
9
|
+
## Develop
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install
|
|
13
|
+
npm run typecheck
|
|
14
|
+
npm run build
|
|
15
|
+
npm test
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Package shape
|
|
19
|
+
|
|
20
|
+
- `.` — the engine core: graph building (`buildGraph`), the unified loader
|
|
21
|
+
(`loadKnowledgeBase`), the query helpers, providers, parsing, identity, and
|
|
22
|
+
supporting types.
|
|
23
|
+
- `./sources` — source adapters (`ManifestSource`, `GitHubApiSource`,
|
|
24
|
+
`FileSystemSource`) and the `RepoSource` / `RepoData` contracts.
|
|
25
|
+
- `./store` — the optional sqlite-backed graph store entry point.
|
|
26
|
+
|
|
27
|
+
## Loading a knowledge base
|
|
28
|
+
|
|
29
|
+
`loadKnowledgeBase` has two call shapes, distinguished by the first argument:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { loadKnowledgeBase } from '@anokye-labs/kbexplorer-engine';
|
|
33
|
+
import { GitHubApiSource, FileSystemSource } from '@anokye-labs/kbexplorer-engine/sources';
|
|
34
|
+
|
|
35
|
+
// Config-first (scripting): returns a bare KBGraph.
|
|
36
|
+
// With no `source`, a GitHubApiSource is built from config.source.
|
|
37
|
+
const graph = await loadKnowledgeBase(config);
|
|
38
|
+
const localGraph = await loadKnowledgeBase(config, { source: new FileSystemSource('./my-kb') });
|
|
39
|
+
|
|
40
|
+
// Positional (advanced): returns { graph, config }. This is the SHA-pinned
|
|
41
|
+
// contract consumed by kbexplorer-template — it is unchanged.
|
|
42
|
+
const { graph: g, config: c } = await loadKnowledgeBase(new GitHubApiSource(config.source), config);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The two forms differ only in ergonomics and return shape; the positional form
|
|
46
|
+
is fully preserved.
|
|
47
|
+
|
|
48
|
+
## Query API
|
|
49
|
+
|
|
50
|
+
Pure, runtime-agnostic helpers over a computed `KBGraph`:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import {
|
|
54
|
+
getNode, findNodes, neighbors, related, subgraph, shortestPath,
|
|
55
|
+
} from '@anokye-labs/kbexplorer-engine';
|
|
56
|
+
|
|
57
|
+
getNode(graph, 'home'); // KBNode | undefined
|
|
58
|
+
findNodes(graph, n => n.cluster === 'engine'); // KBNode[]
|
|
59
|
+
neighbors(graph, 'home', { direction: 'out' }); // KBNode[]
|
|
60
|
+
related(graph, 'home'); // KBNode[] (precomputed index)
|
|
61
|
+
subgraph(graph, 'home', { radius: 2 }); // KBGraph neighborhood
|
|
62
|
+
shortestPath(graph, 'home', 'loader'); // string[] | null
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Sources
|
|
66
|
+
|
|
67
|
+
- `ManifestSource(manifest, config)` — a frozen, pre-built snapshot.
|
|
68
|
+
- `GitHubApiSource(sourceConfig)` — the live GitHub API.
|
|
69
|
+
- `FileSystemSource(rootDir, options?)` — **Node-only** adapter that walks a
|
|
70
|
+
local directory into the same `RepoData` bundle a manifest yields (authored
|
|
71
|
+
`content/`, `.github` structural files, `content-model/`, README).
|
|
72
|
+
|
|
73
|
+
## Recipes
|
|
74
|
+
|
|
75
|
+
Runnable examples of the scriptable API against an in-repo fixture live in
|
|
76
|
+
[`docs/recipes.md`](./docs/recipes.md). CI builds the package and runs all eight
|
|
77
|
+
scripts on every pull request.
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { SourceConfig } from '@anokye-labs/kbexplorer-core';
|
|
2
|
+
import { E as EngineEnv, k as GHCommit, G as GHIssue, c as GHRelease, b as GHTreeItem, a as ContentModelSource, R as RepoSource } from './repo-data-0JvFdLGv.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* GitHub API client for fetching repository content at runtime.
|
|
6
|
+
*
|
|
7
|
+
* Runtime-agnostic, boundary-pure port of kbexplorer-template's
|
|
8
|
+
* `src/api/github.ts` (anokye-labs/kbexplorer-template#472, slice 4/5). The
|
|
9
|
+
* template client wrapped every call in a browser-storage cache and read the
|
|
10
|
+
* API base from a Vite build-time env at module scope. Neither of those belongs
|
|
11
|
+
* in this runtime-agnostic engine package, so this port drops the cache layer
|
|
12
|
+
* entirely (it stays template-side as a thin wrapper) and injects the API base
|
|
13
|
+
* per call via an optional {@link EngineEnv} argument — mirroring the injection
|
|
14
|
+
* idiom already used in `src/store/config.ts`. `resolveImageUrl` (a pure
|
|
15
|
+
* UI/dev-server concern) is intentionally not ported; it stays in template.
|
|
16
|
+
*
|
|
17
|
+
* Supports two modes:
|
|
18
|
+
* - authored: fetches markdown files from a content directory
|
|
19
|
+
* - repo-aware: fetches issues, PRs, README, and file tree
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The distinct GitHub REST endpoint path patterns this client's `ghFetch` call
|
|
24
|
+
* sites hit — one entry per endpoint family. Exported as a single source of
|
|
25
|
+
* truth so an out-of-repo drift-detection test (kbexplorer-template's
|
|
26
|
+
* `twin-coverage.test.ts`) can import it instead of regexing this file's raw
|
|
27
|
+
* source text: adding a new endpoint here fails that test until a matching twin
|
|
28
|
+
* route exists. Keep this in sync with the paths passed to `ghFetch` below.
|
|
29
|
+
*/
|
|
30
|
+
declare const GITHUB_ENDPOINT_PATTERNS: readonly ["contents/", "git/trees/", "issues", "pulls", "commits", "releases", "branches", "languages"];
|
|
31
|
+
declare class NotModifiedError extends Error {
|
|
32
|
+
constructor();
|
|
33
|
+
}
|
|
34
|
+
declare class RateLimitError extends Error {
|
|
35
|
+
resetAt?: Date;
|
|
36
|
+
constructor(resetAt?: Date);
|
|
37
|
+
}
|
|
38
|
+
declare class GitHubApiError extends Error {
|
|
39
|
+
status: number;
|
|
40
|
+
constructor(status: number, body: string);
|
|
41
|
+
}
|
|
42
|
+
interface GHFileContent {
|
|
43
|
+
name: string;
|
|
44
|
+
path: string;
|
|
45
|
+
sha: string;
|
|
46
|
+
content: string;
|
|
47
|
+
encoding: string;
|
|
48
|
+
}
|
|
49
|
+
interface CacheStore {
|
|
50
|
+
get<T>(key: string): T | undefined;
|
|
51
|
+
set<T>(key: string, value: T): void;
|
|
52
|
+
}
|
|
53
|
+
/** Fetch and decode a single file from the repo. */
|
|
54
|
+
declare function fetchFile(source: SourceConfig, path: string, env?: EngineEnv, cache?: CacheStore): Promise<string>;
|
|
55
|
+
/** List all files in a directory (recursive via Git Trees API). */
|
|
56
|
+
declare function fetchTree(source: SourceConfig, path?: string, env?: EngineEnv, cache?: CacheStore): Promise<GHTreeItem[]>;
|
|
57
|
+
/** Fetch issues (not PRs) from the repo. */
|
|
58
|
+
declare function fetchIssues(source: SourceConfig, env?: EngineEnv, cache?: CacheStore): Promise<GHIssue[]>;
|
|
59
|
+
/** Fetch pull requests from the repo. */
|
|
60
|
+
declare function fetchPullRequests(source: SourceConfig, env?: EngineEnv, cache?: CacheStore): Promise<GHIssue[]>;
|
|
61
|
+
/** Fetch recent commits from the repo. */
|
|
62
|
+
declare function fetchCommits(source: SourceConfig, count?: number, env?: EngineEnv, cache?: CacheStore): Promise<GHCommit[]>;
|
|
63
|
+
/** Fetch GitHub releases (non-draft, newest-first, capped at 30). */
|
|
64
|
+
declare function fetchReleases(source: SourceConfig, limit?: number, env?: EngineEnv, cache?: CacheStore): Promise<GHRelease[]>;
|
|
65
|
+
/** Fetch multiple files in parallel. */
|
|
66
|
+
declare function fetchFiles(source: SourceConfig, paths: string[], env?: EngineEnv, cache?: CacheStore): Promise<Map<string, string>>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* RepoManifest — the pre-built snapshot shape a {@link ManifestSource} is
|
|
70
|
+
* constructed from (relocated from kbexplorer-template's `src/engine/
|
|
71
|
+
* local-loader.ts` in anokye-labs/kbexplorer-template#472, slice 4/5).
|
|
72
|
+
*
|
|
73
|
+
* It moves into this package because `ManifestSource`'s constructor takes one.
|
|
74
|
+
* The manifest-generation script and the local (manifest-import) loader remain
|
|
75
|
+
* template-side; only the interface travels here.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
interface RepoManifest {
|
|
79
|
+
configRaw: string | null;
|
|
80
|
+
authoredContent: Record<string, string>;
|
|
81
|
+
tree: Array<{
|
|
82
|
+
path: string;
|
|
83
|
+
type: 'blob' | 'tree';
|
|
84
|
+
size?: number;
|
|
85
|
+
}>;
|
|
86
|
+
readme: string | null;
|
|
87
|
+
issues: GHIssue[];
|
|
88
|
+
pullRequests: Array<{
|
|
89
|
+
number: number;
|
|
90
|
+
title: string;
|
|
91
|
+
body: string;
|
|
92
|
+
state: string;
|
|
93
|
+
labels: Array<{
|
|
94
|
+
name: string;
|
|
95
|
+
color: string;
|
|
96
|
+
}>;
|
|
97
|
+
html_url: string;
|
|
98
|
+
created_at: string;
|
|
99
|
+
updated_at: string;
|
|
100
|
+
head_branch?: string;
|
|
101
|
+
}>;
|
|
102
|
+
commits: Array<{
|
|
103
|
+
sha: string;
|
|
104
|
+
commit: {
|
|
105
|
+
message: string;
|
|
106
|
+
author: {
|
|
107
|
+
name: string;
|
|
108
|
+
date: string;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
html_url: string;
|
|
112
|
+
}>;
|
|
113
|
+
branches?: Array<{
|
|
114
|
+
name: string;
|
|
115
|
+
protected: boolean;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* GitHub releases (non-draft, newest-first, capped at 30). Absent/empty in
|
|
119
|
+
* repos without releases — the WorkProvider handles this gracefully (safe no-op).
|
|
120
|
+
*/
|
|
121
|
+
releases?: GHRelease[];
|
|
122
|
+
repoMetadata?: {
|
|
123
|
+
name: string;
|
|
124
|
+
description: string;
|
|
125
|
+
html_url: string;
|
|
126
|
+
/** Repo homepage URL (blank when unset). Matches the old generator's 12-key `fetchRepoMetadata` shape. */
|
|
127
|
+
homepage: string;
|
|
128
|
+
default_branch: string;
|
|
129
|
+
stargazers_count: number;
|
|
130
|
+
forks_count: number;
|
|
131
|
+
private: boolean;
|
|
132
|
+
topics: string[];
|
|
133
|
+
primary_language: string;
|
|
134
|
+
languages: Array<{
|
|
135
|
+
name: string;
|
|
136
|
+
size: number;
|
|
137
|
+
}>;
|
|
138
|
+
owner: {
|
|
139
|
+
login: string;
|
|
140
|
+
avatar_url: string;
|
|
141
|
+
};
|
|
142
|
+
} | null;
|
|
143
|
+
nodemapRaw?: string | null;
|
|
144
|
+
nodemapFiles?: Record<string, string>;
|
|
145
|
+
nodemapDirs?: Record<string, Array<{
|
|
146
|
+
path: string;
|
|
147
|
+
type: 'blob' | 'tree';
|
|
148
|
+
size?: number;
|
|
149
|
+
}>>;
|
|
150
|
+
structuredNodeMapRaw?: string | null;
|
|
151
|
+
structuralFiles?: Record<string, string>;
|
|
152
|
+
/**
|
|
153
|
+
* Optional content-model source (F2): schema files + entity files keyed by
|
|
154
|
+
* path relative to `structuredContent.path` (default `content-model/`). Absent
|
|
155
|
+
* (null) in repos without a content model — the ContentModelProvider is then a
|
|
156
|
+
* safe no-op.
|
|
157
|
+
*/
|
|
158
|
+
contentModel?: ContentModelSource | null;
|
|
159
|
+
/**
|
|
160
|
+
* Optional raw contents of the dedicated theme file referenced by
|
|
161
|
+
* `config.theme.themesFile` (F5/T5.1). Read at manifest-generation time from
|
|
162
|
+
* the host repo and merged into the theme block in local mode the same way
|
|
163
|
+
* the remote loader fetches it at runtime. Null/absent when no themesFile is
|
|
164
|
+
* configured or the file is missing — a safe no-op.
|
|
165
|
+
*/
|
|
166
|
+
themeFileRaw?: string | null;
|
|
167
|
+
generatedAt: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* buildManifest() — the manifest PRODUCER (anokye-labs/kbexplorer-engine#17,
|
|
172
|
+
* the anchor child of the thin-CLI/fat-engine epic
|
|
173
|
+
* anokye-labs/kbexplorer-template#463).
|
|
174
|
+
*
|
|
175
|
+
* The engine already owned the manifest **consume** path — `ManifestSource`,
|
|
176
|
+
* the `RepoSource` acquisition adapters (`GitHubApiSource`, `FileSystemSource`),
|
|
177
|
+
* and the `RepoManifest` snapshot shape itself (`./repo-manifest`). What was
|
|
178
|
+
* missing was the **producer**: something that turns a `RepoSource` into a
|
|
179
|
+
* `RepoManifest`. That logic lived duplicated outside the engine, in
|
|
180
|
+
* kbexplorer-template's `scripts/generate-manifest.js` and the CLI's
|
|
181
|
+
* byte-parallel `src/lib/repo-manifest.ts` fork (flagged by cli#232).
|
|
182
|
+
*
|
|
183
|
+
* `buildManifest` closes that loop *without* re-implementing any fetching:
|
|
184
|
+
* acquisition (GitHub REST calls, local filesystem walks) already lives on the
|
|
185
|
+
* injected `RepoSource`. This function drives it through the existing
|
|
186
|
+
* `getRepoData()` seam exactly once and re-shapes the normalized `RepoData`
|
|
187
|
+
* bundle into the serializable `RepoManifest` snapshot — the mechanical,
|
|
188
|
+
* source-agnostic half of what the template script does before it writes
|
|
189
|
+
* `repo-manifest.json` to disk. It is deliberately the mirror image of
|
|
190
|
+
* `ManifestSource.getRepoData()` (which maps `RepoManifest` → `RepoData`);
|
|
191
|
+
* this maps `RepoData` → `RepoManifest`.
|
|
192
|
+
*
|
|
193
|
+
* Signature note (`buildManifest(source, options)`, not `(source, config)`):
|
|
194
|
+
* every `RepoManifest` field this function assembles comes straight off
|
|
195
|
+
* `RepoData` — none of them need a resolved `KBConfig`. The two fields a
|
|
196
|
+
* `RepoSource` cannot uniformly supply are threaded in via `options` instead:
|
|
197
|
+
*
|
|
198
|
+
* - `configRaw` — the raw (pre-YAML-parse) `config.yaml` text. `KBConfig`
|
|
199
|
+
* (what `GitHubApiSource.resolveConfig()` / a local `loadConfig()` caller
|
|
200
|
+
* would have) is already-parsed, so it can't round-trip back to source
|
|
201
|
+
* text. Callers already hold (or can cheaply obtain) the raw text — a local
|
|
202
|
+
* caller reads the same file `FileSystemSource` walks, and a remote caller
|
|
203
|
+
* already has the engine's exported `fetchFile` to hand — so this function
|
|
204
|
+
* accepts it rather than duplicating a second raw-fetch code path.
|
|
205
|
+
* - `generatedAt` — the snapshot timestamp. Defaults to
|
|
206
|
+
* `new Date().toISOString()` but is overridable so callers (and this
|
|
207
|
+
* package's own tests) can produce deterministic output for
|
|
208
|
+
* idempotency/`--check`-drift assertions, matching the issue's requirement
|
|
209
|
+
* that source-derived fields (everything *except* `generatedAt`/live
|
|
210
|
+
* GitHub state) be deterministic.
|
|
211
|
+
*
|
|
212
|
+
* `nodemapFiles`/`nodemapDirs` are passed through as-is from `RepoData` when a
|
|
213
|
+
* source populates them (neither shipped `RepoSource` does yet — both leave
|
|
214
|
+
* `nodemapRaw: null`, matching their existing, unchanged behavior — so
|
|
215
|
+
* `buildManifest` carries whatever future sources supply without
|
|
216
|
+
* re-implementing the template script's nodemap-collection logic itself,
|
|
217
|
+
* which is out of this issue's scope: see the issue's "non-goals").
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
/** Options for fields `RepoData` cannot uniformly supply (see module docs). */
|
|
221
|
+
interface BuildManifestOptions {
|
|
222
|
+
/** Raw (pre-parse) `config.yaml` text to embed, or `null`/omitted when there is none. */
|
|
223
|
+
configRaw?: string | null;
|
|
224
|
+
/**
|
|
225
|
+
* Overrides the snapshot timestamp. Defaults to `new Date().toISOString()`.
|
|
226
|
+
* Pass a fixed value for deterministic/idempotency tests and `--check` drift
|
|
227
|
+
* comparisons, which must ignore this volatile field regardless.
|
|
228
|
+
*/
|
|
229
|
+
generatedAt?: string;
|
|
230
|
+
/** When set, overlay the live-GitHub fields from this source onto the primary-source manifest (hybrid: local content + live augmentation). */
|
|
231
|
+
augmentFrom?: RepoSource;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Drive `source.getRepoData()` once and assemble a `RepoManifest` snapshot
|
|
235
|
+
* from the result — the producer half of the `ManifestSource` contract.
|
|
236
|
+
*/
|
|
237
|
+
declare function buildManifest(source: RepoSource, options?: BuildManifestOptions): Promise<RepoManifest>;
|
|
238
|
+
|
|
239
|
+
export { type BuildManifestOptions as B, type CacheStore as C, type GHFileContent as G, NotModifiedError as N, type RepoManifest as R, fetchFile as a, buildManifest as b, fetchFiles as c, fetchIssues as d, fetchPullRequests as e, fetchCommits as f, fetchReleases as g, fetchTree as h, GITHUB_ENDPOINT_PATTERNS as i, GitHubApiError as j, RateLimitError as k };
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { SourceConfig } from '@anokye-labs/kbexplorer-core';
|
|
2
|
+
import { E as EngineEnv, k as GHCommit, G as GHIssue, c as GHRelease, b as GHTreeItem, a as ContentModelSource, R as RepoSource } from './repo-data-0JvFdLGv.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* GitHub API client for fetching repository content at runtime.
|
|
6
|
+
*
|
|
7
|
+
* Runtime-agnostic, boundary-pure port of kbexplorer-template's
|
|
8
|
+
* `src/api/github.ts` (anokye-labs/kbexplorer-template#472, slice 4/5). The
|
|
9
|
+
* template client wrapped every call in a browser-storage cache and read the
|
|
10
|
+
* API base from a Vite build-time env at module scope. Neither of those belongs
|
|
11
|
+
* in this runtime-agnostic engine package, so this port drops the cache layer
|
|
12
|
+
* entirely (it stays template-side as a thin wrapper) and injects the API base
|
|
13
|
+
* per call via an optional {@link EngineEnv} argument — mirroring the injection
|
|
14
|
+
* idiom already used in `src/store/config.ts`. `resolveImageUrl` (a pure
|
|
15
|
+
* UI/dev-server concern) is intentionally not ported; it stays in template.
|
|
16
|
+
*
|
|
17
|
+
* Supports two modes:
|
|
18
|
+
* - authored: fetches markdown files from a content directory
|
|
19
|
+
* - repo-aware: fetches issues, PRs, README, and file tree
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The distinct GitHub REST endpoint path patterns this client's `ghFetch` call
|
|
24
|
+
* sites hit — one entry per endpoint family. Exported as a single source of
|
|
25
|
+
* truth so an out-of-repo drift-detection test (kbexplorer-template's
|
|
26
|
+
* `twin-coverage.test.ts`) can import it instead of regexing this file's raw
|
|
27
|
+
* source text: adding a new endpoint here fails that test until a matching twin
|
|
28
|
+
* route exists. Keep this in sync with the paths passed to `ghFetch` below.
|
|
29
|
+
*/
|
|
30
|
+
declare const GITHUB_ENDPOINT_PATTERNS: readonly ["contents/", "git/trees/", "issues", "pulls", "commits", "releases", "branches", "languages"];
|
|
31
|
+
declare class NotModifiedError extends Error {
|
|
32
|
+
constructor();
|
|
33
|
+
}
|
|
34
|
+
declare class RateLimitError extends Error {
|
|
35
|
+
resetAt?: Date;
|
|
36
|
+
constructor(resetAt?: Date);
|
|
37
|
+
}
|
|
38
|
+
declare class GitHubApiError extends Error {
|
|
39
|
+
status: number;
|
|
40
|
+
constructor(status: number, body: string);
|
|
41
|
+
}
|
|
42
|
+
interface GHFileContent {
|
|
43
|
+
name: string;
|
|
44
|
+
path: string;
|
|
45
|
+
sha: string;
|
|
46
|
+
content: string;
|
|
47
|
+
encoding: string;
|
|
48
|
+
}
|
|
49
|
+
interface CacheStore {
|
|
50
|
+
get<T>(key: string): T | undefined;
|
|
51
|
+
set<T>(key: string, value: T): void;
|
|
52
|
+
}
|
|
53
|
+
/** Fetch and decode a single file from the repo. */
|
|
54
|
+
declare function fetchFile(source: SourceConfig, path: string, env?: EngineEnv, cache?: CacheStore): Promise<string>;
|
|
55
|
+
/** List all files in a directory (recursive via Git Trees API). */
|
|
56
|
+
declare function fetchTree(source: SourceConfig, path?: string, env?: EngineEnv, cache?: CacheStore): Promise<GHTreeItem[]>;
|
|
57
|
+
/** Fetch issues (not PRs) from the repo. */
|
|
58
|
+
declare function fetchIssues(source: SourceConfig, env?: EngineEnv, cache?: CacheStore): Promise<GHIssue[]>;
|
|
59
|
+
/** Fetch pull requests from the repo. */
|
|
60
|
+
declare function fetchPullRequests(source: SourceConfig, env?: EngineEnv, cache?: CacheStore): Promise<GHIssue[]>;
|
|
61
|
+
/** Fetch recent commits from the repo. */
|
|
62
|
+
declare function fetchCommits(source: SourceConfig, count?: number, env?: EngineEnv, cache?: CacheStore): Promise<GHCommit[]>;
|
|
63
|
+
/** Fetch GitHub releases (non-draft, newest-first, capped at 30). */
|
|
64
|
+
declare function fetchReleases(source: SourceConfig, limit?: number, env?: EngineEnv, cache?: CacheStore): Promise<GHRelease[]>;
|
|
65
|
+
/** Fetch multiple files in parallel. */
|
|
66
|
+
declare function fetchFiles(source: SourceConfig, paths: string[], env?: EngineEnv, cache?: CacheStore): Promise<Map<string, string>>;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* RepoManifest — the pre-built snapshot shape a {@link ManifestSource} is
|
|
70
|
+
* constructed from (relocated from kbexplorer-template's `src/engine/
|
|
71
|
+
* local-loader.ts` in anokye-labs/kbexplorer-template#472, slice 4/5).
|
|
72
|
+
*
|
|
73
|
+
* It moves into this package because `ManifestSource`'s constructor takes one.
|
|
74
|
+
* The manifest-generation script and the local (manifest-import) loader remain
|
|
75
|
+
* template-side; only the interface travels here.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
interface RepoManifest {
|
|
79
|
+
configRaw: string | null;
|
|
80
|
+
authoredContent: Record<string, string>;
|
|
81
|
+
tree: Array<{
|
|
82
|
+
path: string;
|
|
83
|
+
type: 'blob' | 'tree';
|
|
84
|
+
size?: number;
|
|
85
|
+
}>;
|
|
86
|
+
readme: string | null;
|
|
87
|
+
issues: GHIssue[];
|
|
88
|
+
pullRequests: Array<{
|
|
89
|
+
number: number;
|
|
90
|
+
title: string;
|
|
91
|
+
body: string;
|
|
92
|
+
state: string;
|
|
93
|
+
labels: Array<{
|
|
94
|
+
name: string;
|
|
95
|
+
color: string;
|
|
96
|
+
}>;
|
|
97
|
+
html_url: string;
|
|
98
|
+
created_at: string;
|
|
99
|
+
updated_at: string;
|
|
100
|
+
head_branch?: string;
|
|
101
|
+
}>;
|
|
102
|
+
commits: Array<{
|
|
103
|
+
sha: string;
|
|
104
|
+
commit: {
|
|
105
|
+
message: string;
|
|
106
|
+
author: {
|
|
107
|
+
name: string;
|
|
108
|
+
date: string;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
html_url: string;
|
|
112
|
+
}>;
|
|
113
|
+
branches?: Array<{
|
|
114
|
+
name: string;
|
|
115
|
+
protected: boolean;
|
|
116
|
+
}>;
|
|
117
|
+
/**
|
|
118
|
+
* GitHub releases (non-draft, newest-first, capped at 30). Absent/empty in
|
|
119
|
+
* repos without releases — the WorkProvider handles this gracefully (safe no-op).
|
|
120
|
+
*/
|
|
121
|
+
releases?: GHRelease[];
|
|
122
|
+
repoMetadata?: {
|
|
123
|
+
name: string;
|
|
124
|
+
description: string;
|
|
125
|
+
html_url: string;
|
|
126
|
+
/** Repo homepage URL (blank when unset). Matches the old generator's 12-key `fetchRepoMetadata` shape. */
|
|
127
|
+
homepage: string;
|
|
128
|
+
default_branch: string;
|
|
129
|
+
stargazers_count: number;
|
|
130
|
+
forks_count: number;
|
|
131
|
+
private: boolean;
|
|
132
|
+
topics: string[];
|
|
133
|
+
primary_language: string;
|
|
134
|
+
languages: Array<{
|
|
135
|
+
name: string;
|
|
136
|
+
size: number;
|
|
137
|
+
}>;
|
|
138
|
+
owner: {
|
|
139
|
+
login: string;
|
|
140
|
+
avatar_url: string;
|
|
141
|
+
};
|
|
142
|
+
} | null;
|
|
143
|
+
nodemapRaw?: string | null;
|
|
144
|
+
nodemapFiles?: Record<string, string>;
|
|
145
|
+
nodemapDirs?: Record<string, Array<{
|
|
146
|
+
path: string;
|
|
147
|
+
type: 'blob' | 'tree';
|
|
148
|
+
size?: number;
|
|
149
|
+
}>>;
|
|
150
|
+
structuredNodeMapRaw?: string | null;
|
|
151
|
+
structuralFiles?: Record<string, string>;
|
|
152
|
+
/**
|
|
153
|
+
* Optional content-model source (F2): schema files + entity files keyed by
|
|
154
|
+
* path relative to `structuredContent.path` (default `content-model/`). Absent
|
|
155
|
+
* (null) in repos without a content model — the ContentModelProvider is then a
|
|
156
|
+
* safe no-op.
|
|
157
|
+
*/
|
|
158
|
+
contentModel?: ContentModelSource | null;
|
|
159
|
+
/**
|
|
160
|
+
* Optional raw contents of the dedicated theme file referenced by
|
|
161
|
+
* `config.theme.themesFile` (F5/T5.1). Read at manifest-generation time from
|
|
162
|
+
* the host repo and merged into the theme block in local mode the same way
|
|
163
|
+
* the remote loader fetches it at runtime. Null/absent when no themesFile is
|
|
164
|
+
* configured or the file is missing — a safe no-op.
|
|
165
|
+
*/
|
|
166
|
+
themeFileRaw?: string | null;
|
|
167
|
+
generatedAt: string;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* buildManifest() — the manifest PRODUCER (anokye-labs/kbexplorer-engine#17,
|
|
172
|
+
* the anchor child of the thin-CLI/fat-engine epic
|
|
173
|
+
* anokye-labs/kbexplorer-template#463).
|
|
174
|
+
*
|
|
175
|
+
* The engine already owned the manifest **consume** path — `ManifestSource`,
|
|
176
|
+
* the `RepoSource` acquisition adapters (`GitHubApiSource`, `FileSystemSource`),
|
|
177
|
+
* and the `RepoManifest` snapshot shape itself (`./repo-manifest`). What was
|
|
178
|
+
* missing was the **producer**: something that turns a `RepoSource` into a
|
|
179
|
+
* `RepoManifest`. That logic lived duplicated outside the engine, in
|
|
180
|
+
* kbexplorer-template's `scripts/generate-manifest.js` and the CLI's
|
|
181
|
+
* byte-parallel `src/lib/repo-manifest.ts` fork (flagged by cli#232).
|
|
182
|
+
*
|
|
183
|
+
* `buildManifest` closes that loop *without* re-implementing any fetching:
|
|
184
|
+
* acquisition (GitHub REST calls, local filesystem walks) already lives on the
|
|
185
|
+
* injected `RepoSource`. This function drives it through the existing
|
|
186
|
+
* `getRepoData()` seam exactly once and re-shapes the normalized `RepoData`
|
|
187
|
+
* bundle into the serializable `RepoManifest` snapshot — the mechanical,
|
|
188
|
+
* source-agnostic half of what the template script does before it writes
|
|
189
|
+
* `repo-manifest.json` to disk. It is deliberately the mirror image of
|
|
190
|
+
* `ManifestSource.getRepoData()` (which maps `RepoManifest` → `RepoData`);
|
|
191
|
+
* this maps `RepoData` → `RepoManifest`.
|
|
192
|
+
*
|
|
193
|
+
* Signature note (`buildManifest(source, options)`, not `(source, config)`):
|
|
194
|
+
* every `RepoManifest` field this function assembles comes straight off
|
|
195
|
+
* `RepoData` — none of them need a resolved `KBConfig`. The two fields a
|
|
196
|
+
* `RepoSource` cannot uniformly supply are threaded in via `options` instead:
|
|
197
|
+
*
|
|
198
|
+
* - `configRaw` — the raw (pre-YAML-parse) `config.yaml` text. `KBConfig`
|
|
199
|
+
* (what `GitHubApiSource.resolveConfig()` / a local `loadConfig()` caller
|
|
200
|
+
* would have) is already-parsed, so it can't round-trip back to source
|
|
201
|
+
* text. Callers already hold (or can cheaply obtain) the raw text — a local
|
|
202
|
+
* caller reads the same file `FileSystemSource` walks, and a remote caller
|
|
203
|
+
* already has the engine's exported `fetchFile` to hand — so this function
|
|
204
|
+
* accepts it rather than duplicating a second raw-fetch code path.
|
|
205
|
+
* - `generatedAt` — the snapshot timestamp. Defaults to
|
|
206
|
+
* `new Date().toISOString()` but is overridable so callers (and this
|
|
207
|
+
* package's own tests) can produce deterministic output for
|
|
208
|
+
* idempotency/`--check`-drift assertions, matching the issue's requirement
|
|
209
|
+
* that source-derived fields (everything *except* `generatedAt`/live
|
|
210
|
+
* GitHub state) be deterministic.
|
|
211
|
+
*
|
|
212
|
+
* `nodemapFiles`/`nodemapDirs` are passed through as-is from `RepoData` when a
|
|
213
|
+
* source populates them (neither shipped `RepoSource` does yet — both leave
|
|
214
|
+
* `nodemapRaw: null`, matching their existing, unchanged behavior — so
|
|
215
|
+
* `buildManifest` carries whatever future sources supply without
|
|
216
|
+
* re-implementing the template script's nodemap-collection logic itself,
|
|
217
|
+
* which is out of this issue's scope: see the issue's "non-goals").
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
/** Options for fields `RepoData` cannot uniformly supply (see module docs). */
|
|
221
|
+
interface BuildManifestOptions {
|
|
222
|
+
/** Raw (pre-parse) `config.yaml` text to embed, or `null`/omitted when there is none. */
|
|
223
|
+
configRaw?: string | null;
|
|
224
|
+
/**
|
|
225
|
+
* Overrides the snapshot timestamp. Defaults to `new Date().toISOString()`.
|
|
226
|
+
* Pass a fixed value for deterministic/idempotency tests and `--check` drift
|
|
227
|
+
* comparisons, which must ignore this volatile field regardless.
|
|
228
|
+
*/
|
|
229
|
+
generatedAt?: string;
|
|
230
|
+
/** When set, overlay the live-GitHub fields from this source onto the primary-source manifest (hybrid: local content + live augmentation). */
|
|
231
|
+
augmentFrom?: RepoSource;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Drive `source.getRepoData()` once and assemble a `RepoManifest` snapshot
|
|
235
|
+
* from the result — the producer half of the `ManifestSource` contract.
|
|
236
|
+
*/
|
|
237
|
+
declare function buildManifest(source: RepoSource, options?: BuildManifestOptions): Promise<RepoManifest>;
|
|
238
|
+
|
|
239
|
+
export { type BuildManifestOptions as B, type CacheStore as C, type GHFileContent as G, NotModifiedError as N, type RepoManifest as R, fetchFile as a, buildManifest as b, fetchFiles as c, fetchIssues as d, fetchPullRequests as e, fetchCommits as f, fetchReleases as g, fetchTree as h, GITHUB_ENDPOINT_PATTERNS as i, GitHubApiError as j, RateLimitError as k };
|