base-link-core 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.
@@ -0,0 +1,43 @@
1
+ export declare const ENVELOPE_VERSION: "0.1.0";
2
+ export declare const ENVELOPE_SHAPES: readonly ["list", "detail", "stat", "receipt", "analysis", "fallback"];
3
+ export type EnvelopeShape = (typeof ENVELOPE_SHAPES)[number];
4
+ export interface EnvelopeDataByShape {
5
+ list: {
6
+ items: unknown[];
7
+ total?: number;
8
+ };
9
+ detail: {
10
+ item: Record<string, unknown>;
11
+ };
12
+ stat: {
13
+ metrics: Record<string, number>;
14
+ };
15
+ receipt: {
16
+ ok: boolean;
17
+ message: string;
18
+ };
19
+ analysis: {
20
+ summary: string;
21
+ };
22
+ fallback: {
23
+ reason: string;
24
+ degraded: true;
25
+ };
26
+ }
27
+ export interface Envelope<S extends EnvelopeShape = EnvelopeShape> {
28
+ version: typeof ENVELOPE_VERSION;
29
+ skill: string;
30
+ shape: S;
31
+ key: string;
32
+ data: EnvelopeDataByShape[S];
33
+ }
34
+ export declare function assertShapeData(shape: EnvelopeShape, data: Record<string, unknown>): void;
35
+ export declare function createEnvelope<S extends EnvelopeShape>(input: {
36
+ skill: string;
37
+ shape: S;
38
+ key: string;
39
+ data: EnvelopeDataByShape[S];
40
+ }): Envelope<S>;
41
+ export declare function parseEnvelope(input: unknown): Envelope;
42
+ export declare function isEnvelope(input: unknown): input is Envelope;
43
+ //# sourceMappingURL=envelope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,gBAAgB,EAAG,OAAgB,CAAC;AAGjD,eAAO,MAAM,eAAe,wEAAyE,CAAC;AACtG,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE;QAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC;IAC1C,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IAC1C,OAAO,EAAE;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,QAAQ,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9B,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE,CAAC;CAC9C;AAED,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,aAAa,GAAG,aAAa;IAC/D,OAAO,EAAE,OAAO,gBAAgB,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC,CAAC;IAET,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC;CAC9B;AA0BD,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAmCzF;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,aAAa,EAAE,KAAK,EAAE;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC;CAC9B,GAAG,QAAQ,CAAC,CAAC,CAAC,CAQd;AAGD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAYtD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAO5D"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * envelope:唯一出口载荷。6 形状全字段,semver 版本化。
3
+ * 零依赖:手写守卫(不用 Zod),保 P4 零依赖冻结;Zod 可后加于 combos/skill 层。
4
+ */
5
+ import { EnvelopeError } from './errors.js';
6
+ export const ENVELOPE_VERSION = '0.1.0';
7
+ // 6 形状:list 今日列表 / detail 单条详情 / stat 聚合统计 / receipt 写入回执 / analysis 开放式分析(L6) / fallback 降级载荷
8
+ export const ENVELOPE_SHAPES = ['list', 'detail', 'stat', 'receipt', 'analysis', 'fallback'];
9
+ const SEMVER_RE = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:[-+].*)?$/;
10
+ function vstr(v) { return String(v); }
11
+ function assertSemverCompatible(version) {
12
+ if (typeof version !== 'string' || !SEMVER_RE.test(version)) {
13
+ throw new EnvelopeError('version 非法 semver:' + vstr(version));
14
+ }
15
+ const major = version.split('.')[0];
16
+ const want = ENVELOPE_VERSION.split('.')[0];
17
+ if (major !== want)
18
+ throw new EnvelopeError('version 主版本不兼容:' + version + '(当前 ' + ENVELOPE_VERSION + ')');
19
+ }
20
+ function assertNonEmptyString(value, field) {
21
+ if (typeof value !== 'string' || value.length === 0)
22
+ throw new EnvelopeError(field + ' 须为非空字符串');
23
+ }
24
+ function assertDataObject(data) {
25
+ if (typeof data !== 'object' || data === null || Array.isArray(data)) {
26
+ throw new EnvelopeError('data 须为对象(坏输入不返空数组)');
27
+ }
28
+ }
29
+ // 逐形状全字段校验:缺字段即 throw,不返空。
30
+ export function assertShapeData(shape, data) {
31
+ switch (shape) {
32
+ case 'list':
33
+ if (!Array.isArray(data.items))
34
+ throw new EnvelopeError('list 形缺 items 数组');
35
+ if (data.total !== undefined && typeof data.total !== 'number')
36
+ throw new EnvelopeError('list 形 total 须为 number');
37
+ break;
38
+ case 'detail':
39
+ if (typeof data.item !== 'object' || data.item === null || Array.isArray(data.item)) {
40
+ throw new EnvelopeError('detail 形缺 item 对象');
41
+ }
42
+ break;
43
+ case 'stat':
44
+ if (typeof data.metrics !== 'object' || data.metrics === null || Array.isArray(data.metrics)) {
45
+ throw new EnvelopeError('stat 形缺 metrics 对象');
46
+ }
47
+ for (const v of Object.values(data.metrics)) {
48
+ if (typeof v !== 'number')
49
+ throw new EnvelopeError('stat 形 metrics 值须全为 number');
50
+ }
51
+ break;
52
+ case 'receipt':
53
+ if (typeof data.ok !== 'boolean' || typeof data.message !== 'string') {
54
+ throw new EnvelopeError('receipt 形缺 ok/message 全字段');
55
+ }
56
+ break;
57
+ case 'analysis':
58
+ if (typeof data.summary !== 'string' || data.summary.length === 0) {
59
+ throw new EnvelopeError('analysis 形缺 summary 全字段');
60
+ }
61
+ break;
62
+ case 'fallback':
63
+ if (typeof data.reason !== 'string' || data.reason.length === 0 || data.degraded !== true) {
64
+ throw new EnvelopeError('fallback 形缺 reason/degraded:true 全字段(须显式标记降级)');
65
+ }
66
+ break;
67
+ }
68
+ }
69
+ export function createEnvelope(input) {
70
+ if (typeof input !== 'object' || input === null)
71
+ throw new EnvelopeError('入参须为对象');
72
+ assertNonEmptyString(input.skill, 'skill');
73
+ if (!ENVELOPE_SHAPES.includes(input.shape))
74
+ throw new EnvelopeError('未知 shape:' + vstr(input.shape));
75
+ assertNonEmptyString(input.key, 'key');
76
+ assertDataObject(input.data);
77
+ assertShapeData(input.shape, input.data);
78
+ return { version: ENVELOPE_VERSION, skill: input.skill, shape: input.shape, key: input.key, data: input.data };
79
+ }
80
+ // 校验未知输入(CLI/跨包边界):合法返回 envelope,非法 throw。
81
+ export function parseEnvelope(input) {
82
+ if (typeof input !== 'object' || input === null)
83
+ throw new EnvelopeError('envelope 须为对象');
84
+ const e = input;
85
+ assertSemverCompatible(e.version);
86
+ assertNonEmptyString(e.skill, 'skill');
87
+ if (typeof e.shape !== 'string' || !ENVELOPE_SHAPES.includes(e.shape)) {
88
+ throw new EnvelopeError('未知 shape:' + vstr(e.shape));
89
+ }
90
+ assertNonEmptyString(e.key, 'key');
91
+ assertDataObject(e.data);
92
+ assertShapeData(e.shape, e.data);
93
+ return { version: ENVELOPE_VERSION, skill: e.skill, shape: e.shape, key: e.key, data: e.data };
94
+ }
95
+ export function isEnvelope(input) {
96
+ try {
97
+ parseEnvelope(input);
98
+ return true;
99
+ }
100
+ catch {
101
+ return false;
102
+ }
103
+ }
104
+ //# sourceMappingURL=envelope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envelope.js","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /** link-core 错误基类:坏输入一律 throw,永不返空数组冒充正常。 */
2
+ export declare class LinkCoreError extends Error {
3
+ readonly code: string;
4
+ constructor(code: string, message: string, options?: {
5
+ cause?: unknown;
6
+ });
7
+ }
8
+ export declare class EnvelopeError extends LinkCoreError {
9
+ constructor(message: string, options?: {
10
+ cause?: unknown;
11
+ });
12
+ }
13
+ export declare class RegistryError extends LinkCoreError {
14
+ constructor(message: string, options?: {
15
+ cause?: unknown;
16
+ });
17
+ }
18
+ export declare class RunnerError extends LinkCoreError {
19
+ constructor(message: string, options?: {
20
+ cause?: unknown;
21
+ });
22
+ }
23
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBACV,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAKzE;AAED,qBAAa,aAAc,SAAQ,aAAa;gBAClC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED,qBAAa,aAAc,SAAQ,aAAa;gBAClC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED,qBAAa,WAAY,SAAQ,aAAa;gBAChC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D"}
package/dist/errors.js ADDED
@@ -0,0 +1,28 @@
1
+ /** link-core 错误基类:坏输入一律 throw,永不返空数组冒充正常。 */
2
+ export class LinkCoreError extends Error {
3
+ code;
4
+ constructor(code, message, options) {
5
+ super(message, options);
6
+ this.name = 'LinkCoreError';
7
+ this.code = code;
8
+ }
9
+ }
10
+ export class EnvelopeError extends LinkCoreError {
11
+ constructor(message, options) {
12
+ super('ENVELOPE_INVALID', message, options);
13
+ this.name = 'EnvelopeError';
14
+ }
15
+ }
16
+ export class RegistryError extends LinkCoreError {
17
+ constructor(message, options) {
18
+ super('REGISTRY_UNKNOWN_KEY', message, options);
19
+ this.name = 'RegistryError';
20
+ }
21
+ }
22
+ export class RunnerError extends LinkCoreError {
23
+ constructor(message, options) {
24
+ super('RUNNER_FAILED', message, options);
25
+ this.name = 'RunnerError';
26
+ }
27
+ }
28
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,IAAI,CAAS;IACtB,YAAY,IAAY,EAAE,OAAe,EAAE,OAA6B;QACtE,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,aAAa;IAC9C,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,kBAAkB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,aAAc,SAAQ,aAAa;IAC9C,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,sBAAsB,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,WAAY,SAAQ,aAAa;IAC5C,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,eAAe,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ export { ENVELOPE_VERSION, ENVELOPE_SHAPES, createEnvelope, parseEnvelope, isEnvelope, assertShapeData } from './envelope.js';
2
+ export type { Envelope, EnvelopeShape, EnvelopeDataByShape } from './envelope.js';
3
+ export { createRegistry, parseRegistryKey } from './registry.js';
4
+ export type { Registry, RegistryKey, ParsedKey } from './registry.js';
5
+ export { runCombo } from './runner.js';
6
+ export type { RunRequest, Fetcher } from './runner.js';
7
+ export { LinkCoreError, EnvelopeError, RegistryError, RunnerError } from './errors.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC9H,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ // link-core:沉底零依赖。envelope 全字段 + registry key 类型 + runner 契约。
2
+ export { ENVELOPE_VERSION, ENVELOPE_SHAPES, createEnvelope, parseEnvelope, isEnvelope, assertShapeData } from './envelope.js';
3
+ export { createRegistry, parseRegistryKey } from './registry.js';
4
+ export { runCombo } from './runner.js';
5
+ export { LinkCoreError, EnvelopeError, RegistryError, RunnerError } from './errors.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAE9H,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,14 @@
1
+ export type RegistryKey = string;
2
+ export interface ParsedKey {
3
+ skill: string;
4
+ combo: string;
5
+ key: string;
6
+ }
7
+ export declare function parseRegistryKey(raw: unknown): ParsedKey;
8
+ export interface Registry {
9
+ keys(): string[];
10
+ has(key: string): boolean;
11
+ resolve(key: string): ParsedKey;
12
+ }
13
+ export declare function createRegistry(knownKeys: readonly string[]): Registry;
14
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAIjC,MAAM,WAAW,SAAS;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;CAAE;AAGzE,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,CAWxD;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,IAAI,MAAM,EAAE,CAAC;IACjB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,CAcrE"}
@@ -0,0 +1,40 @@
1
+ // registry:电话本。key 语法 skill.combo 命名空间修复:裸 key 拒绝并给修复提示;对不上注册表即 fail。
2
+ import { RegistryError } from './errors.js';
3
+ const KEY_RE = /^[a-z][a-z0-9-]*\.[a-z0-9][a-z0-9-.]*$/;
4
+ // 命名空间修复:trim + 小写化仅做提示,正文保持原样校验;裸 key(如 today)拒绝,提示补命名空间为 <skill>.today。
5
+ export function parseRegistryKey(raw) {
6
+ if (typeof raw !== 'string' || raw.length === 0)
7
+ throw new RegistryError('registry key 须为非空字符串');
8
+ const key = raw.trim();
9
+ if (!KEY_RE.test(key)) {
10
+ const hint = key.includes('.')
11
+ ? '命名空间非法,形如 skill.combo'
12
+ : '缺命名空间,应为 <skill>.' + key;
13
+ throw new RegistryError('非法 registry key:' + JSON.stringify(raw) + '(' + hint + ')');
14
+ }
15
+ const dot = key.indexOf('.');
16
+ return { skill: key.slice(0, dot), combo: key.slice(dot + 1), key };
17
+ }
18
+ export function createRegistry(knownKeys) {
19
+ const set = new Set();
20
+ for (const k of knownKeys)
21
+ set.add(parseRegistryKey(k).key);
22
+ return {
23
+ keys: () => [...set],
24
+ has: (key) => {
25
+ try {
26
+ return set.has(parseRegistryKey(key).key);
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ },
32
+ resolve: (key) => {
33
+ const parsed = parseRegistryKey(key);
34
+ if (!set.has(parsed.key))
35
+ throw new RegistryError('未知 registry key:' + parsed.key + '(对不上即 fail,不返空)');
36
+ return parsed;
37
+ },
38
+ };
39
+ }
40
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAI5C,MAAM,MAAM,GAAG,wCAAwC,CAAC;AAIxD,0EAA0E;AAC1E,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,aAAa,CAAC,sBAAsB,CAAC,CAAC;IACjG,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;YAC5B,CAAC,CAAC,uBAAuB;YACzB,CAAC,CAAC,mBAAmB,GAAG,GAAG,CAAC;QAC9B,MAAM,IAAI,aAAa,CAAC,kBAAkB,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;AACtE,CAAC;AAQD,MAAM,UAAU,cAAc,CAAC,SAA4B;IACzD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,SAAS;QAAE,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5D,OAAO;QACL,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC;QACpB,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE;YACnB,IAAI,CAAC;gBAAC,OAAO,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC;gBAAC,OAAO,KAAK,CAAC;YAAC,CAAC;QAC5E,CAAC;QACD,OAAO,EAAE,CAAC,GAAW,EAAE,EAAE;YACvB,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;YACrC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC;gBAAE,MAAM,IAAI,aAAa,CAAC,kBAAkB,GAAG,MAAM,CAAC,GAAG,GAAG,iBAAiB,CAAC,CAAC;YACvG,OAAO,MAAM,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { type Envelope, type EnvelopeShape, type EnvelopeDataByShape } from './envelope.js';
2
+ import type { Registry } from './registry.js';
3
+ export interface RunRequest<S extends EnvelopeShape> {
4
+ key: string;
5
+ shape: S;
6
+ params?: Record<string, unknown>;
7
+ signal?: AbortSignal;
8
+ }
9
+ export type Fetcher<S extends EnvelopeShape> = (req: {
10
+ skill: string;
11
+ combo: string;
12
+ params: Record<string, unknown>;
13
+ signal?: AbortSignal;
14
+ }) => Promise<EnvelopeDataByShape[S]> | EnvelopeDataByShape[S];
15
+ export declare function runCombo<S extends EnvelopeShape>(registry: Registry, req: RunRequest<S>, fetchData: Fetcher<S>): Promise<Envelope<S>>;
16
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,KAAK,QAAQ,EAAE,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAE5G,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,aAAa;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,CAAC,CAAC;IACT,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,aAAa,IAAI,CAC7C,GAAG,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,KACzF,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC;AAE9D,wBAAsB,QAAQ,CAAC,CAAC,SAAS,aAAa,EACpD,QAAQ,EAAE,QAAQ,EAClB,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,EAClB,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,GACpB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAuBtB"}
package/dist/runner.js ADDED
@@ -0,0 +1,33 @@
1
+ // runner:唯一取数编排。key 先过 registry,载荷再过 envelope 全字段;失败 throw,永不合成空数组。
2
+ import { createEnvelope } from './envelope.js';
3
+ import { RunnerError } from './errors.js';
4
+ export async function runCombo(registry, req, fetchData) {
5
+ if (typeof req !== 'object' || req === null)
6
+ throw new RunnerError('请求须为对象');
7
+ let skill = '';
8
+ try {
9
+ skill = registry.resolve(req.key).skill;
10
+ }
11
+ catch (e) {
12
+ throw new RunnerError('key 解析失败:' + e.message, { cause: e });
13
+ }
14
+ if (req.params !== undefined && (typeof req.params !== 'object' || req.params === null || Array.isArray(req.params))) {
15
+ throw new RunnerError('params 须为对象');
16
+ }
17
+ let data;
18
+ try {
19
+ data = await fetchData({ skill, combo: req.key.split('.').slice(1).join('.'), params: req.params ?? {}, signal: req.signal });
20
+ }
21
+ catch (e) {
22
+ if (e instanceof RunnerError)
23
+ throw e;
24
+ throw new RunnerError('取数失败:' + e.message, { cause: e });
25
+ }
26
+ try {
27
+ return createEnvelope({ skill, shape: req.shape, key: req.key, data });
28
+ }
29
+ catch (e) {
30
+ throw new RunnerError('载荷未过 envelope 全字段:' + e.message, { cause: e });
31
+ }
32
+ }
33
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,OAAO,EAAE,cAAc,EAA+D,MAAM,eAAe,CAAC;AAC5G,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAc1C,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,QAAkB,EAClB,GAAkB,EAClB,SAAqB;IAErB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,MAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC7E,IAAI,KAAK,GAAG,EAAE,CAAC;IACf,IAAI,CAAC;QACH,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;IAC1C,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,WAAW,CAAC,WAAW,GAAI,CAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QACrH,MAAM,IAAI,WAAW,CAAC,aAAa,CAAC,CAAC;IACvC,CAAC;IACD,IAAI,IAA4B,CAAC;IACjC,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAChI,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,WAAW;YAAE,MAAM,CAAC,CAAC;QACtC,MAAM,IAAI,WAAW,CAAC,OAAO,GAAI,CAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,CAAC;QACH,OAAO,cAAc,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IACzE,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,WAAW,CAAC,oBAAoB,GAAI,CAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IACnF,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "base-link-core",
3
+ "version": "0.1.0",
4
+ "description": "联动沉底包:envelope+registry+runner+校验,零依赖",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": "./dist/index.js"
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "engines": {
15
+ "node": ">=22.13"
16
+ },
17
+ "scripts": {
18
+ "build": "tsc -b",
19
+ "test": "node --test ../../test/scaffold.test.mjs"
20
+ }
21
+ }