@fenglimg/fabric-shared 1.7.0 → 1.8.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ interface AtomicWriteOptions {
2
+ fsync?: boolean;
3
+ }
4
+ interface AtomicWriteJsonOptions extends AtomicWriteOptions {
5
+ indent?: number;
6
+ }
7
+ declare function atomicWriteText(path: string, content: string, opts?: AtomicWriteOptions): Promise<void>;
8
+ declare function atomicWriteJson(path: string, value: unknown, opts?: AtomicWriteJsonOptions): Promise<void>;
9
+ interface LedgerWriteQueue {
10
+ append(path: string, line: string): Promise<void>;
11
+ }
12
+ declare function createLedgerWriteQueue(): LedgerWriteQueue;
13
+
14
+ export { type AtomicWriteJsonOptions, type AtomicWriteOptions, type LedgerWriteQueue, atomicWriteJson, atomicWriteText, createLedgerWriteQueue };
@@ -0,0 +1,60 @@
1
+ // src/node/atomic-write.ts
2
+ import { appendFile, open, rename, unlink, writeFile } from "fs/promises";
3
+ function makeTmpSuffix() {
4
+ const rand = Math.floor(Math.random() * 65535).toString(16).padStart(4, "0");
5
+ return `.${process.pid}.${Date.now()}.${rand}.tmp`;
6
+ }
7
+ async function atomicWriteText(path, content, opts) {
8
+ const tmpPath = path + makeTmpSuffix();
9
+ try {
10
+ if (opts?.fsync) {
11
+ const fd = await open(tmpPath, "w");
12
+ try {
13
+ await fd.writeFile(content, "utf8");
14
+ await fd.datasync();
15
+ } finally {
16
+ await fd.close();
17
+ }
18
+ } else {
19
+ await writeFile(tmpPath, content, "utf8");
20
+ }
21
+ await rename(tmpPath, path);
22
+ } catch (err) {
23
+ try {
24
+ await unlink(tmpPath);
25
+ } catch {
26
+ }
27
+ throw err;
28
+ }
29
+ }
30
+ async function atomicWriteJson(path, value, opts) {
31
+ const indent = opts?.indent ?? 2;
32
+ const content = JSON.stringify(value, null, indent) + "\n";
33
+ await atomicWriteText(path, content, { fsync: opts?.fsync });
34
+ }
35
+ function createLedgerWriteQueue() {
36
+ const chains = /* @__PURE__ */ new Map();
37
+ async function doAppend(path, line) {
38
+ const normalized = line.endsWith("\n") ? line : line + "\n";
39
+ await appendFile(path, normalized, "utf8");
40
+ }
41
+ return {
42
+ append(path, line) {
43
+ const prev = chains.get(path) ?? Promise.resolve();
44
+ const next = prev.catch(() => void 0).then(() => doAppend(path, line));
45
+ chains.set(path, next);
46
+ const guarded = next.catch(() => void 0);
47
+ guarded.finally(() => {
48
+ if (chains.get(path) === next) {
49
+ chains.delete(path);
50
+ }
51
+ });
52
+ return next;
53
+ }
54
+ };
55
+ }
56
+ export {
57
+ atomicWriteJson,
58
+ atomicWriteText,
59
+ createLedgerWriteQueue
60
+ };
@@ -0,0 +1,12 @@
1
+ declare const FABRIC_BOOTSTRAP_PATH = ".fabric/bootstrap/README.md";
2
+ /**
3
+ * Build the content for .fabric/bootstrap/README.md.
4
+ *
5
+ * Produces the same structured bootstrap guide that `fab init` writes, using
6
+ * the detected framework kind and the project name from package.json. This
7
+ * lives in shared so both the CLI and the server doctor --fix path can use the
8
+ * identical builder.
9
+ */
10
+ declare function buildBootstrapContent(projectRoot: string): string;
11
+
12
+ export { FABRIC_BOOTSTRAP_PATH, buildBootstrapContent };
@@ -0,0 +1,52 @@
1
+ import {
2
+ detectFramework
3
+ } from "../chunk-GI6L6VTT.js";
4
+
5
+ // src/node/bootstrap-guide.ts
6
+ import { existsSync, readFileSync } from "fs";
7
+ import { join, parse } from "path";
8
+ var FABRIC_BOOTSTRAP_PATH = ".fabric/bootstrap/README.md";
9
+ function buildBootstrapContent(projectRoot) {
10
+ const framework = detectFramework(projectRoot);
11
+ const projectName = readProjectName(projectRoot) ?? parse(projectRoot).base;
12
+ const frameworkKind = framework.kind;
13
+ const content = `# ${projectName} \u2014 Fabric Bootstrap Protocol
14
+
15
+ ## CORE RULES (DO NOT TRANSLATE)
16
+
17
+ MUST: Treat this file as the Fabric Protocol bootstrap for this repository.
18
+ MUST: Treat \`.fabric/rules/\` as the source of truth for all Fabric rule bodies.
19
+ MUST: Before ANY code reading, architecture planning, or logic modification, call \`fab_plan_context(paths=[<target file>])\`, then call \`fab_get_rule_sections\`.
20
+ MUST: When creating or changing an L1/L2 rule node, keep \`.fabric/agents.meta.json\` synchronized through Fabric tooling.
21
+ MUST: Preserve protected tokens exactly: \`AGENTS.md\`, \`.fabric/rules/\`, \`.fabric/agents.meta.json\`, \`fab_plan_context\`, \`fab_get_rule_sections\`, \`rule sources\`, \`rule source mirroring\`, \`MUST\`, \`NEVER\`.
22
+ NEVER: Add import-style directive lines to this bootstrap file.
23
+ NEVER: Put framework, domain, repository rule bodies, or submodule rules in this file.
24
+ NEVER: Create colocated \`AGENTS.md\` rule files under source directories.
25
+
26
+ ## Usage
27
+
28
+ - This file bootstraps the Fabric Protocol; it does not carry project-specific rules.
29
+ - Detailed bootstrap notes are in \`.fabric/bootstrap/README.md\`.
30
+ - Detected framework kind: \`${frameworkKind}\`.
31
+ - This repository uses \`rule source mirroring\`: source directories contain ZERO rule files, while \`.fabric/rules/\` mirrors source paths for AI constraints.
32
+ - Root-level rules belong in \`.fabric/rules/root.md\`; cross-domain rules in \`.fabric/rules/_cross/\`.
33
+ - If \`.fabric/rules/root.md\` is missing, complete the Fabric initialization flow before normal coding.
34
+ `;
35
+ return content;
36
+ }
37
+ function readProjectName(projectRoot) {
38
+ const packageJsonPath = join(projectRoot, "package.json");
39
+ if (!existsSync(packageJsonPath)) {
40
+ return void 0;
41
+ }
42
+ try {
43
+ const raw = JSON.parse(readFileSync(packageJsonPath, "utf8"));
44
+ return typeof raw.name === "string" && raw.name.length > 0 ? raw.name : void 0;
45
+ } catch {
46
+ return void 0;
47
+ }
48
+ }
49
+ export {
50
+ FABRIC_BOOTSTRAP_PATH,
51
+ buildBootstrapContent
52
+ };
@@ -0,0 +1,16 @@
1
+ interface PayloadGuardOptions {
2
+ warnBytes?: number;
3
+ hardBytes?: number;
4
+ }
5
+ interface PayloadGuardResult {
6
+ bytes: number;
7
+ warning?: {
8
+ code: 'mcp_payload_warn';
9
+ message: string;
10
+ bytes: number;
11
+ threshold: number;
12
+ };
13
+ }
14
+ declare function enforcePayloadLimit(serializedPayload: string, opts?: PayloadGuardOptions): PayloadGuardResult;
15
+
16
+ export { type PayloadGuardOptions, type PayloadGuardResult, enforcePayloadLimit };
@@ -0,0 +1,40 @@
1
+ import {
2
+ MCPError
3
+ } from "../chunk-3SZRB42B.js";
4
+
5
+ // src/node/mcp-payload-guard.ts
6
+ var McpPayloadTooLargeError = class extends MCPError {
7
+ code = "MCP_PAYLOAD_TOO_LARGE";
8
+ httpStatus = 413;
9
+ };
10
+ var DEFAULT_WARN = 16384;
11
+ var DEFAULT_HARD = 65536;
12
+ function enforcePayloadLimit(serializedPayload, opts) {
13
+ const warnAt = opts?.warnBytes ?? DEFAULT_WARN;
14
+ const hardAt = opts?.hardBytes ?? DEFAULT_HARD;
15
+ const bytes = Buffer.byteLength(serializedPayload, "utf8");
16
+ if (bytes > hardAt) {
17
+ throw new McpPayloadTooLargeError(
18
+ `MCP payload ${bytes} bytes exceeds hard limit ${hardAt}`,
19
+ {
20
+ actionHint: `Reduce response size or increase fabric.config.json mcpPayloadLimits.hardBytes`,
21
+ details: { bytes, threshold: hardAt }
22
+ }
23
+ );
24
+ }
25
+ if (bytes > warnAt) {
26
+ return {
27
+ bytes,
28
+ warning: {
29
+ code: "mcp_payload_warn",
30
+ message: `Payload ${bytes}B exceeds warning threshold ${warnAt}B`,
31
+ bytes,
32
+ threshold: warnAt
33
+ }
34
+ };
35
+ }
36
+ return { bytes };
37
+ }
38
+ export {
39
+ enforcePayloadLimit
40
+ };
package/dist/node.js CHANGED
@@ -1,175 +1,6 @@
1
- // src/detector.ts
2
- import { existsSync, readFileSync } from "fs";
3
- import { join } from "path";
4
- function detectFramework(root) {
5
- const evidence = [];
6
- const creatorConfigPath = join(root, "project.config.json");
7
- if (existsSync(creatorConfigPath)) {
8
- const version = readCreatorVersion(creatorConfigPath);
9
- return {
10
- kind: "cocos-creator",
11
- version,
12
- subkind: inferCocosSubkind(root, version),
13
- evidence: version === "unknown" ? ["project.config.json"] : [`project.config.json: creator.version=${version}`],
14
- framework: "cocos-creator",
15
- confidence: "HIGH",
16
- ast_evidence: [],
17
- co_packages: collectProjectFileEvidence(root, ["package.json", "tsconfig.json"])
18
- };
19
- }
20
- const packageJsonPath = join(root, "package.json");
21
- if (existsSync(packageJsonPath)) {
22
- const packageJson = readPackageJson(packageJsonPath);
23
- const creatorVersion = packageJson.creator?.version;
24
- if (typeof creatorVersion === "string" && creatorVersion.trim().length > 0) {
25
- const deps2 = collectDependencyVersions(packageJson);
26
- return {
27
- kind: "cocos-creator",
28
- version: creatorVersion,
29
- subkind: inferCocosSubkind(root, creatorVersion),
30
- evidence: [`package.json: creator.version=${creatorVersion}`],
31
- framework: "cocos-creator",
32
- confidence: "HIGH",
33
- ast_evidence: [],
34
- co_packages: collectCoPackages(deps2, "cocos-creator", root)
35
- };
36
- }
37
- const deps = collectDependencyVersions(packageJson);
38
- for (const [dependencyName, kind] of [
39
- ["next", "next"],
40
- ["vite", "vite"],
41
- ["react", "react"],
42
- ["vue", "vue"]
43
- ]) {
44
- if (deps.has(dependencyName)) {
45
- const version = deps.get(dependencyName) ?? "unknown";
46
- evidence.push(`package.json dependency: ${dependencyName}@${version}`);
47
- return {
48
- kind,
49
- version,
50
- subkind: inferPackageSubkind(kind),
51
- evidence,
52
- framework: kind,
53
- confidence: determinePackageConfidence(kind, deps, root),
54
- ast_evidence: [],
55
- co_packages: collectCoPackages(deps, kind, root)
56
- };
57
- }
58
- }
59
- evidence.push("package.json");
60
- }
61
- if (existsSync(join(root, "Cargo.toml"))) {
62
- return {
63
- kind: "rust",
64
- version: "unknown",
65
- subkind: "cargo-project",
66
- evidence: ["Cargo.toml"],
67
- framework: "rust",
68
- confidence: "HIGH",
69
- ast_evidence: [],
70
- co_packages: collectProjectFileEvidence(root, ["Cargo.lock"])
71
- };
72
- }
73
- if (existsSync(join(root, "pyproject.toml"))) {
74
- return {
75
- kind: "python",
76
- version: "unknown",
77
- subkind: "pyproject",
78
- evidence: ["pyproject.toml"],
79
- framework: "python",
80
- confidence: "HIGH",
81
- ast_evidence: [],
82
- co_packages: collectProjectFileEvidence(root, ["uv.lock", "poetry.lock", "requirements.txt"])
83
- };
84
- }
85
- return {
86
- kind: "unknown",
87
- version: "unknown",
88
- subkind: "unknown",
89
- evidence,
90
- framework: "unknown",
91
- confidence: "LOW",
92
- ast_evidence: [],
93
- co_packages: []
94
- };
95
- }
96
- function readPackageJson(packageJsonPath) {
97
- try {
98
- return JSON.parse(readFileSync(packageJsonPath, "utf8"));
99
- } catch {
100
- return {};
101
- }
102
- }
103
- function readCreatorVersion(creatorConfigPath) {
104
- try {
105
- const creatorConfig = JSON.parse(readFileSync(creatorConfigPath, "utf8"));
106
- return creatorConfig.creator?.version ?? "unknown";
107
- } catch {
108
- return "unknown";
109
- }
110
- }
111
- function collectDependencyVersions(packageJson) {
112
- return new Map([
113
- ...Object.entries(packageJson.dependencies ?? {}),
114
- ...Object.entries(packageJson.devDependencies ?? {}),
115
- ...Object.entries(packageJson.peerDependencies ?? {}),
116
- ...Object.entries(packageJson.optionalDependencies ?? {})
117
- ]);
118
- }
119
- function inferCocosSubkind(root, version) {
120
- const majorVersion = Number.parseInt(version.split(".")[0] ?? "", 10);
121
- if (majorVersion === 2) {
122
- return "javascript-traditional";
123
- }
124
- if (majorVersion >= 3) {
125
- return "typescript-component";
126
- }
127
- return existsSync(join(root, "tsconfig.json")) ? "typescript-component" : "javascript-traditional";
128
- }
129
- function inferPackageSubkind(kind) {
130
- switch (kind) {
131
- case "next":
132
- return "next-application";
133
- case "vite":
134
- return "vite-application";
135
- case "react":
136
- return "react-application";
137
- case "vue":
138
- return "vue-application";
139
- default:
140
- return "unknown";
141
- }
142
- }
143
- function determinePackageConfidence(kind, deps, root) {
144
- const coPackages = collectCoPackages(deps, kind, root);
145
- return coPackages.length > 0 ? "HIGH" : "MEDIUM";
146
- }
147
- function collectCoPackages(deps, kind, root) {
148
- const expectedPackagesByFramework = {
149
- next: ["react", "react-dom", "typescript"],
150
- vite: ["@vitejs/plugin-react", "@vitejs/plugin-vue", "typescript", "react", "vue"],
151
- react: ["react-dom", "@types/react", "@types/react-dom"],
152
- vue: ["@vitejs/plugin-vue", "typescript"],
153
- "cocos-creator": ["typescript"]
154
- };
155
- const expectedProjectFilesByFramework = {
156
- next: ["next.config.js", "next.config.mjs", "next.config.ts", "tsconfig.json"],
157
- vite: ["vite.config.js", "vite.config.mjs", "vite.config.ts", "tsconfig.json"],
158
- react: ["tsconfig.json"],
159
- vue: ["vue.config.js", "vite.config.ts", "tsconfig.json"],
160
- "cocos-creator": ["project.config.json", "tsconfig.json"]
161
- };
162
- return [
163
- ...compactStrings((expectedPackagesByFramework[kind] ?? []).map((packageName) => deps.has(packageName) ? packageName : null)),
164
- ...collectProjectFileEvidence(root, expectedProjectFilesByFramework[kind] ?? [])
165
- ];
166
- }
167
- function collectProjectFileEvidence(root, relativePaths) {
168
- return relativePaths.filter((relativePath) => existsSync(join(root, relativePath)));
169
- }
170
- function compactStrings(values) {
171
- return [...new Set(values.filter((value) => value !== null && value !== void 0 && value.length > 0))];
172
- }
1
+ import {
2
+ detectFramework
3
+ } from "./chunk-GI6L6VTT.js";
173
4
  export {
174
5
  detectFramework
175
6
  };