dsh-featherhunter-plugin-base 0.0.0-stage → 0.0.2-drill
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 +46 -2
- package/data/img/card-feishu.png +0 -0
- package/data/img/card-qq.png +0 -0
- package/data/img/card-wechat-2026-10-23.png +0 -0
- package/data/img/card-wechat-2026-11-06.png +0 -0
- package/data/img/qr-feishu.png +0 -0
- package/data/img/qr-qq.png +0 -0
- package/data/img/qr-wechat-2026-10-23.png +0 -0
- package/data/img/qr-wechat-2026-11-06.png +0 -0
- package/data/manifest.json +158 -0
- package/dist/client/content.d.ts +116 -0
- package/dist/client/index.d.ts +15 -0
- package/dist/client/index.js +1317 -0
- package/dist/client/modal.d.ts +42 -0
- package/dist/client/plugins.d.ts +44 -0
- package/dist/client/styles.d.ts +13 -0
- package/dist/client/texts.d.ts +11 -0
- package/dist/client/types.d.ts +90 -0
- package/dist/core/baseline.d.ts +2 -0
- package/dist/core/index.d.ts +40 -0
- package/dist/core/index.js +358 -0
- package/dist/core/types.d.ts +84 -0
- package/dist/node/cli.d.ts +2 -0
- package/dist/node/cli.js +797 -0
- package/dist/node/index.d.ts +17 -0
- package/dist/node/index.js +1220 -0
- package/dist/node/readme-compare.d.ts +199 -0
- package/dist/node/readme-sync.d.ts +143 -0
- package/dist/node/renderers.d.ts +48 -0
- package/dist/node/types.d.ts +56 -0
- package/package.json +75 -4
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* README block extraction and normalized comparison.
|
|
3
|
+
*
|
|
4
|
+
* The five plugin repositories hand-write four README blocks (contact, star
|
|
5
|
+
* history, thanks, author plugins) in slightly different shapes. This module
|
|
6
|
+
* (a) locates those blocks in a README text, (b) parses each shape leniently
|
|
7
|
+
* into a comparable structure, and (c) compares two blocks by *content*
|
|
8
|
+
* (members / links / texts / widths / thanks table data), reporting pure
|
|
9
|
+
* formatting differences separately.
|
|
10
|
+
*
|
|
11
|
+
* It is deliberately dependency-free (no node: imports) so both the ./node
|
|
12
|
+
* entry and the fixture extraction script can use it.
|
|
13
|
+
*/
|
|
14
|
+
/** Kind of a managed README block. */
|
|
15
|
+
export type ReadmeBlockKind = "contact" | "star-history" | "thanks" | "more";
|
|
16
|
+
/** A line that looks like merge-conflict residue; writers must stop. */
|
|
17
|
+
export interface SuspiciousLine {
|
|
18
|
+
/** 1-based line number. */
|
|
19
|
+
line: number;
|
|
20
|
+
/** The offending line, verbatim. */
|
|
21
|
+
text: string;
|
|
22
|
+
reason: "conflict-start" | "conflict-separator" | "conflict-end" | "merge-conflict-residue";
|
|
23
|
+
}
|
|
24
|
+
/** Raw block slices located in a README text. */
|
|
25
|
+
export interface LocatedBlocks {
|
|
26
|
+
/** Raw block text per kind; null when the block is absent. */
|
|
27
|
+
blocks: Record<ReadmeBlockKind, string | null>;
|
|
28
|
+
/** Block kinds in the order they appear in the file. */
|
|
29
|
+
order: ReadmeBlockKind[];
|
|
30
|
+
/** Merge-conflict residue / suspicious lines found anywhere in the file. */
|
|
31
|
+
suspicious: SuspiciousLine[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Locate the four managed blocks in a README text.
|
|
35
|
+
*
|
|
36
|
+
* Boundaries are the next managed-block start (or EOF), trimmed of blank lines
|
|
37
|
+
* and trailing `---` rules. Block order is recorded but never assumed: the
|
|
38
|
+
* five repos currently order the blocks differently.
|
|
39
|
+
*/
|
|
40
|
+
export declare function locateReadmeBlocks(markdown: string): LocatedBlocks;
|
|
41
|
+
/** Find merge-conflict residue. Writers must stop and hand the file to a human. */
|
|
42
|
+
export declare function findSuspiciousLines(markdown: string): SuspiciousLine[];
|
|
43
|
+
/** Parse an HTML attribute string into a lower-cased attribute map. */
|
|
44
|
+
export declare function parseAttributes(attributeText: string): Record<string, string>;
|
|
45
|
+
/** File name of an image src, dropping directory prefix, query and hash. */
|
|
46
|
+
export declare function assetName(src: string): string;
|
|
47
|
+
/** Parsed block heading; `sub` is the small label (CONNECT / THANKS / MORE). */
|
|
48
|
+
export interface ParsedHeading {
|
|
49
|
+
sub: string | null;
|
|
50
|
+
text: string;
|
|
51
|
+
}
|
|
52
|
+
/** Parse the heading of a managed block in either HTML or Markdown form. */
|
|
53
|
+
export declare function parseHeading(raw: string): ParsedHeading | null;
|
|
54
|
+
/** One group (chat group QR code) inside the contact block. */
|
|
55
|
+
export interface ContactGroup {
|
|
56
|
+
/** Table header text, e.g. 飞书群(主). */
|
|
57
|
+
title: string;
|
|
58
|
+
/** Image src exactly as written (relative or absolute). */
|
|
59
|
+
src: string;
|
|
60
|
+
/** Image file name; stable across relative prefixes and CDN URLs. */
|
|
61
|
+
asset: string;
|
|
62
|
+
/** Rendered image width, if any. */
|
|
63
|
+
width: number | null;
|
|
64
|
+
alt: string;
|
|
65
|
+
}
|
|
66
|
+
/** Parsed contact block. */
|
|
67
|
+
export interface ParsedContactBlock {
|
|
68
|
+
variant: "html-heading" | "markdown-heading";
|
|
69
|
+
heading: ParsedHeading | null;
|
|
70
|
+
welcome: string | null;
|
|
71
|
+
groups: ContactGroup[];
|
|
72
|
+
}
|
|
73
|
+
/** Parse the contact block in either heading variant. */
|
|
74
|
+
export declare function parseContactBlock(raw: string): ParsedContactBlock;
|
|
75
|
+
/** A dark/light `<source>` inside a `<picture>`. */
|
|
76
|
+
export interface StarSource {
|
|
77
|
+
/** "dark" / "light" when declared, otherwise null. */
|
|
78
|
+
media: string | null;
|
|
79
|
+
src: string;
|
|
80
|
+
/** Image file name, stable across CDN URLs and cache busters. */
|
|
81
|
+
asset: string;
|
|
82
|
+
}
|
|
83
|
+
/** The visible star history image. */
|
|
84
|
+
export interface StarImage {
|
|
85
|
+
alt: string;
|
|
86
|
+
src: string;
|
|
87
|
+
asset: string;
|
|
88
|
+
width: number | null;
|
|
89
|
+
}
|
|
90
|
+
/** Parsed star history block. */
|
|
91
|
+
export interface ParsedStarHistoryBlock {
|
|
92
|
+
variant: "picture-link" | "picture" | "markdown-image" | "unknown";
|
|
93
|
+
/** Click-through link around the picture, e.g. deck's star-history.html. */
|
|
94
|
+
link: string | null;
|
|
95
|
+
sources: StarSource[];
|
|
96
|
+
image: StarImage | null;
|
|
97
|
+
/** Caption below the image (im keeps a two-axis explanation). */
|
|
98
|
+
caption: string | null;
|
|
99
|
+
}
|
|
100
|
+
/** Parse the star history block in any of its four current shapes. */
|
|
101
|
+
export declare function parseStarHistoryBlock(raw: string): ParsedStarHistoryBlock;
|
|
102
|
+
/** One table cell of the thanks table. */
|
|
103
|
+
export interface ThanksCell {
|
|
104
|
+
/** Visible text (links reduced to their label). */
|
|
105
|
+
text: string;
|
|
106
|
+
/** Link targets found in the cell, in order. */
|
|
107
|
+
links: string[];
|
|
108
|
+
}
|
|
109
|
+
/** One row of the thanks table. */
|
|
110
|
+
export interface ThanksRow {
|
|
111
|
+
cells: ThanksCell[];
|
|
112
|
+
}
|
|
113
|
+
/** Parsed thanks block. */
|
|
114
|
+
export interface ParsedThanksBlock {
|
|
115
|
+
variant: "html-heading" | "markdown-heading";
|
|
116
|
+
heading: ParsedHeading | null;
|
|
117
|
+
/** Language of the table markers (THANKS-TABLE-ZH / -EN). */
|
|
118
|
+
lang: "zh" | "en" | null;
|
|
119
|
+
headers: string[];
|
|
120
|
+
rows: ThanksRow[];
|
|
121
|
+
/** Non-table prose around the table, kept for fixtures but not compared. */
|
|
122
|
+
prose: string | null;
|
|
123
|
+
}
|
|
124
|
+
/** Parse the thanks block, including the empty-table case used by ilife. */
|
|
125
|
+
export declare function parseThanksBlock(raw: string): ParsedThanksBlock;
|
|
126
|
+
/** One plugin row in the "more from the author" block. */
|
|
127
|
+
export interface MoreItem {
|
|
128
|
+
name: string;
|
|
129
|
+
url: string;
|
|
130
|
+
description: string;
|
|
131
|
+
}
|
|
132
|
+
/** Parsed "more from the author" block. */
|
|
133
|
+
export interface ParsedMoreBlock {
|
|
134
|
+
variant: "html-heading" | "markdown-heading";
|
|
135
|
+
heading: ParsedHeading | null;
|
|
136
|
+
intro: string | null;
|
|
137
|
+
items: MoreItem[];
|
|
138
|
+
footer: string | null;
|
|
139
|
+
}
|
|
140
|
+
/** Parse the "more from the author" block. Rows are returned in file order. */
|
|
141
|
+
export declare function parseMoreBlock(raw: string): ParsedMoreBlock;
|
|
142
|
+
/** Formatting-only differences; they never make a block content-different. */
|
|
143
|
+
export type FormatStage = "line-endings" | "whitespace" | "quotes" | "attribute-order" | "self-closing";
|
|
144
|
+
/** A formatting difference kind (`markup` = structural markup beyond the stages). */
|
|
145
|
+
export type FormatDifferenceKind = FormatStage | "heading-variant" | "markup";
|
|
146
|
+
/** One detected pure-format difference. */
|
|
147
|
+
export interface FormatDifference {
|
|
148
|
+
kind: FormatDifferenceKind;
|
|
149
|
+
/** Set when the difference was found at README level. */
|
|
150
|
+
block?: ReadmeBlockKind;
|
|
151
|
+
}
|
|
152
|
+
/** Content difference dimensions required by the equivalence criterion. */
|
|
153
|
+
export type DifferenceDimension = "presence" | "members" | "links" | "text" | "width" | "table" | "image";
|
|
154
|
+
/** One content difference, addressed by a structural path. */
|
|
155
|
+
export interface Difference {
|
|
156
|
+
dimension: DifferenceDimension;
|
|
157
|
+
path: string;
|
|
158
|
+
expected: unknown;
|
|
159
|
+
actual: unknown;
|
|
160
|
+
}
|
|
161
|
+
/** Comparison options. */
|
|
162
|
+
export interface CompareOptions {
|
|
163
|
+
/**
|
|
164
|
+
* How image sources are compared. `basename` (default) ignores relative/CDN
|
|
165
|
+
* prefixes and `?v=` cache busters; `exact` compares the raw src.
|
|
166
|
+
*/
|
|
167
|
+
imageSrc?: "basename" | "exact";
|
|
168
|
+
}
|
|
169
|
+
/** Result of comparing one managed block. */
|
|
170
|
+
export interface BlockComparison {
|
|
171
|
+
kind: ReadmeBlockKind;
|
|
172
|
+
/** Raw text is byte-identical. */
|
|
173
|
+
identical: boolean;
|
|
174
|
+
/** Content is equivalent under the F18 criterion. */
|
|
175
|
+
equivalent: boolean;
|
|
176
|
+
contentDifferences: Difference[];
|
|
177
|
+
/** Only populated when content is equivalent but raw text differs. */
|
|
178
|
+
formatDifferences: FormatDifference[];
|
|
179
|
+
}
|
|
180
|
+
/** Result of comparing two READMEs. */
|
|
181
|
+
export interface ReadmeComparison {
|
|
182
|
+
equivalent: boolean;
|
|
183
|
+
blocks: Record<ReadmeBlockKind, BlockComparison>;
|
|
184
|
+
contentDifferences: Difference[];
|
|
185
|
+
formatDifferences: FormatDifference[];
|
|
186
|
+
order: {
|
|
187
|
+
expected: ReadmeBlockKind[];
|
|
188
|
+
actual: ReadmeBlockKind[];
|
|
189
|
+
same: boolean;
|
|
190
|
+
};
|
|
191
|
+
/** Merge-conflict residue from the actual README: callers must stop. */
|
|
192
|
+
suspicious: SuspiciousLine[];
|
|
193
|
+
}
|
|
194
|
+
/** Canonical form that erases whitespace, quote, attribute-order and self-close style. */
|
|
195
|
+
export declare function canonicalizeMarkup(markup: string): string;
|
|
196
|
+
/** Compare one managed block; null means the block is absent. */
|
|
197
|
+
export declare function compareBlocks(kind: ReadmeBlockKind, expectedRaw: string | null, actualRaw: string | null, options?: CompareOptions): BlockComparison;
|
|
198
|
+
/** Compare two READMEs (raw text or already-located blocks) block by block. */
|
|
199
|
+
export declare function compareReadmeBlocks(expected: string | LocatedBlocks, actual: string | LocatedBlocks, options?: CompareOptions): ReadmeComparison;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { Lang } from "../core/types";
|
|
2
|
+
import type { BlockName, BlockRenderOptions, HeadingVariant, ReadmeSyncOptions } from "./types";
|
|
3
|
+
/** One marker pair managed inside a README. */
|
|
4
|
+
export interface BlockMarkerSet {
|
|
5
|
+
/** Language of the segment; unset = the block's default language. */
|
|
6
|
+
lang?: Lang;
|
|
7
|
+
start: string;
|
|
8
|
+
end: string;
|
|
9
|
+
}
|
|
10
|
+
/** Options handed to a block renderer; `lang` is always resolved. */
|
|
11
|
+
export interface BlockRenderContext extends BlockRenderOptions {
|
|
12
|
+
lang: Lang;
|
|
13
|
+
}
|
|
14
|
+
/** Half-open character range of a legacy (pre-marker) block in a README. */
|
|
15
|
+
export interface LegacyBlockSpan {
|
|
16
|
+
start: number;
|
|
17
|
+
end: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* One managed README block.
|
|
21
|
+
*
|
|
22
|
+
* Ticket 09/10 only need to fill in `render` (and, when the block is new like
|
|
23
|
+
* the MORE block on ilife, `locateLegacy`) for the reserved entries.
|
|
24
|
+
*/
|
|
25
|
+
export interface BlockDefinition {
|
|
26
|
+
name: BlockName;
|
|
27
|
+
/** Human-readable name used in CLI diagnostics. */
|
|
28
|
+
label: string;
|
|
29
|
+
/** Marker pairs the engine owns. `thanks` has one pair per language. */
|
|
30
|
+
markers: readonly BlockMarkerSet[];
|
|
31
|
+
/**
|
|
32
|
+
* Render the content between the marker pair. A definition without a
|
|
33
|
+
* renderer is reserved: its markers are validated when present but never
|
|
34
|
+
* rewritten.
|
|
35
|
+
*/
|
|
36
|
+
render?: (context: BlockRenderContext) => string;
|
|
37
|
+
/** Locate the pre-marker block extent in a README (used by `readme:init`). */
|
|
38
|
+
locateLegacy?: (markdown: string) => LegacyBlockSpan | null;
|
|
39
|
+
/** Recover the language and heading variant from a current block text. */
|
|
40
|
+
detect?: (raw: string) => {
|
|
41
|
+
lang?: Lang;
|
|
42
|
+
variant?: HeadingVariant;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export declare const BLOCK_MARKERS: {
|
|
46
|
+
readonly contact: {
|
|
47
|
+
readonly start: "<!-- CONTACT-START -->";
|
|
48
|
+
readonly end: "<!-- CONTACT-END -->";
|
|
49
|
+
};
|
|
50
|
+
readonly more: {
|
|
51
|
+
readonly start: "<!-- MORE-START -->";
|
|
52
|
+
readonly end: "<!-- MORE-END -->";
|
|
53
|
+
};
|
|
54
|
+
readonly starHistory: {
|
|
55
|
+
readonly start: "<!-- STAR-HISTORY-START -->";
|
|
56
|
+
readonly end: "<!-- STAR-HISTORY-END -->";
|
|
57
|
+
};
|
|
58
|
+
readonly thanks: {
|
|
59
|
+
readonly start: "<!-- THANKS-TABLE-ZH-START -->";
|
|
60
|
+
readonly end: "<!-- THANKS-TABLE-ZH-END -->";
|
|
61
|
+
readonly zh: {
|
|
62
|
+
readonly start: "<!-- THANKS-TABLE-ZH-START -->";
|
|
63
|
+
readonly end: "<!-- THANKS-TABLE-ZH-END -->";
|
|
64
|
+
};
|
|
65
|
+
readonly en: {
|
|
66
|
+
readonly start: "<!-- THANKS-TABLE-EN-START -->";
|
|
67
|
+
readonly end: "<!-- THANKS-TABLE-EN-END -->";
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Registry of the managed README blocks.
|
|
73
|
+
*
|
|
74
|
+
* Order is documentation order, not README order: the engine locates every
|
|
75
|
+
* block by its markers, so block order inside a README never matters.
|
|
76
|
+
*/
|
|
77
|
+
export declare const BLOCK_DEFINITIONS: readonly BlockDefinition[];
|
|
78
|
+
/** Look up one block definition by name. */
|
|
79
|
+
export declare function blockDefinition(name: BlockName): BlockDefinition | undefined;
|
|
80
|
+
/** Definitions that can be rendered today (reserved blocks are skipped). */
|
|
81
|
+
export declare function implementedBlockDefinitions(): BlockDefinition[];
|
|
82
|
+
/** Result of updating one README file. */
|
|
83
|
+
export interface ReadmeFileResult {
|
|
84
|
+
/** Absolute path of the README. */
|
|
85
|
+
readmePath: string;
|
|
86
|
+
ok: boolean;
|
|
87
|
+
/** The README content must change (or would change when `check`). */
|
|
88
|
+
changed: boolean;
|
|
89
|
+
/** The engine actually wrote the file. */
|
|
90
|
+
written: boolean;
|
|
91
|
+
blocks: Array<{
|
|
92
|
+
name: BlockName;
|
|
93
|
+
status: "inserted" | "updated" | "unchanged" | "skipped";
|
|
94
|
+
}>;
|
|
95
|
+
errors: string[];
|
|
96
|
+
}
|
|
97
|
+
/** Options for one README target. */
|
|
98
|
+
export interface ReadmeTargetOptions extends BlockRenderOptions {
|
|
99
|
+
/** Directory that contains the README (`--root`). */
|
|
100
|
+
root: string;
|
|
101
|
+
/** README path relative to `root`; default `README.md`. */
|
|
102
|
+
readme?: string;
|
|
103
|
+
}
|
|
104
|
+
/** Options for one `readme:init` / `readme:sync` file update. */
|
|
105
|
+
export interface ReadmeUpdateOptions extends ReadmeTargetOptions {
|
|
106
|
+
/** `init` may insert missing markers; `sync` only rewrites marked text. */
|
|
107
|
+
command?: "init" | "sync";
|
|
108
|
+
/** Report drift without writing. */
|
|
109
|
+
check?: boolean;
|
|
110
|
+
}
|
|
111
|
+
/** Result of updating several README files. */
|
|
112
|
+
export interface ReadmeBatchResult {
|
|
113
|
+
ok: boolean;
|
|
114
|
+
files: ReadmeFileResult[];
|
|
115
|
+
}
|
|
116
|
+
/** Hooks used by tests to observe the atomic-write window. */
|
|
117
|
+
export interface AtomicWriteHooks {
|
|
118
|
+
/** Called after the temp file is complete but before the rename. */
|
|
119
|
+
beforeRename?: (tempPath: string) => void;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Write a file atomically: full content goes to a sibling temp file first, the
|
|
123
|
+
* original permissions are copied, and only then the temp file is renamed over
|
|
124
|
+
* the destination. A failure before the rename leaves the original untouched
|
|
125
|
+
* and removes the temp file.
|
|
126
|
+
*/
|
|
127
|
+
export declare function writeFileAtomic(filePath: string, content: string, hooks?: AtomicWriteHooks): void;
|
|
128
|
+
/**
|
|
129
|
+
* Update one README.
|
|
130
|
+
*
|
|
131
|
+
* `init` seeds missing marker pairs at the legacy block position; `sync`
|
|
132
|
+
* only rewrites already-marked text. In both modes a README with suspicious
|
|
133
|
+
* merge-conflict residue or broken markers is left byte-identical.
|
|
134
|
+
*/
|
|
135
|
+
export declare function updateReadme(options: ReadmeUpdateOptions): ReadmeFileResult;
|
|
136
|
+
/**
|
|
137
|
+
* Update every target root (CLI `--root`, repeatable).
|
|
138
|
+
*
|
|
139
|
+
* All targets are processed even when one fails, so a maintainer sees every
|
|
140
|
+
* problem in one run; `--all` (the CLI flag) merely mirrors that policy for
|
|
141
|
+
* the multi-repository workflow.
|
|
142
|
+
*/
|
|
143
|
+
export declare function updateReadmes(options: ReadmeSyncOptions): ReadmeBatchResult;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Group, Manifest } from "../core/types";
|
|
2
|
+
import type { BlockRenderOptions } from "./types";
|
|
3
|
+
/** jsDelivr base of the published data package (exact-version images). */
|
|
4
|
+
export declare const DEFAULT_CDN_BASE = "https://cdn.jsdelivr.net/npm/dsh-featherhunter-plugin-base";
|
|
5
|
+
/** Whole-card image width used by the README contact table (spec rule 9). */
|
|
6
|
+
export declare const CONTACT_CARD_WIDTH = 260;
|
|
7
|
+
/** Fixed wording of the contact block, independent of the group data. */
|
|
8
|
+
export declare const CONTACT_TEXT: {
|
|
9
|
+
readonly sub: "CONNECT";
|
|
10
|
+
readonly heading: {
|
|
11
|
+
readonly zh: "反馈与联系";
|
|
12
|
+
readonly en: "Feedback and Contact";
|
|
13
|
+
};
|
|
14
|
+
readonly welcome: {
|
|
15
|
+
readonly zh: "👏欢迎提交PR、ISSUE。星星之火可以燎原🔥有任何建议、意见、想法、吐槽、idea都可以进群交流。";
|
|
16
|
+
readonly en: "👏Welcome PRs and issues. A single spark can start a prairie fire 🔥. Any suggestion, opinion, idea or rant — come join the group and talk.";
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Cache-busting date for a limited-time card (`YYYYMMDD`), or null for a
|
|
21
|
+
* permanent card.
|
|
22
|
+
*
|
|
23
|
+
* A card counts as limited-time when the manifest marks it with `validUntil`
|
|
24
|
+
* or the published file name carries a `YYYY-MM-DD` date. The date in the
|
|
25
|
+
* file name wins; otherwise the manifest `updatedAt` date is used, because
|
|
26
|
+
* GitHub's camo proxy caches images by URL and a new QR code must get a new
|
|
27
|
+
* URL.
|
|
28
|
+
*/
|
|
29
|
+
export declare function contactCardCacheDate(group: Group, manifest: Manifest): string | null;
|
|
30
|
+
/**
|
|
31
|
+
* Absolute pristine CDN URL of one published card image.
|
|
32
|
+
*
|
|
33
|
+
* Shape: `<cdnBase>@<manifest.version>/data/img/<card>[?v=YYYYMMDD]`. The
|
|
34
|
+
* base must be an absolute https URL — README images are never relative or
|
|
35
|
+
* copied into the plugin repositories.
|
|
36
|
+
*/
|
|
37
|
+
export declare function contactCardUrl(group: Group, manifest: Manifest, options?: BlockRenderOptions): string;
|
|
38
|
+
/**
|
|
39
|
+
* Render the README contact block content (no markers).
|
|
40
|
+
*
|
|
41
|
+
* Keeps the pre-migration structure: heading, centered wrapper, welcome line,
|
|
42
|
+
* one header row and one row of {@link CONTACT_CARD_WIDTH}-wide whole cards.
|
|
43
|
+
* Modal-only fields (`title`, `subtitle`, `note`, `qr`) never appear here.
|
|
44
|
+
*
|
|
45
|
+
* @param options data + language + image base; `variant` selects the standard
|
|
46
|
+
* CONNECT h2 (default) or ilife's Markdown `##` heading.
|
|
47
|
+
*/
|
|
48
|
+
export declare function renderContactBlock(options?: BlockRenderOptions): string;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Lang, Manifest } from "../core/types";
|
|
2
|
+
/**
|
|
3
|
+
* Heading form of a rendered block. Most repositories use an HTML <h2>
|
|
4
|
+
* heading; ilife (and the prompt MORE block) use a Markdown `##` heading.
|
|
5
|
+
*/
|
|
6
|
+
export type HeadingVariant = "html-heading" | "markdown-heading";
|
|
7
|
+
/** Shared options for the README block renderers. */
|
|
8
|
+
export interface BlockRenderOptions {
|
|
9
|
+
/** Manifest to render from; defaults to the packaged baseline manifest. */
|
|
10
|
+
manifest?: Manifest;
|
|
11
|
+
/** Published package version, used for precise CDN URLs. */
|
|
12
|
+
version?: string;
|
|
13
|
+
/** Target language of the generated block. */
|
|
14
|
+
lang?: Lang;
|
|
15
|
+
/** CDN base URL override (tests / mirrors). */
|
|
16
|
+
cdnBase?: string;
|
|
17
|
+
/** Heading form override; renderers default to their repository convention. */
|
|
18
|
+
variant?: HeadingVariant;
|
|
19
|
+
}
|
|
20
|
+
/** Marker names of the four managed README blocks. */
|
|
21
|
+
export type BlockName = "contact" | "star-history" | "thanks" | "more";
|
|
22
|
+
export interface VerifyResult {
|
|
23
|
+
ok: boolean;
|
|
24
|
+
errors: string[];
|
|
25
|
+
}
|
|
26
|
+
/** Options for the README writer (ticket 08). */
|
|
27
|
+
export interface ReadmeSyncOptions {
|
|
28
|
+
/** Repository roots to update. */
|
|
29
|
+
roots: string[];
|
|
30
|
+
/** `init` may insert missing marker pairs; `sync` only rewrites marked text. */
|
|
31
|
+
command?: "init" | "sync";
|
|
32
|
+
/** Report drift without writing. */
|
|
33
|
+
check?: boolean;
|
|
34
|
+
/** Process every target root and keep going after per-file errors. */
|
|
35
|
+
all?: boolean;
|
|
36
|
+
dryRun?: boolean;
|
|
37
|
+
/** Manifest to render from; defaults to the packaged baseline manifest. */
|
|
38
|
+
manifest?: Manifest;
|
|
39
|
+
/** Published package version, used for precise CDN URLs. */
|
|
40
|
+
version?: string;
|
|
41
|
+
/** CDN base URL override (tests / mirrors). */
|
|
42
|
+
cdnBase?: string;
|
|
43
|
+
/** Target language override; detected from the README when omitted. */
|
|
44
|
+
lang?: Lang;
|
|
45
|
+
/** Heading form override; detected from the README when omitted. */
|
|
46
|
+
variant?: HeadingVariant;
|
|
47
|
+
}
|
|
48
|
+
export interface DoctorCheck {
|
|
49
|
+
name: string;
|
|
50
|
+
ok: boolean;
|
|
51
|
+
detail: string;
|
|
52
|
+
}
|
|
53
|
+
export interface DoctorResult {
|
|
54
|
+
ok: boolean;
|
|
55
|
+
checks: DoctorCheck[];
|
|
56
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,77 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-featherhunter-plugin-base",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.0.2-drill",
|
|
4
|
+
"description": "FeatherHunter DSH plugin shared base: settings-panel contact modal / author plugins card widgets and README block renderers",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"dsh",
|
|
7
|
+
"deepseek-harness",
|
|
8
|
+
"plugin",
|
|
9
|
+
"readme",
|
|
10
|
+
"contact"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"data",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"exports": {
|
|
25
|
+
"./core": {
|
|
26
|
+
"types": "./dist/core/index.d.ts",
|
|
27
|
+
"import": "./dist/core/index.js",
|
|
28
|
+
"default": "./dist/core/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./client": {
|
|
31
|
+
"types": "./dist/client/index.d.ts",
|
|
32
|
+
"import": "./dist/client/index.js",
|
|
33
|
+
"default": "./dist/client/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./node": {
|
|
36
|
+
"types": "./dist/node/index.d.ts",
|
|
37
|
+
"import": "./dist/node/index.js",
|
|
38
|
+
"default": "./dist/node/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./package.json": "./package.json"
|
|
41
|
+
},
|
|
42
|
+
"bin": {
|
|
43
|
+
"dsh-base": "dist/node/cli.js"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"react": "^18 || ^19"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "node build.mjs",
|
|
50
|
+
"build:demo": "node build.mjs --demo",
|
|
51
|
+
"build:data": "node scripts/run-ts.mjs build-data",
|
|
52
|
+
"data:check": "node scripts/run-ts.mjs build-data --check",
|
|
53
|
+
"contact:update": "node scripts/run-ts.mjs contact-update",
|
|
54
|
+
"readme:init": "node dist/node/cli.js readme:init",
|
|
55
|
+
"readme:sync": "node dist/node/cli.js readme:sync",
|
|
56
|
+
"readme:check": "node dist/node/cli.js readme:sync --check",
|
|
57
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"test:watch": "vitest",
|
|
60
|
+
"test:network": "node scripts/cdn-smoke.mjs",
|
|
61
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@types/node": "^26.6.6",
|
|
65
|
+
"@types/pngjs": "^6.0.5",
|
|
66
|
+
"@types/react": "^19.3.0",
|
|
67
|
+
"@types/react-dom": "^19.3.0",
|
|
68
|
+
"esbuild": "^0.28.2",
|
|
69
|
+
"jpeg-js": "^0.4.4",
|
|
70
|
+
"jsdom": "^30.1.3",
|
|
71
|
+
"pngjs": "^7.0.0",
|
|
72
|
+
"react": "^19.3.1",
|
|
73
|
+
"react-dom": "^19.3.1",
|
|
74
|
+
"typescript": "^7.0.2",
|
|
75
|
+
"vitest": "^5.0.5"
|
|
76
|
+
}
|
|
77
|
+
}
|