@tinyviber/a2h 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/README.md +229 -0
- package/bin/a2h.js +7 -0
- package/dist/actions/engine.d.ts +40 -0
- package/dist/actions/engine.d.ts.map +1 -0
- package/dist/actions/engine.js +227 -0
- package/dist/actions/engine.js.map +1 -0
- package/dist/actions/policy.d.ts +8 -0
- package/dist/actions/policy.d.ts.map +1 -0
- package/dist/actions/policy.js +55 -0
- package/dist/actions/policy.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +258 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +8 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +69 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/ir/build.d.ts +8 -0
- package/dist/ir/build.d.ts.map +1 -0
- package/dist/ir/build.js +397 -0
- package/dist/ir/build.js.map +1 -0
- package/dist/ir/enrich.d.ts +21 -0
- package/dist/ir/enrich.d.ts.map +1 -0
- package/dist/ir/enrich.js +177 -0
- package/dist/ir/enrich.js.map +1 -0
- package/dist/ir/identity.d.ts +3 -0
- package/dist/ir/identity.d.ts.map +1 -0
- package/dist/ir/identity.js +91 -0
- package/dist/ir/identity.js.map +1 -0
- package/dist/ir/scoring.d.ts +7 -0
- package/dist/ir/scoring.d.ts.map +1 -0
- package/dist/ir/scoring.js +88 -0
- package/dist/ir/scoring.js.map +1 -0
- package/dist/parsers/code.d.ts +3 -0
- package/dist/parsers/code.d.ts.map +1 -0
- package/dist/parsers/code.js +17 -0
- package/dist/parsers/code.js.map +1 -0
- package/dist/parsers/diff.d.ts +11 -0
- package/dist/parsers/diff.d.ts.map +1 -0
- package/dist/parsers/diff.js +125 -0
- package/dist/parsers/diff.js.map +1 -0
- package/dist/parsers/json.d.ts +3 -0
- package/dist/parsers/json.d.ts.map +1 -0
- package/dist/parsers/json.js +16 -0
- package/dist/parsers/json.js.map +1 -0
- package/dist/parsers/log.d.ts +3 -0
- package/dist/parsers/log.d.ts.map +1 -0
- package/dist/parsers/log.js +105 -0
- package/dist/parsers/log.js.map +1 -0
- package/dist/parsers/markdown.d.ts +15 -0
- package/dist/parsers/markdown.d.ts.map +1 -0
- package/dist/parsers/markdown.js +89 -0
- package/dist/parsers/markdown.js.map +1 -0
- package/dist/parsers/text.d.ts +24 -0
- package/dist/parsers/text.d.ts.map +1 -0
- package/dist/parsers/text.js +98 -0
- package/dist/parsers/text.js.map +1 -0
- package/dist/presentation/blocks.d.ts +14 -0
- package/dist/presentation/blocks.d.ts.map +1 -0
- package/dist/presentation/blocks.js +87 -0
- package/dist/presentation/blocks.js.map +1 -0
- package/dist/presentation/present.d.ts +12 -0
- package/dist/presentation/present.d.ts.map +1 -0
- package/dist/presentation/present.js +115 -0
- package/dist/presentation/present.js.map +1 -0
- package/dist/providers/mock.d.ts +3 -0
- package/dist/providers/mock.d.ts.map +1 -0
- package/dist/providers/mock.js +171 -0
- package/dist/providers/mock.js.map +1 -0
- package/dist/providers/registry.d.ts +12 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +20 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/types.d.ts +64 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +40 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/renderer/assets/index.html +41 -0
- package/dist/renderer/assets/js/actions.js +205 -0
- package/dist/renderer/assets/js/api.js +75 -0
- package/dist/renderer/assets/js/artifacts.js +422 -0
- package/dist/renderer/assets/js/blocks.js +301 -0
- package/dist/renderer/assets/js/bus.js +21 -0
- package/dist/renderer/assets/js/cards.js +137 -0
- package/dist/renderer/assets/js/dom.js +61 -0
- package/dist/renderer/assets/js/format.js +118 -0
- package/dist/renderer/assets/js/main.js +125 -0
- package/dist/renderer/assets/js/nav.js +80 -0
- package/dist/renderer/assets/js/router.js +64 -0
- package/dist/renderer/assets/js/store.js +69 -0
- package/dist/renderer/assets/js/views.js +356 -0
- package/dist/renderer/assets/styles.css +856 -0
- package/dist/renderer/content.d.ts +11 -0
- package/dist/renderer/content.d.ts.map +1 -0
- package/dist/renderer/content.js +114 -0
- package/dist/renderer/content.js.map +1 -0
- package/dist/scanner/classify.d.ts +17 -0
- package/dist/scanner/classify.d.ts.map +1 -0
- package/dist/scanner/classify.js +117 -0
- package/dist/scanner/classify.js.map +1 -0
- package/dist/scanner/git.d.ts +3 -0
- package/dist/scanner/git.d.ts.map +1 -0
- package/dist/scanner/git.js +43 -0
- package/dist/scanner/git.js.map +1 -0
- package/dist/scanner/ignore.d.ts +22 -0
- package/dist/scanner/ignore.d.ts.map +1 -0
- package/dist/scanner/ignore.js +57 -0
- package/dist/scanner/ignore.js.map +1 -0
- package/dist/scanner/scan.d.ts +8 -0
- package/dist/scanner/scan.d.ts.map +1 -0
- package/dist/scanner/scan.js +108 -0
- package/dist/scanner/scan.js.map +1 -0
- package/dist/security/boundary.d.ts +19 -0
- package/dist/security/boundary.d.ts.map +1 -0
- package/dist/security/boundary.js +59 -0
- package/dist/security/boundary.js.map +1 -0
- package/dist/security/urlPolicy.d.ts +4 -0
- package/dist/security/urlPolicy.d.ts.map +1 -0
- package/dist/security/urlPolicy.js +56 -0
- package/dist/security/urlPolicy.js.map +1 -0
- package/dist/security/workspaceRead.d.ts +25 -0
- package/dist/security/workspaceRead.d.ts.map +1 -0
- package/dist/security/workspaceRead.js +103 -0
- package/dist/security/workspaceRead.js.map +1 -0
- package/dist/semantics/load.d.ts +11 -0
- package/dist/semantics/load.d.ts.map +1 -0
- package/dist/semantics/load.js +537 -0
- package/dist/semantics/load.js.map +1 -0
- package/dist/semantics/resolve.d.ts +37 -0
- package/dist/semantics/resolve.d.ts.map +1 -0
- package/dist/semantics/resolve.js +211 -0
- package/dist/semantics/resolve.js.map +1 -0
- package/dist/semantics/types.d.ts +158 -0
- package/dist/semantics/types.d.ts.map +1 -0
- package/dist/semantics/types.js +3 -0
- package/dist/semantics/types.js.map +1 -0
- package/dist/server/server.d.ts +17 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +510 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/workspace.d.ts +53 -0
- package/dist/server/workspace.d.ts.map +1 -0
- package/dist/server/workspace.js +211 -0
- package/dist/server/workspace.js.map +1 -0
- package/dist/types.d.ts +570 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/dist/util/path.d.ts +11 -0
- package/dist/util/path.d.ts.map +1 -0
- package/dist/util/path.js +35 -0
- package/dist/util/path.js.map +1 -0
- package/dist/util/safeRead.d.ts +38 -0
- package/dist/util/safeRead.d.ts.map +1 -0
- package/dist/util/safeRead.js +117 -0
- package/dist/util/safeRead.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isSafeUrl = void 0;
|
|
7
|
+
exports.renderMarkdown = renderMarkdown;
|
|
8
|
+
exports.splitFrontmatter = splitFrontmatter;
|
|
9
|
+
const markdown_it_1 = __importDefault(require("markdown-it"));
|
|
10
|
+
const urlPolicy_1 = require("../security/urlPolicy");
|
|
11
|
+
// Markdown rendering is deliberately conservative:
|
|
12
|
+
// - raw HTML is escaped, never executed (`html: false`)
|
|
13
|
+
// - link protocols are validated (no javascript:/data:/vbscript:)
|
|
14
|
+
// - relative image srcs are rewritten through a caller-provided resolver
|
|
15
|
+
// The resolver is supplied by the content layer, which validates that the
|
|
16
|
+
// target file really exists inside the workspace before producing a URL.
|
|
17
|
+
/**
|
|
18
|
+
* The workspace's single URL policy, shared with the block renderer so a
|
|
19
|
+
* producer cannot get a laxer answer out of one path than the other.
|
|
20
|
+
*/
|
|
21
|
+
exports.isSafeUrl = urlPolicy_1.isSafeLinkHref;
|
|
22
|
+
function renderMarkdown(mdText, resolveImage) {
|
|
23
|
+
const md = new markdown_it_1.default({
|
|
24
|
+
html: false,
|
|
25
|
+
linkify: true,
|
|
26
|
+
breaks: false,
|
|
27
|
+
});
|
|
28
|
+
md.validateLink = exports.isSafeUrl;
|
|
29
|
+
if (resolveImage) {
|
|
30
|
+
const defaultImage = md.renderer.rules.image;
|
|
31
|
+
md.renderer.rules.image = (tokens, idx, options, env, self) => {
|
|
32
|
+
const token = tokens[idx];
|
|
33
|
+
const srcIdx = token.attrIndex('src');
|
|
34
|
+
if (srcIdx < 0)
|
|
35
|
+
return defaultImage ? defaultImage(tokens, idx, options, env, self) : '';
|
|
36
|
+
const rawSrc = token.attrs[srcIdx][1];
|
|
37
|
+
// Only a plain workspace-relative src is rewritten. Anything with a
|
|
38
|
+
// scheme, and anything protocol-relative, is left to the default rule —
|
|
39
|
+
// which the page's `img-src 'self' data:` then refuses to load, so a
|
|
40
|
+
// workspace cannot make the viewer fetch a remote pixel.
|
|
41
|
+
if ((0, urlPolicy_1.isWorkspaceRelative)(rawSrc)) {
|
|
42
|
+
const resolved = resolveImage(rawSrc);
|
|
43
|
+
if (resolved) {
|
|
44
|
+
token.attrs[srcIdx][1] = resolved;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
// Broken / unresolvable local image — render as plain alt text.
|
|
48
|
+
const alt = token.content || '';
|
|
49
|
+
return `<span class="a2h-img-missing">${md.utils.escapeHtml(alt || 'image')}</span>`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return defaultImage ? defaultImage(tokens, idx, options, env, self) : '';
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return md.render(mdText);
|
|
56
|
+
}
|
|
57
|
+
/** Extracts simple YAML-style frontmatter (key: value) from the top of a doc. */
|
|
58
|
+
function splitFrontmatter(text) {
|
|
59
|
+
if (!text.startsWith('---'))
|
|
60
|
+
return { body: text };
|
|
61
|
+
const lines = text.split(/\r?\n/);
|
|
62
|
+
// Skip the opening '---'
|
|
63
|
+
const closeIdx = lines.findIndex((l, i) => i > 0 && l.trim() === '---');
|
|
64
|
+
if (closeIdx === -1)
|
|
65
|
+
return { body: text };
|
|
66
|
+
const fm = {};
|
|
67
|
+
for (let i = 1; i < closeIdx; i++) {
|
|
68
|
+
const line = lines[i];
|
|
69
|
+
const m = line.match(/^([A-Za-z0-9_-]+)\s*:\s*(.*)$/);
|
|
70
|
+
if (m) {
|
|
71
|
+
const key = m[1];
|
|
72
|
+
let value = m[2].trim();
|
|
73
|
+
if (value === 'true')
|
|
74
|
+
value = true;
|
|
75
|
+
else if (value === 'false')
|
|
76
|
+
value = false;
|
|
77
|
+
else if (value === 'null' || value === '~')
|
|
78
|
+
value = null;
|
|
79
|
+
else if (/^-?\d+(\.\d+)?$/.test(value))
|
|
80
|
+
value = Number(value);
|
|
81
|
+
else if (value.startsWith('"') && value.endsWith('"'))
|
|
82
|
+
value = value.slice(1, -1);
|
|
83
|
+
fm[key] = value;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const body = lines.slice(closeIdx + 1).join('\n');
|
|
87
|
+
return { frontmatter: fm, body };
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/parsers/markdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface TextResult {
|
|
2
|
+
text: string;
|
|
3
|
+
truncated: boolean;
|
|
4
|
+
totalBytes: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Lenient whole-file read for preview rendering: an unreadable file comes back
|
|
8
|
+
* as empty text rather than throwing, because a preview that cannot be built
|
|
9
|
+
* should still render as something. The symlink guarantee lives in
|
|
10
|
+
* `readFileNoFollow`, which is the only implementation of "read a whole file
|
|
11
|
+
* without following a link".
|
|
12
|
+
*/
|
|
13
|
+
export declare function readFileText(path: string, maxBytes: number): TextResult;
|
|
14
|
+
export interface LinesResult {
|
|
15
|
+
lines: string[];
|
|
16
|
+
truncated: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Splits text into lines, dropping a single trailing newline. */
|
|
19
|
+
export declare function splitLines(text: string): string[];
|
|
20
|
+
/** Streams the file counting newlines, capped at maxBytes. */
|
|
21
|
+
export declare function countLinesFull(path: string, maxBytes: number): LinesResult;
|
|
22
|
+
/** Reads the last `count` lines of a file efficiently (from the end). */
|
|
23
|
+
export declare function readTailLines(path: string, count: number): LinesResult;
|
|
24
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/parsers/text.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,CAIvE;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,kEAAkE;AAClE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAIjD;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,CA0B1E;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CA8BtE"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readFileText = readFileText;
|
|
4
|
+
exports.splitLines = splitLines;
|
|
5
|
+
exports.countLinesFull = countLinesFull;
|
|
6
|
+
exports.readTailLines = readTailLines;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const safeRead_1 = require("../util/safeRead");
|
|
9
|
+
/**
|
|
10
|
+
* Lenient whole-file read for preview rendering: an unreadable file comes back
|
|
11
|
+
* as empty text rather than throwing, because a preview that cannot be built
|
|
12
|
+
* should still render as something. The symlink guarantee lives in
|
|
13
|
+
* `readFileNoFollow`, which is the only implementation of "read a whole file
|
|
14
|
+
* without following a link".
|
|
15
|
+
*/
|
|
16
|
+
function readFileText(path, maxBytes) {
|
|
17
|
+
const read = (0, safeRead_1.readFileNoFollow)(path, maxBytes);
|
|
18
|
+
if (!read)
|
|
19
|
+
return { text: '', truncated: false, totalBytes: 0 };
|
|
20
|
+
return { text: read.text, truncated: read.truncated, totalBytes: read.totalBytes };
|
|
21
|
+
}
|
|
22
|
+
/** Splits text into lines, dropping a single trailing newline. */
|
|
23
|
+
function splitLines(text) {
|
|
24
|
+
const lines = text.split(/\r?\n/);
|
|
25
|
+
if (lines.length > 0 && lines[lines.length - 1] === '')
|
|
26
|
+
lines.pop();
|
|
27
|
+
return lines;
|
|
28
|
+
}
|
|
29
|
+
/** Streams the file counting newlines, capped at maxBytes. */
|
|
30
|
+
function countLinesFull(path, maxBytes) {
|
|
31
|
+
let fd;
|
|
32
|
+
try {
|
|
33
|
+
fd = (0, safeRead_1.openNoFollow)(path);
|
|
34
|
+
const stat = (0, node_fs_1.fstatSync)(fd);
|
|
35
|
+
const totalBytes = stat.size;
|
|
36
|
+
const toScan = Math.min(totalBytes, maxBytes);
|
|
37
|
+
const chunk = 256 * 1024;
|
|
38
|
+
const buf = Buffer.alloc(chunk);
|
|
39
|
+
let offset = 0;
|
|
40
|
+
let lines = 0;
|
|
41
|
+
let truncated = false;
|
|
42
|
+
while (offset < toScan) {
|
|
43
|
+
const want = Math.min(chunk, toScan - offset);
|
|
44
|
+
const n = (0, node_fs_1.readSync)(fd, buf, 0, want, offset);
|
|
45
|
+
if (n <= 0)
|
|
46
|
+
break;
|
|
47
|
+
for (let i = 0; i < n; i++)
|
|
48
|
+
if (buf[i] === 0x0a)
|
|
49
|
+
lines++;
|
|
50
|
+
offset += n;
|
|
51
|
+
}
|
|
52
|
+
truncated = totalBytes > maxBytes;
|
|
53
|
+
return { lines: [String(lines)], truncated };
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return { lines: [], truncated: false };
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
if (fd !== undefined)
|
|
60
|
+
(0, node_fs_1.closeSync)(fd);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** Reads the last `count` lines of a file efficiently (from the end). */
|
|
64
|
+
function readTailLines(path, count) {
|
|
65
|
+
let fd;
|
|
66
|
+
try {
|
|
67
|
+
fd = (0, safeRead_1.openNoFollow)(path);
|
|
68
|
+
const stat = (0, node_fs_1.fstatSync)(fd);
|
|
69
|
+
const totalBytes = stat.size;
|
|
70
|
+
// Read the final chunk; expand if we did not collect enough lines.
|
|
71
|
+
let chunk = 64 * 1024;
|
|
72
|
+
let text = '';
|
|
73
|
+
let truncated = false;
|
|
74
|
+
for (let attempt = 0; attempt < 4; attempt++) {
|
|
75
|
+
const start = Math.max(0, totalBytes - chunk);
|
|
76
|
+
const len = totalBytes - start;
|
|
77
|
+
const buf = Buffer.alloc(len);
|
|
78
|
+
const n = (0, node_fs_1.readSync)(fd, buf, 0, len, start);
|
|
79
|
+
text = buf.slice(0, n).toString('utf8');
|
|
80
|
+
const lines = splitLines(text);
|
|
81
|
+
if (lines.length >= count || start === 0) {
|
|
82
|
+
truncated = totalBytes > chunk;
|
|
83
|
+
return { lines: lines.slice(-count), truncated };
|
|
84
|
+
}
|
|
85
|
+
chunk *= 4;
|
|
86
|
+
}
|
|
87
|
+
const lines = splitLines(text);
|
|
88
|
+
return { lines: lines.slice(-count), truncated: true };
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return { lines: [], truncated: false };
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
if (fd !== undefined)
|
|
95
|
+
(0, node_fs_1.closeSync)(fd);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/parsers/text.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Block } from '../types';
|
|
2
|
+
import type { WorkspaceReader } from '../security/workspaceRead';
|
|
3
|
+
export interface BlockContext {
|
|
4
|
+
/**
|
|
5
|
+
* The only route from a producer-authored path to bytes. Absent means no
|
|
6
|
+
* file-backed block can resolve — which is the correct default for a caller
|
|
7
|
+
* that has no scanner index to check against.
|
|
8
|
+
*/
|
|
9
|
+
reader?: WorkspaceReader;
|
|
10
|
+
/** Maps an already-validated workspace image path to a servable URL. */
|
|
11
|
+
imageUrl?: (relPath: string) => string | undefined;
|
|
12
|
+
}
|
|
13
|
+
export declare function resolveBlocks(blocks: Block[] | undefined, ctx: BlockContext): Block[] | undefined;
|
|
14
|
+
//# sourceMappingURL=blocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../../src/presentation/blocks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAoBjE,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CACpD;AAID,wBAAgB,aAAa,CAC3B,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAC3B,GAAG,EAAE,YAAY,GAChB,KAAK,EAAE,GAAG,SAAS,CAQrB"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveBlocks = resolveBlocks;
|
|
4
|
+
const markdown_1 = require("../parsers/markdown");
|
|
5
|
+
const urlPolicy_1 = require("../security/urlPolicy");
|
|
6
|
+
const path_1 = require("../util/path");
|
|
7
|
+
const MAX_BLOCK_FILE_BYTES = 512 * 1024;
|
|
8
|
+
function resolveBlocks(blocks, ctx) {
|
|
9
|
+
if (!blocks || blocks.length === 0)
|
|
10
|
+
return undefined;
|
|
11
|
+
const out = [];
|
|
12
|
+
for (const block of blocks) {
|
|
13
|
+
const resolved = resolveBlock(block, ctx);
|
|
14
|
+
if (resolved)
|
|
15
|
+
out.push(resolved);
|
|
16
|
+
}
|
|
17
|
+
return out.length ? out : undefined;
|
|
18
|
+
}
|
|
19
|
+
function resolveBlock(block, ctx) {
|
|
20
|
+
if (!block || typeof block.type !== 'string')
|
|
21
|
+
return undefined;
|
|
22
|
+
const record = block;
|
|
23
|
+
if (block.type === 'markdown') {
|
|
24
|
+
const source = readBlockText(record, ctx);
|
|
25
|
+
if (source === undefined)
|
|
26
|
+
return undefined;
|
|
27
|
+
const baseDir = typeof record.path === 'string' ? dirOf(record.path) : '';
|
|
28
|
+
return {
|
|
29
|
+
...record,
|
|
30
|
+
type: 'markdown',
|
|
31
|
+
html: (0, markdown_1.renderMarkdown)(source, (src) => resolveBlockImage(src, baseDir, ctx)),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Anything that will become an `<a href>` is normalised here, at the
|
|
35
|
+
// boundary, rather than trusted to the renderer: an unsafe scheme is stripped
|
|
36
|
+
// (the item keeps its title, it just stops being a link). The client applies
|
|
37
|
+
// the same policy again — same allowlist, two enforcement points.
|
|
38
|
+
if (block.type === 'list') {
|
|
39
|
+
return { ...record, type: 'list', items: sanitizeListItems(record.items) };
|
|
40
|
+
}
|
|
41
|
+
return block;
|
|
42
|
+
}
|
|
43
|
+
function sanitizeListItems(items) {
|
|
44
|
+
if (!Array.isArray(items))
|
|
45
|
+
return items;
|
|
46
|
+
return items.map((item) => {
|
|
47
|
+
if (!item || typeof item !== 'object' || Array.isArray(item))
|
|
48
|
+
return item;
|
|
49
|
+
const record = item;
|
|
50
|
+
if (record.href === undefined || (0, urlPolicy_1.isSafeLinkHref)(record.href))
|
|
51
|
+
return record;
|
|
52
|
+
const clean = { ...record };
|
|
53
|
+
delete clean.href;
|
|
54
|
+
return clean;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function readBlockText(record, ctx) {
|
|
58
|
+
if (typeof record.text === 'string') {
|
|
59
|
+
return record.text.trim() ? record.text : undefined;
|
|
60
|
+
}
|
|
61
|
+
if (typeof record.path === 'string') {
|
|
62
|
+
// The reader applies the workspace boundary; a refusal and a missing file
|
|
63
|
+
// look the same from here, and both mean "this block has nothing to show".
|
|
64
|
+
const text = ctx.reader?.readText(record.path, MAX_BLOCK_FILE_BYTES);
|
|
65
|
+
// An empty block would render as a blank frame, so treat it as absent.
|
|
66
|
+
if (text === undefined || !text.trim())
|
|
67
|
+
return undefined;
|
|
68
|
+
return text;
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
function resolveBlockImage(src, baseDir, ctx) {
|
|
73
|
+
const { reader, imageUrl } = ctx;
|
|
74
|
+
if (!reader || !imageUrl)
|
|
75
|
+
return undefined;
|
|
76
|
+
const clean = decodeURIComponent(src.split('#')[0].split('?')[0]).trim();
|
|
77
|
+
const rel = baseDir ? (0, path_1.normalizeRel)(`${baseDir}/${clean}`) : (0, path_1.normalizeRel)(clean);
|
|
78
|
+
if (!rel)
|
|
79
|
+
return undefined;
|
|
80
|
+
if (!reader.canReadImage(rel))
|
|
81
|
+
return undefined;
|
|
82
|
+
return imageUrl(rel);
|
|
83
|
+
}
|
|
84
|
+
function dirOf(path) {
|
|
85
|
+
return path.includes('/') ? path.slice(0, path.lastIndexOf('/')) : '';
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=blocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocks.js","sourceRoot":"","sources":["../../src/presentation/blocks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Block, Presentation, SemanticIR } from '../types';
|
|
2
|
+
import type { ResolvedSemantics } from '../semantics/resolve';
|
|
3
|
+
import { type BlockContext } from './blocks';
|
|
4
|
+
export interface PresentOptions {
|
|
5
|
+
semantics?: ResolvedSemantics;
|
|
6
|
+
/** Needed to resolve producer-authored markdown blocks. */
|
|
7
|
+
blockContext?: BlockContext;
|
|
8
|
+
}
|
|
9
|
+
export declare function present(ir: SemanticIR, options?: PresentOptions): Presentation;
|
|
10
|
+
/** Resolves the producer-authored blocks attached to one artifact node. */
|
|
11
|
+
export declare function resolveArtifactBlocks(blocks: Block[] | undefined, ctx: BlockContext): Block[] | undefined;
|
|
12
|
+
//# sourceMappingURL=present.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"present.d.ts","sourceRoot":"","sources":["../../src/presentation/present.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,KAAK,EACL,YAAY,EAGZ,UAAU,EAGX,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAiB,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAoB5D,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,2DAA2D;IAC3D,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,GAAE,cAAmB,GAAG,YAAY,CAkDlF;AAuDD,2EAA2E;AAC3E,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,EAC3B,GAAG,EAAE,YAAY,GAChB,KAAK,EAAE,GAAG,SAAS,CAErB"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.present = present;
|
|
4
|
+
exports.resolveArtifactBlocks = resolveArtifactBlocks;
|
|
5
|
+
const blocks_1 = require("./blocks");
|
|
6
|
+
// Presentation layer: maps the semantic IR into the final ordered view the
|
|
7
|
+
// renderer consumes. This is the fourth tree — ordering, "what to read first",
|
|
8
|
+
// section descriptions, and the task/run/action surface live here, not in the
|
|
9
|
+
// IR. Nothing in this file is renderer-specific.
|
|
10
|
+
const SECTION_DESCRIPTIONS = {
|
|
11
|
+
overview: 'The entry point — what this workspace is about.',
|
|
12
|
+
reports: 'Written outputs, notes, and summaries.',
|
|
13
|
+
changes: 'Diffs and patches worth reviewing.',
|
|
14
|
+
visuals: 'Screenshots and generated images.',
|
|
15
|
+
logs: 'Build and run logs.',
|
|
16
|
+
data: 'Structured JSON data.',
|
|
17
|
+
code: 'Source files.',
|
|
18
|
+
other: 'Everything else the scanner found.',
|
|
19
|
+
};
|
|
20
|
+
const MAX_HIGHLIGHTS = 5;
|
|
21
|
+
function present(ir, options = {}) {
|
|
22
|
+
const semantics = options.semantics;
|
|
23
|
+
// No reader means no file-backed block resolves. That is the safe default:
|
|
24
|
+
// a caller without a scanner index cannot accidentally grant disk access by
|
|
25
|
+
// forgetting a check.
|
|
26
|
+
const ctx = options.blockContext ?? {};
|
|
27
|
+
const sections = ir.root.children.map((section) => ({
|
|
28
|
+
id: section.id,
|
|
29
|
+
title: section.title,
|
|
30
|
+
kind: section.id,
|
|
31
|
+
description: SECTION_DESCRIPTIONS[section.id],
|
|
32
|
+
artifacts: section.children.map(toView),
|
|
33
|
+
explicit: Boolean(semantics?.groups.some((g) => g.id === section.id)),
|
|
34
|
+
}));
|
|
35
|
+
const allArtifacts = sections.flatMap((s) => s.artifacts);
|
|
36
|
+
const byId = new Map(allArtifacts.map((a) => [a.id, a]));
|
|
37
|
+
const nonReadme = allArtifacts.filter((a) => a.kind !== 'readme');
|
|
38
|
+
nonReadme.sort((a, b) => b.priority - a.priority || (b.meta?.mtimeMs ?? 0) - (a.meta?.mtimeMs ?? 0));
|
|
39
|
+
const tasks = (semantics?.tasks ?? []).map((task) => attachTaskArtifacts(task, byId, ctx));
|
|
40
|
+
const runs = (semantics?.runs ?? []).map((run) => ({
|
|
41
|
+
...run,
|
|
42
|
+
blocks: (0, blocks_1.resolveBlocks)(run.blocks, ctx),
|
|
43
|
+
}));
|
|
44
|
+
const warnings = [...ir.warnings, ...(semantics?.warnings ?? [])];
|
|
45
|
+
return {
|
|
46
|
+
identity: ir.identity,
|
|
47
|
+
git: ir.git,
|
|
48
|
+
stats: ir.stats,
|
|
49
|
+
semantics: ir.semantics,
|
|
50
|
+
simulatedActions: semantics?.actions.some((a) => a.simulated) ?? false,
|
|
51
|
+
highlights: nonReadme.slice(0, MAX_HIGHLIGHTS),
|
|
52
|
+
sections,
|
|
53
|
+
tasks,
|
|
54
|
+
runs,
|
|
55
|
+
actions: semantics?.actions ?? [],
|
|
56
|
+
panels: (0, blocks_1.resolveBlocks)(semantics?.panels, ctx) ?? [],
|
|
57
|
+
audit: [],
|
|
58
|
+
warnings,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function toView(node) {
|
|
62
|
+
return {
|
|
63
|
+
id: node.id,
|
|
64
|
+
title: node.title,
|
|
65
|
+
kind: node.kind ?? 'file',
|
|
66
|
+
content: node.content ?? 'file',
|
|
67
|
+
path: node.path,
|
|
68
|
+
summary: node.summary,
|
|
69
|
+
meta: node.meta,
|
|
70
|
+
tags: node.tags,
|
|
71
|
+
priority: node.priority,
|
|
72
|
+
source: node.source,
|
|
73
|
+
group: node.group,
|
|
74
|
+
taskId: node.taskId,
|
|
75
|
+
relations: node.relations,
|
|
76
|
+
metrics: node.metrics,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* A task's artifact list is the union of what the producer declared and what
|
|
81
|
+
* items actually point at the task. Paths that do not resolve to a scanned
|
|
82
|
+
* file are dropped rather than rendered as dead links.
|
|
83
|
+
*/
|
|
84
|
+
function attachTaskArtifacts(task, byId, ctx) {
|
|
85
|
+
const seen = new Set();
|
|
86
|
+
const artifacts = [];
|
|
87
|
+
for (const path of task.artifacts) {
|
|
88
|
+
const normalized = path.replace(/^\.\//, '');
|
|
89
|
+
if (seen.has(normalized))
|
|
90
|
+
continue;
|
|
91
|
+
if (!byId.has(normalized))
|
|
92
|
+
continue;
|
|
93
|
+
seen.add(normalized);
|
|
94
|
+
artifacts.push(normalized);
|
|
95
|
+
}
|
|
96
|
+
for (const view of byId.values()) {
|
|
97
|
+
if (view.taskId !== task.id)
|
|
98
|
+
continue;
|
|
99
|
+
if (seen.has(view.id))
|
|
100
|
+
continue;
|
|
101
|
+
seen.add(view.id);
|
|
102
|
+
artifacts.push(view.id);
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
...task,
|
|
106
|
+
artifacts,
|
|
107
|
+
blocks: (0, blocks_1.resolveBlocks)(task.blocks, ctx),
|
|
108
|
+
runs: task.runs.map((run) => ({ ...run, blocks: (0, blocks_1.resolveBlocks)(run.blocks, ctx) })),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Resolves the producer-authored blocks attached to one artifact node. */
|
|
112
|
+
function resolveArtifactBlocks(blocks, ctx) {
|
|
113
|
+
return (0, blocks_1.resolveBlocks)(blocks, ctx);
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=present.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"present.js","sourceRoot":"","sources":["../../src/presentation/present.ts"],"names":[],"mappings":";;AAqCA,0BAkDC;AAwDD,sDAKC;AAzID,qCAA4D;AAE5D,2EAA2E;AAC3E,+EAA+E;AAC/E,8EAA8E;AAC9E,iDAAiD;AAEjD,MAAM,oBAAoB,GAAyC;IACjE,QAAQ,EAAE,iDAAiD;IAC3D,OAAO,EAAE,wCAAwC;IACjD,OAAO,EAAE,oCAAoC;IAC7C,OAAO,EAAE,mCAAmC;IAC5C,IAAI,EAAE,qBAAqB;IAC3B,IAAI,EAAE,uBAAuB;IAC7B,IAAI,EAAE,eAAe;IACrB,KAAK,EAAE,oCAAoC;CAC5C,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,CAAC;AAQzB,SAAgB,OAAO,CAAC,EAAc,EAAE,UAA0B,EAAE;IAClE,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACpC,2EAA2E;IAC3E,4EAA4E;IAC5E,sBAAsB;IACtB,MAAM,GAAG,GAAiB,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;IAErD,MAAM,QAAQ,GAAkB,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACjE,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,IAAI,EAAE,OAAO,CAAC,EAAiB;QAC/B,WAAW,EAAE,oBAAoB,CAAC,OAAO,CAAC,EAAiB,CAAC;QAC5D,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC;QACvC,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,CAAC,CAAC;KACtE,CAAC,CAAC,CAAC;IAEJ,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAEzD,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;IAClE,SAAS,CAAC,IAAI,CACZ,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,CAAC,CACrF,CAAC;IAEF,MAAM,KAAK,GAAG,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAClD,mBAAmB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CACrC,CAAC;IAEF,MAAM,IAAI,GAAG,CAAC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACjD,GAAG,GAAG;QACN,MAAM,EAAE,IAAA,sBAAa,EAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC;KACvC,CAAC,CAAC,CAAC;IAEJ,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,SAAS,EAAE,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;IAElE,OAAO;QACL,QAAQ,EAAE,EAAE,CAAC,QAAQ;QACrB,GAAG,EAAE,EAAE,CAAC,GAAG;QACX,KAAK,EAAE,EAAE,CAAC,KAAK;QACf,SAAS,EAAE,EAAE,CAAC,SAAS;QACvB,gBAAgB,EAAE,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,KAAK;QACtE,UAAU,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC;QAC9C,QAAQ;QACR,KAAK;QACL,IAAI;QACJ,OAAO,EAAE,SAAS,EAAE,OAAO,IAAI,EAAE;QACjC,MAAM,EAAE,IAAA,sBAAa,EAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,EAAE;QACnD,KAAK,EAAE,EAAE;QACT,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,IAAkB;IAChC,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,MAAM;QACzB,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,MAAM;QAC/B,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,mBAAmB,CAC1B,IAAc,EACd,IAA+B,EAC/B,GAAiB;IAEjB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QAClC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC7C,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;YAAE,SAAS;QACnC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;YAAE,SAAS;QACpC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACrB,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC7B,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;QACjC,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,EAAE;YAAE,SAAS;QACtC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAAE,SAAS;QAChC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClB,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC1B,CAAC;IAED,OAAO;QACL,GAAG,IAAI;QACP,SAAS;QACT,MAAM,EAAE,IAAA,sBAAa,EAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC;QACvC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,IAAA,sBAAa,EAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;KACnF,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,SAAgB,qBAAqB,CACnC,MAA2B,EAC3B,GAAiB;IAEjB,OAAO,IAAA,sBAAa,EAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../src/providers/mock.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAiB,cAAc,EAAoB,MAAM,SAAS,CAAC;AAuH/E,wBAAgB,kBAAkB,IAAI,cAAc,CAyEnD"}
|