@invinite-org/create-chartlang 0.1.1
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/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/README.md +68 -0
- package/dist/chartlangVersions.d.ts +36 -0
- package/dist/chartlangVersions.d.ts.map +1 -0
- package/dist/chartlangVersions.js +45 -0
- package/dist/chartlangVersions.js.map +1 -0
- package/dist/createApp.d.ts +198 -0
- package/dist/createApp.d.ts.map +1 -0
- package/dist/createApp.js +392 -0
- package/dist/createApp.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/rewritePackageJson.d.ts +59 -0
- package/dist/rewritePackageJson.d.ts.map +1 -0
- package/dist/rewritePackageJson.js +128 -0
- package/dist/rewritePackageJson.js.map +1 -0
- package/dist/seamTemplates.d.ts +51 -0
- package/dist/seamTemplates.d.ts.map +1 -0
- package/dist/seamTemplates.js +318 -0
- package/dist/seamTemplates.js.map +1 -0
- package/dist/starterTsconfig.d.ts +54 -0
- package/dist/starterTsconfig.d.ts.map +1 -0
- package/dist/starterTsconfig.js +83 -0
- package/dist/starterTsconfig.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// Copyright (c) 2026 Invinite. Licensed under the MIT License.
|
|
2
|
+
// See the LICENSE file in the repo root for full license text.
|
|
3
|
+
import { CHARTLANG_VERSIONS } from "./chartlangVersions.js";
|
|
4
|
+
const WORKSPACE_RANGE = "workspace:*";
|
|
5
|
+
const CHARTLANG_SCOPE = "@invinite-org/chartlang-";
|
|
6
|
+
const EXAMPLE_ADAPTER_PREFIX = "chartlang-example-";
|
|
7
|
+
const EXAMPLE_ADAPTER_SUFFIX = "-adapter";
|
|
8
|
+
// The starter ships every adapter-matrix chart lib (echarts in deps;
|
|
9
|
+
// konva/lightweight-charts/uplot in devDeps) for its in-monorepo dev setup.
|
|
10
|
+
// A standalone clone needs only the chosen library, so all four are dropped
|
|
11
|
+
// here; the caller re-adds the chosen one at its registry range afterwards.
|
|
12
|
+
const MATRIX_CHART_LIBS = [
|
|
13
|
+
"echarts",
|
|
14
|
+
"konva",
|
|
15
|
+
"lightweight-charts",
|
|
16
|
+
"uplot",
|
|
17
|
+
];
|
|
18
|
+
function isExampleAdapterName(name) {
|
|
19
|
+
return name.startsWith(EXAMPLE_ADAPTER_PREFIX) && name.endsWith(EXAMPLE_ADAPTER_SUFFIX);
|
|
20
|
+
}
|
|
21
|
+
function resolveChartlangRange(name, bundleVersions) {
|
|
22
|
+
const fromBundle = bundleVersions[name];
|
|
23
|
+
if (fromBundle !== undefined) {
|
|
24
|
+
return fromBundle;
|
|
25
|
+
}
|
|
26
|
+
const fromManifest = CHARTLANG_VERSIONS[name];
|
|
27
|
+
if (fromManifest === undefined) {
|
|
28
|
+
throw new Error(`no published version known for "${name}" — add it to CHARTLANG_VERSIONS in create-chartlang`);
|
|
29
|
+
}
|
|
30
|
+
return fromManifest;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Rewrite ONE dependency block (deps or devDeps): replace every
|
|
34
|
+
* `@invinite-org/chartlang-*: workspace:*` with its published `^`-range, drop
|
|
35
|
+
* every `chartlang-example-*-adapter: workspace:*` (the chosen one is vendored
|
|
36
|
+
* locally), drop every matrix chart lib (the caller re-adds only the chosen
|
|
37
|
+
* one), and leave non-chartlang deps untouched.
|
|
38
|
+
*/
|
|
39
|
+
function rewriteBlock(block, bundleVersions) {
|
|
40
|
+
if (block === undefined) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
const out = {};
|
|
44
|
+
for (const [name, range] of Object.entries(block)) {
|
|
45
|
+
if (isExampleAdapterName(name)) {
|
|
46
|
+
// The chosen adapter is vendored; the other examples are not shipped.
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (MATRIX_CHART_LIBS.includes(name)) {
|
|
50
|
+
// Drop every matrix chart lib; the caller re-adds only the chosen one.
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (name.startsWith(CHARTLANG_SCOPE) && range === WORKSPACE_RANGE) {
|
|
54
|
+
out[name] = resolveChartlangRange(name, bundleVersions);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
out[name] = range;
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
// The e2e suite (Playwright + `tests/` + `playwright.config.ts`) is stripped
|
|
62
|
+
// from the clone, so its scripts + runner devDependency are dropped too —
|
|
63
|
+
// they would otherwise reference deleted files.
|
|
64
|
+
const E2E_SCRIPTS = ["e2e", "e2e:install"];
|
|
65
|
+
const E2E_DEV_DEPS = ["@playwright/test"];
|
|
66
|
+
/** Return a copy of `block` without the named keys (or `undefined` as-is). */
|
|
67
|
+
function omitKeys(block, keys) {
|
|
68
|
+
if (block === undefined) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
const out = {};
|
|
72
|
+
for (const [name, value] of Object.entries(block)) {
|
|
73
|
+
if (keys.includes(name)) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
out[name] = value;
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Rewrite the cloned starter's `package.json` for a standalone, installable
|
|
82
|
+
* project: stamp the project name, replace every `workspace:*` chartlang dep
|
|
83
|
+
* with its published `^`-range, drop the unused example-adapter deps, wire the
|
|
84
|
+
* vendored adapter as a local `file:` dep, and add the chosen chart library +
|
|
85
|
+
* range. The result contains NO `workspace:` ranges (asserted) so it resolves
|
|
86
|
+
* straight from npm.
|
|
87
|
+
*
|
|
88
|
+
* @since 0.1
|
|
89
|
+
* @stable
|
|
90
|
+
* @example
|
|
91
|
+
* import { rewriteStarterPackageJson } from "@invinite-org/create-chartlang";
|
|
92
|
+
* const next = rewriteStarterPackageJson({
|
|
93
|
+
* source: '{"name":"x","dependencies":{}}',
|
|
94
|
+
* projectName: "my-app",
|
|
95
|
+
* libraryId: "echarts",
|
|
96
|
+
* chartLibrary: "echarts",
|
|
97
|
+
* chartLibraryRange: "^5",
|
|
98
|
+
* vendoredAdapterName: "@local/echarts-adapter",
|
|
99
|
+
* vendoredAdapterSpec: "file:./vendor/echarts-adapter",
|
|
100
|
+
* bundleVersions: {},
|
|
101
|
+
* });
|
|
102
|
+
* void next;
|
|
103
|
+
*/
|
|
104
|
+
export function rewriteStarterPackageJson(opts) {
|
|
105
|
+
const parsed = JSON.parse(opts.source);
|
|
106
|
+
parsed.name = opts.projectName;
|
|
107
|
+
const deps = rewriteBlock(parsed.dependencies, opts.bundleVersions) ?? {};
|
|
108
|
+
parsed.dependencies = deps;
|
|
109
|
+
const devDeps = omitKeys(rewriteBlock(parsed.devDependencies, opts.bundleVersions), E2E_DEV_DEPS);
|
|
110
|
+
if (devDeps !== undefined) {
|
|
111
|
+
parsed.devDependencies = devDeps;
|
|
112
|
+
}
|
|
113
|
+
const scripts = omitKeys(parsed.scripts, E2E_SCRIPTS);
|
|
114
|
+
if (scripts !== undefined) {
|
|
115
|
+
parsed.scripts = scripts;
|
|
116
|
+
}
|
|
117
|
+
// Wire the vendored adapter + its chart library as runtime deps.
|
|
118
|
+
deps[opts.vendoredAdapterName] = opts.vendoredAdapterSpec;
|
|
119
|
+
if (opts.chartLibrary !== "") {
|
|
120
|
+
deps[opts.chartLibrary] = opts.chartLibraryRange;
|
|
121
|
+
}
|
|
122
|
+
const out = `${JSON.stringify(parsed, null, 4)}\n`;
|
|
123
|
+
if (out.includes(WORKSPACE_RANGE)) {
|
|
124
|
+
throw new Error("rewritten package.json still contains a workspace: dependency");
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=rewritePackageJson.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rewritePackageJson.js","sourceRoot":"","sources":["../src/rewritePackageJson.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n\nimport { CHARTLANG_VERSIONS } from \"./chartlangVersions.js\";\nimport type { SeamId } from \"./seamTemplates.js\";\n\nconst WORKSPACE_RANGE = \"workspace:*\";\nconst CHARTLANG_SCOPE = \"@invinite-org/chartlang-\";\nconst EXAMPLE_ADAPTER_PREFIX = \"chartlang-example-\";\nconst EXAMPLE_ADAPTER_SUFFIX = \"-adapter\";\n\n// The starter ships every adapter-matrix chart lib (echarts in deps;\n// konva/lightweight-charts/uplot in devDeps) for its in-monorepo dev setup.\n// A standalone clone needs only the chosen library, so all four are dropped\n// here; the caller re-adds the chosen one at its registry range afterwards.\nconst MATRIX_CHART_LIBS: ReadonlyArray<string> = [\n \"echarts\",\n \"konva\",\n \"lightweight-charts\",\n \"uplot\",\n];\n\n/**\n * Options for {@link rewriteStarterPackageJson}.\n *\n * @since 0.1\n * @stable\n * @example\n * import type { RewriteOpts } from \"@invinite-org/create-chartlang\";\n * declare const opts: RewriteOpts;\n * void opts.projectName;\n */\nexport type RewriteOpts = Readonly<{\n /** The cloned starter's raw `package.json` text. */\n source: string;\n /** The project name to stamp into the rewritten `package.json`. */\n projectName: string;\n /** The chosen chart-library adapter id. */\n libraryId: SeamId;\n /** The npm chart library the chosen adapter depends on (empty for canvas2d). */\n chartLibrary: string;\n /** The semver range for {@link chartLibrary} (from the adapter registry). */\n chartLibraryRange: string;\n /** The local package name the chosen adapter was vendored under. */\n vendoredAdapterName: string;\n /** The dependency value for the vendored adapter (e.g. `file:./vendor/echarts-adapter`). */\n vendoredAdapterSpec: string;\n /**\n * Published `^`-ranges harvested from the vendored bundle's own (already\n * generator-pinned) `package.json` deps. Preferred over the baked\n * {@link CHARTLANG_VERSIONS} manifest for the packages it covers.\n */\n bundleVersions: Readonly<Record<string, string>>;\n}>;\n\ntype DepMap = Record<string, string>;\n\nfunction isExampleAdapterName(name: string): boolean {\n return name.startsWith(EXAMPLE_ADAPTER_PREFIX) && name.endsWith(EXAMPLE_ADAPTER_SUFFIX);\n}\n\nfunction resolveChartlangRange(\n name: string,\n bundleVersions: Readonly<Record<string, string>>,\n): string {\n const fromBundle = bundleVersions[name];\n if (fromBundle !== undefined) {\n return fromBundle;\n }\n const fromManifest = CHARTLANG_VERSIONS[name];\n if (fromManifest === undefined) {\n throw new Error(\n `no published version known for \"${name}\" — add it to CHARTLANG_VERSIONS in create-chartlang`,\n );\n }\n return fromManifest;\n}\n\n/**\n * Rewrite ONE dependency block (deps or devDeps): replace every\n * `@invinite-org/chartlang-*: workspace:*` with its published `^`-range, drop\n * every `chartlang-example-*-adapter: workspace:*` (the chosen one is vendored\n * locally), drop every matrix chart lib (the caller re-adds only the chosen\n * one), and leave non-chartlang deps untouched.\n */\nfunction rewriteBlock(\n block: DepMap | undefined,\n bundleVersions: Readonly<Record<string, string>>,\n): DepMap | undefined {\n if (block === undefined) {\n return undefined;\n }\n const out: DepMap = {};\n for (const [name, range] of Object.entries(block)) {\n if (isExampleAdapterName(name)) {\n // The chosen adapter is vendored; the other examples are not shipped.\n continue;\n }\n if (MATRIX_CHART_LIBS.includes(name)) {\n // Drop every matrix chart lib; the caller re-adds only the chosen one.\n continue;\n }\n if (name.startsWith(CHARTLANG_SCOPE) && range === WORKSPACE_RANGE) {\n out[name] = resolveChartlangRange(name, bundleVersions);\n continue;\n }\n out[name] = range;\n }\n return out;\n}\n\n// The e2e suite (Playwright + `tests/` + `playwright.config.ts`) is stripped\n// from the clone, so its scripts + runner devDependency are dropped too —\n// they would otherwise reference deleted files.\nconst E2E_SCRIPTS = [\"e2e\", \"e2e:install\"] as const;\nconst E2E_DEV_DEPS = [\"@playwright/test\"] as const;\n\n/** Return a copy of `block` without the named keys (or `undefined` as-is). */\nfunction omitKeys(block: DepMap | undefined, keys: ReadonlyArray<string>): DepMap | undefined {\n if (block === undefined) {\n return undefined;\n }\n const out: DepMap = {};\n for (const [name, value] of Object.entries(block)) {\n if (keys.includes(name)) {\n continue;\n }\n out[name] = value;\n }\n return out;\n}\n\ntype PackageJson = {\n name?: string;\n scripts?: DepMap;\n dependencies?: DepMap;\n devDependencies?: DepMap;\n [key: string]: unknown;\n};\n\n/**\n * Rewrite the cloned starter's `package.json` for a standalone, installable\n * project: stamp the project name, replace every `workspace:*` chartlang dep\n * with its published `^`-range, drop the unused example-adapter deps, wire the\n * vendored adapter as a local `file:` dep, and add the chosen chart library +\n * range. The result contains NO `workspace:` ranges (asserted) so it resolves\n * straight from npm.\n *\n * @since 0.1\n * @stable\n * @example\n * import { rewriteStarterPackageJson } from \"@invinite-org/create-chartlang\";\n * const next = rewriteStarterPackageJson({\n * source: '{\"name\":\"x\",\"dependencies\":{}}',\n * projectName: \"my-app\",\n * libraryId: \"echarts\",\n * chartLibrary: \"echarts\",\n * chartLibraryRange: \"^5\",\n * vendoredAdapterName: \"@local/echarts-adapter\",\n * vendoredAdapterSpec: \"file:./vendor/echarts-adapter\",\n * bundleVersions: {},\n * });\n * void next;\n */\nexport function rewriteStarterPackageJson(opts: RewriteOpts): string {\n const parsed = JSON.parse(opts.source) as PackageJson;\n\n parsed.name = opts.projectName;\n\n const deps = rewriteBlock(parsed.dependencies, opts.bundleVersions) ?? {};\n parsed.dependencies = deps;\n const devDeps = omitKeys(\n rewriteBlock(parsed.devDependencies, opts.bundleVersions),\n E2E_DEV_DEPS,\n );\n if (devDeps !== undefined) {\n parsed.devDependencies = devDeps;\n }\n\n const scripts = omitKeys(parsed.scripts, E2E_SCRIPTS);\n if (scripts !== undefined) {\n parsed.scripts = scripts;\n }\n\n // Wire the vendored adapter + its chart library as runtime deps.\n deps[opts.vendoredAdapterName] = opts.vendoredAdapterSpec;\n if (opts.chartLibrary !== \"\") {\n deps[opts.chartLibrary] = opts.chartLibraryRange;\n }\n\n const out = `${JSON.stringify(parsed, null, 4)}\\n`;\n if (out.includes(WORKSPACE_RANGE)) {\n throw new Error(\"rewritten package.json still contains a workspace: dependency\");\n }\n return out;\n}\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The five bundled adapter ids the installer can vendor + rewrite the seam for.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.1
|
|
5
|
+
* @stable
|
|
6
|
+
* @example
|
|
7
|
+
* import { SEAM_IDS } from "@invinite-org/create-chartlang";
|
|
8
|
+
* for (const id of SEAM_IDS) void id;
|
|
9
|
+
*/
|
|
10
|
+
export declare const SEAM_IDS: readonly ["canvas2d", "lightweight-charts", "uplot", "echarts", "konva"];
|
|
11
|
+
/**
|
|
12
|
+
* A bundled-adapter id the installer can rewrite the seam for (one of
|
|
13
|
+
* {@link SEAM_IDS}).
|
|
14
|
+
*
|
|
15
|
+
* @since 0.1
|
|
16
|
+
* @stable
|
|
17
|
+
* @example
|
|
18
|
+
* import type { SeamId } from "@invinite-org/create-chartlang";
|
|
19
|
+
* const id: SeamId = "echarts";
|
|
20
|
+
* void id;
|
|
21
|
+
*/
|
|
22
|
+
export type SeamId = (typeof SEAM_IDS)[number];
|
|
23
|
+
/**
|
|
24
|
+
* Type guard: is `value` one of the five bundled-adapter ids the installer
|
|
25
|
+
* can rewrite the seam for?
|
|
26
|
+
*
|
|
27
|
+
* @since 0.1
|
|
28
|
+
* @stable
|
|
29
|
+
* @example
|
|
30
|
+
* import { isSeamId } from "@invinite-org/create-chartlang";
|
|
31
|
+
* if (isSeamId("echarts")) {
|
|
32
|
+
* // narrowed to SeamId
|
|
33
|
+
* }
|
|
34
|
+
*/
|
|
35
|
+
export declare function isSeamId(value: string): value is SeamId;
|
|
36
|
+
/**
|
|
37
|
+
* Produce the `activeAdapter.ts` body the installer writes for `id`, with the
|
|
38
|
+
* example-adapter import specifier rewritten to the vendored local adapter
|
|
39
|
+
* package name. The returned string is byte-identical to
|
|
40
|
+
* `SEAM_VARIANTS[id].seamSource` after that single substitution — the parity
|
|
41
|
+
* the matrix-proven SSOT guarantees.
|
|
42
|
+
*
|
|
43
|
+
* @since 0.1
|
|
44
|
+
* @stable
|
|
45
|
+
* @example
|
|
46
|
+
* import { seamTemplateFor } from "@invinite-org/create-chartlang";
|
|
47
|
+
* const body = seamTemplateFor("echarts", "@local/echarts-adapter");
|
|
48
|
+
* void body;
|
|
49
|
+
*/
|
|
50
|
+
export declare function seamTemplateFor(id: SeamId, adapterPkgName: string): string;
|
|
51
|
+
//# sourceMappingURL=seamTemplates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seamTemplates.d.ts","sourceRoot":"","sources":["../src/seamTemplates.ts"],"names":[],"mappings":"AAgBA;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,0EAA2E,CAAC;AAEjG;;;;;;;;;;GAUG;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AA+Q/C;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,MAAM,CAEvD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAE1E"}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
// Copyright (c) 2026 Invinite. Licensed under the MIT License.
|
|
2
|
+
// See the LICENSE file in the repo root for full license text.
|
|
3
|
+
//
|
|
4
|
+
// SEAM TEMPLATES — the installer's emit copy of the per-library
|
|
5
|
+
// `activeAdapter.ts` body. Each body MUST stay byte-identical to
|
|
6
|
+
// `apps/react-starter/src/lib/chart/seamVariants.ts`'s
|
|
7
|
+
// `SEAM_VARIANTS[id].seamSource` (the matrix-proven SSOT) AFTER the single
|
|
8
|
+
// deterministic clone-time substitution this module applies: the package
|
|
9
|
+
// name `chartlang-example-<id>-adapter` → the vendored local adapter name.
|
|
10
|
+
// `seamTemplates.test.ts` clones the real starter tree and byte-diffs every
|
|
11
|
+
// id against that SSOT, so the installer can never emit a seam the starter's
|
|
12
|
+
// `adapter-matrix.spec.ts` never rendered.
|
|
13
|
+
//
|
|
14
|
+
// Edit a library's seam in `seamVariants.ts` (the app SSOT), then mirror it
|
|
15
|
+
// here — the parity test fails the build until they agree.
|
|
16
|
+
/**
|
|
17
|
+
* The five bundled adapter ids the installer can vendor + rewrite the seam for.
|
|
18
|
+
*
|
|
19
|
+
* @since 0.1
|
|
20
|
+
* @stable
|
|
21
|
+
* @example
|
|
22
|
+
* import { SEAM_IDS } from "@invinite-org/create-chartlang";
|
|
23
|
+
* for (const id of SEAM_IDS) void id;
|
|
24
|
+
*/
|
|
25
|
+
export const SEAM_IDS = ["canvas2d", "lightweight-charts", "uplot", "echarts", "konva"];
|
|
26
|
+
const HEADER = `// Copyright (c) 2026 Invinite. Licensed under the MIT License.
|
|
27
|
+
// See the LICENSE file in the repo root for full license text.
|
|
28
|
+
//
|
|
29
|
+
// ACTIVE CHART ADAPTER — the single point the create-chartlang installer
|
|
30
|
+
// rewrites for the chosen library. Do NOT import a concrete
|
|
31
|
+
// chartlang-example-*-adapter anywhere else; the rest of the app drives
|
|
32
|
+
// charts through the abstract \`ActiveAdapterHandle\` + \`createActiveAdapter\`
|
|
33
|
+
// + \`runActiveLoop\` exported here.
|
|
34
|
+
|
|
35
|
+
import type { AlertEmission, CandleEvent } from "@invinite-org/chartlang-adapter-kit"`;
|
|
36
|
+
const OPTS_TYPES = `/** Library-agnostic options ChartPane passes to {@link createActiveAdapter}. */
|
|
37
|
+
export type CreateAdapterOpts = Readonly<{
|
|
38
|
+
container: HTMLElement
|
|
39
|
+
candleSource: AsyncIterable<CandleEvent>
|
|
40
|
+
interval?: string
|
|
41
|
+
onAlert?: (a: AlertEmission) => void
|
|
42
|
+
}>
|
|
43
|
+
|
|
44
|
+
/** Options forwarded to {@link runActiveLoop} (cancellation via \`signal\`). */
|
|
45
|
+
export type RunActiveLoopOpts = Readonly<{ signal?: AbortSignal }>`;
|
|
46
|
+
const CANVAS2D_SEAM = `${HEADER}
|
|
47
|
+
import {
|
|
48
|
+
createCanvas2dAdapter,
|
|
49
|
+
type Canvas2dAdapterHandle,
|
|
50
|
+
runRendererLoop,
|
|
51
|
+
} from "chartlang-example-canvas2d-adapter"
|
|
52
|
+
|
|
53
|
+
export type ActiveAdapterHandle = Canvas2dAdapterHandle
|
|
54
|
+
|
|
55
|
+
export const ACTIVE_ADAPTER_ID = "canvas2d"
|
|
56
|
+
|
|
57
|
+
${OPTS_TYPES}
|
|
58
|
+
|
|
59
|
+
// canvas2d paints onto a <canvas>; the seam creates one inside the generic
|
|
60
|
+
// container so ChartPane only ever provides a DOM node.
|
|
61
|
+
export function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {
|
|
62
|
+
const canvas = opts.container.ownerDocument.createElement("canvas")
|
|
63
|
+
canvas.width = opts.container.clientWidth || 800
|
|
64
|
+
canvas.height = opts.container.clientHeight || 480
|
|
65
|
+
opts.container.replaceChildren(canvas)
|
|
66
|
+
return createCanvas2dAdapter({
|
|
67
|
+
canvas,
|
|
68
|
+
candleSource: opts.candleSource,
|
|
69
|
+
...(opts.interval !== undefined ? { interval: opts.interval } : {}),
|
|
70
|
+
...(opts.onAlert !== undefined ? { onAlert: opts.onAlert } : {}),
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function runActiveLoop(
|
|
75
|
+
handle: ActiveAdapterHandle,
|
|
76
|
+
opts: RunActiveLoopOpts = {},
|
|
77
|
+
): Promise<void> {
|
|
78
|
+
await runRendererLoop(handle, opts)
|
|
79
|
+
}
|
|
80
|
+
`;
|
|
81
|
+
const LWC_SEAM = `${HEADER}
|
|
82
|
+
import { createChart } from "lightweight-charts"
|
|
83
|
+
import {
|
|
84
|
+
createLightweightChartsAdapter,
|
|
85
|
+
type CreateLightweightChartsAdapterOpts,
|
|
86
|
+
type LwcAdapterHandle,
|
|
87
|
+
runRendererLoop,
|
|
88
|
+
} from "chartlang-example-lightweight-charts-adapter"
|
|
89
|
+
|
|
90
|
+
export type ActiveAdapterHandle = LwcAdapterHandle
|
|
91
|
+
|
|
92
|
+
export const ACTIVE_ADAPTER_ID = "lightweight-charts"
|
|
93
|
+
|
|
94
|
+
${OPTS_TYPES}
|
|
95
|
+
|
|
96
|
+
// The real lightweight-charts \`IChartApi\` is wider than the slice the adapter
|
|
97
|
+
// consumes (its internal \`LwcChart\`); the two do not structurally overlap for
|
|
98
|
+
// TS, so go through \`unknown\` at the boundary.
|
|
99
|
+
const makeChart = (el: HTMLElement) =>
|
|
100
|
+
createChart(el) as unknown as ReturnType<
|
|
101
|
+
NonNullable<CreateLightweightChartsAdapterOpts["createChart"]>
|
|
102
|
+
>
|
|
103
|
+
|
|
104
|
+
export function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {
|
|
105
|
+
return createLightweightChartsAdapter({
|
|
106
|
+
container: opts.container,
|
|
107
|
+
createChart: makeChart,
|
|
108
|
+
candleSource: opts.candleSource,
|
|
109
|
+
...(opts.interval !== undefined ? { interval: opts.interval } : {}),
|
|
110
|
+
...(opts.onAlert !== undefined ? { onAlert: opts.onAlert } : {}),
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function runActiveLoop(
|
|
115
|
+
handle: ActiveAdapterHandle,
|
|
116
|
+
opts: RunActiveLoopOpts = {},
|
|
117
|
+
): Promise<void> {
|
|
118
|
+
await runRendererLoop(handle, opts)
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
const UPLOT_SEAM = `${HEADER}
|
|
122
|
+
import "uplot/dist/uPlot.min.css"
|
|
123
|
+
import {
|
|
124
|
+
createUplotAdapter,
|
|
125
|
+
type UplotAdapterHandle,
|
|
126
|
+
runUplotLoop,
|
|
127
|
+
} from "chartlang-example-uplot-adapter"
|
|
128
|
+
|
|
129
|
+
export type ActiveAdapterHandle = UplotAdapterHandle
|
|
130
|
+
|
|
131
|
+
export const ACTIVE_ADAPTER_ID = "uplot"
|
|
132
|
+
|
|
133
|
+
${OPTS_TYPES}
|
|
134
|
+
|
|
135
|
+
export function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {
|
|
136
|
+
return createUplotAdapter({
|
|
137
|
+
target: opts.container,
|
|
138
|
+
width: opts.container.clientWidth || 800,
|
|
139
|
+
height: opts.container.clientHeight || 480,
|
|
140
|
+
candleSource: opts.candleSource,
|
|
141
|
+
...(opts.interval !== undefined ? { interval: opts.interval } : {}),
|
|
142
|
+
...(opts.onAlert !== undefined ? { onAlert: opts.onAlert } : {}),
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function runActiveLoop(
|
|
147
|
+
handle: ActiveAdapterHandle,
|
|
148
|
+
opts: RunActiveLoopOpts = {},
|
|
149
|
+
): Promise<void> {
|
|
150
|
+
await runUplotLoop(handle, opts)
|
|
151
|
+
}
|
|
152
|
+
`;
|
|
153
|
+
const ECHARTS_SEAM = `${HEADER}
|
|
154
|
+
import * as echarts from "echarts"
|
|
155
|
+
import {
|
|
156
|
+
createEChartsAdapter,
|
|
157
|
+
type EChartsAdapterHandle,
|
|
158
|
+
type EChartsSurface,
|
|
159
|
+
runEChartsLoop,
|
|
160
|
+
} from "chartlang-example-echarts-adapter"
|
|
161
|
+
|
|
162
|
+
export type ActiveAdapterHandle = EChartsAdapterHandle
|
|
163
|
+
|
|
164
|
+
export const ACTIVE_ADAPTER_ID = "echarts"
|
|
165
|
+
|
|
166
|
+
${OPTS_TYPES}
|
|
167
|
+
|
|
168
|
+
// echarts needs a DOM container + sizing it does not own, so the factory —
|
|
169
|
+
// not a raw container — is the seam: this module owns the echarts.init call.
|
|
170
|
+
//
|
|
171
|
+
// The adapter's \`buildViewport\` treats \`convertToPixel\` as "returns undefined
|
|
172
|
+
// when the chart isn't laid out yet"; the real echarts instance instead THROWS
|
|
173
|
+
// before its first laid-out \`setOption\` (reading \`queryComponents\` of an
|
|
174
|
+
// undefined coordinate system). Wrap it so a pre-layout call returns undefined,
|
|
175
|
+
// honouring the adapter contract and letting it fall back to a deterministic
|
|
176
|
+
// viewport instead of killing the render loop.
|
|
177
|
+
function makeEchartsSurface(container: HTMLElement): EChartsSurface {
|
|
178
|
+
const chart = echarts.init(container)
|
|
179
|
+
return {
|
|
180
|
+
setOption: (option, setOpts) => chart.setOption(option, setOpts),
|
|
181
|
+
resize: () => chart.resize(),
|
|
182
|
+
dispose: () => chart.dispose(),
|
|
183
|
+
convertToPixel: (finder, value) => {
|
|
184
|
+
try {
|
|
185
|
+
const px = chart.convertToPixel(finder, value as unknown as number[])
|
|
186
|
+
return Array.isArray(px) ? ([px[0], px[1]] as const) : undefined
|
|
187
|
+
} catch {
|
|
188
|
+
return undefined
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {
|
|
195
|
+
return createEChartsAdapter({
|
|
196
|
+
echartsFactory: () => makeEchartsSurface(opts.container),
|
|
197
|
+
candleSource: opts.candleSource,
|
|
198
|
+
...(opts.interval !== undefined ? { interval: opts.interval } : {}),
|
|
199
|
+
...(opts.onAlert !== undefined ? { onAlert: opts.onAlert } : {}),
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export async function runActiveLoop(
|
|
204
|
+
handle: ActiveAdapterHandle,
|
|
205
|
+
opts: RunActiveLoopOpts = {},
|
|
206
|
+
): Promise<void> {
|
|
207
|
+
await runEChartsLoop(handle, opts)
|
|
208
|
+
}
|
|
209
|
+
`;
|
|
210
|
+
const KONVA_SEAM = `${HEADER}
|
|
211
|
+
import Konva from "konva"
|
|
212
|
+
import type { RunnerEmissions } from "@invinite-org/chartlang-adapter-kit"
|
|
213
|
+
import {
|
|
214
|
+
type CreateKonvaAdapterOpts,
|
|
215
|
+
createKonvaAdapter,
|
|
216
|
+
feedCandleEvent,
|
|
217
|
+
handleInterval,
|
|
218
|
+
type KonvaAdapterHandle,
|
|
219
|
+
} from "chartlang-example-konva-adapter"
|
|
220
|
+
|
|
221
|
+
export type ActiveAdapterHandle = KonvaAdapterHandle
|
|
222
|
+
|
|
223
|
+
export const ACTIVE_ADAPTER_ID = "konva"
|
|
224
|
+
|
|
225
|
+
${OPTS_TYPES}
|
|
226
|
+
|
|
227
|
+
export function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {
|
|
228
|
+
const width = opts.container.clientWidth || 800
|
|
229
|
+
const height = opts.container.clientHeight || 480
|
|
230
|
+
// The Konva adapter's StageConfig omits \`container\` (it renders headlessly
|
|
231
|
+
// and lets the caller wire the DOM node), but the real Konva.Stage requires
|
|
232
|
+
// one. Inject the seam's container by subclassing Stage so the adapter's
|
|
233
|
+
// \`new konva.Stage({ width, height })\` mounts onto our DOM node. The real
|
|
234
|
+
// Konva namespace is wider than the slice the adapter consumes, so cast it
|
|
235
|
+
// to the adapter's own \`konva\` field type.
|
|
236
|
+
const konva = {
|
|
237
|
+
...Konva,
|
|
238
|
+
Stage: class extends Konva.Stage {
|
|
239
|
+
constructor(config: { width: number; height: number }) {
|
|
240
|
+
super({ ...config, container: opts.container as HTMLDivElement })
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
} as unknown as CreateKonvaAdapterOpts["konva"]
|
|
244
|
+
return createKonvaAdapter({
|
|
245
|
+
konva,
|
|
246
|
+
stage: { width, height },
|
|
247
|
+
candleSource: opts.candleSource,
|
|
248
|
+
...(opts.interval !== undefined ? { interval: opts.interval } : {}),
|
|
249
|
+
})
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// konva has no exported async loop; mirror the shared loop shape locally over
|
|
253
|
+
// feedCandleEvent / handleInterval and forward alerts from each drain (its
|
|
254
|
+
// factory carries no onAlert hook).
|
|
255
|
+
export async function runActiveLoop(
|
|
256
|
+
handle: ActiveAdapterHandle,
|
|
257
|
+
opts: RunActiveLoopOpts = {},
|
|
258
|
+
): Promise<void> {
|
|
259
|
+
const signal = opts.signal
|
|
260
|
+
const aborted = (): boolean => signal?.aborted ?? false
|
|
261
|
+
if (aborted()) return
|
|
262
|
+
for await (const event of handle.candles({ interval: handleInterval(handle) })) {
|
|
263
|
+
if (aborted()) return
|
|
264
|
+
feedCandleEvent(handle, event)
|
|
265
|
+
await handle.host.push(event)
|
|
266
|
+
if (aborted()) return
|
|
267
|
+
await new Promise<void>((r) => setTimeout(r, 0))
|
|
268
|
+
if (aborted()) return
|
|
269
|
+
const emissions: RunnerEmissions = await handle.host.drain()
|
|
270
|
+
if (aborted()) return
|
|
271
|
+
handle.onEmissions(emissions)
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
`;
|
|
275
|
+
const SEAM_BODIES = {
|
|
276
|
+
canvas2d: CANVAS2D_SEAM,
|
|
277
|
+
"lightweight-charts": LWC_SEAM,
|
|
278
|
+
uplot: UPLOT_SEAM,
|
|
279
|
+
echarts: ECHARTS_SEAM,
|
|
280
|
+
konva: KONVA_SEAM,
|
|
281
|
+
};
|
|
282
|
+
/** The example-adapter package name for an id (the seam's import specifier). */
|
|
283
|
+
function exampleAdapterPkg(id) {
|
|
284
|
+
return `chartlang-example-${id}-adapter`;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Type guard: is `value` one of the five bundled-adapter ids the installer
|
|
288
|
+
* can rewrite the seam for?
|
|
289
|
+
*
|
|
290
|
+
* @since 0.1
|
|
291
|
+
* @stable
|
|
292
|
+
* @example
|
|
293
|
+
* import { isSeamId } from "@invinite-org/create-chartlang";
|
|
294
|
+
* if (isSeamId("echarts")) {
|
|
295
|
+
* // narrowed to SeamId
|
|
296
|
+
* }
|
|
297
|
+
*/
|
|
298
|
+
export function isSeamId(value) {
|
|
299
|
+
return SEAM_IDS.includes(value);
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Produce the `activeAdapter.ts` body the installer writes for `id`, with the
|
|
303
|
+
* example-adapter import specifier rewritten to the vendored local adapter
|
|
304
|
+
* package name. The returned string is byte-identical to
|
|
305
|
+
* `SEAM_VARIANTS[id].seamSource` after that single substitution — the parity
|
|
306
|
+
* the matrix-proven SSOT guarantees.
|
|
307
|
+
*
|
|
308
|
+
* @since 0.1
|
|
309
|
+
* @stable
|
|
310
|
+
* @example
|
|
311
|
+
* import { seamTemplateFor } from "@invinite-org/create-chartlang";
|
|
312
|
+
* const body = seamTemplateFor("echarts", "@local/echarts-adapter");
|
|
313
|
+
* void body;
|
|
314
|
+
*/
|
|
315
|
+
export function seamTemplateFor(id, adapterPkgName) {
|
|
316
|
+
return SEAM_BODIES[id].split(exampleAdapterPkg(id)).join(adapterPkgName);
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=seamTemplates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seamTemplates.js","sourceRoot":"","sources":["../src/seamTemplates.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAC/D,EAAE;AACF,gEAAgE;AAChE,iEAAiE;AACjE,uDAAuD;AACvD,2EAA2E;AAC3E,yEAAyE;AACzE,2EAA2E;AAC3E,4EAA4E;AAC5E,6EAA6E;AAC7E,2CAA2C;AAC3C,EAAE;AACF,4EAA4E;AAC5E,2DAA2D;AAE3D;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,UAAU,EAAE,oBAAoB,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAU,CAAC;AAejG,MAAM,MAAM,GAAG;;;;;;;;;sFASuE,CAAC;AAEvF,MAAM,UAAU,GAAG;;;;;;;;;mEASgD,CAAC;AAEpE,MAAM,aAAa,GAAG,GAAG,MAAM;;;;;;;;;;;EAW7B,UAAU;;;;;;;;;;;;;;;;;;;;;;;CAuBX,CAAC;AAEF,MAAM,QAAQ,GAAG,GAAG,MAAM;;;;;;;;;;;;;EAaxB,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BX,CAAC;AAEF,MAAM,UAAU,GAAG,GAAG,MAAM;;;;;;;;;;;;EAY1B,UAAU;;;;;;;;;;;;;;;;;;;CAmBX,CAAC;AAEF,MAAM,YAAY,GAAG,GAAG,MAAM;;;;;;;;;;;;;EAa5B,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2CX,CAAC;AAEF,MAAM,UAAU,GAAG,GAAG,MAAM;;;;;;;;;;;;;;;EAe1B,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiDX,CAAC;AAEF,MAAM,WAAW,GAAqC;IAClD,QAAQ,EAAE,aAAa;IACvB,oBAAoB,EAAE,QAAQ;IAC9B,KAAK,EAAE,UAAU;IACjB,OAAO,EAAE,YAAY;IACrB,KAAK,EAAE,UAAU;CACpB,CAAC;AAEF,gFAAgF;AAChF,SAAS,iBAAiB,CAAC,EAAU;IACjC,OAAO,qBAAqB,EAAE,UAAU,CAAC;AAC7C,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAa;IAClC,OAAQ,QAAkC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAAC,EAAU,EAAE,cAAsB;IAC9D,OAAO,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;AAC7E,CAAC","sourcesContent":["// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n//\n// SEAM TEMPLATES — the installer's emit copy of the per-library\n// `activeAdapter.ts` body. Each body MUST stay byte-identical to\n// `apps/react-starter/src/lib/chart/seamVariants.ts`'s\n// `SEAM_VARIANTS[id].seamSource` (the matrix-proven SSOT) AFTER the single\n// deterministic clone-time substitution this module applies: the package\n// name `chartlang-example-<id>-adapter` → the vendored local adapter name.\n// `seamTemplates.test.ts` clones the real starter tree and byte-diffs every\n// id against that SSOT, so the installer can never emit a seam the starter's\n// `adapter-matrix.spec.ts` never rendered.\n//\n// Edit a library's seam in `seamVariants.ts` (the app SSOT), then mirror it\n// here — the parity test fails the build until they agree.\n\n/**\n * The five bundled adapter ids the installer can vendor + rewrite the seam for.\n *\n * @since 0.1\n * @stable\n * @example\n * import { SEAM_IDS } from \"@invinite-org/create-chartlang\";\n * for (const id of SEAM_IDS) void id;\n */\nexport const SEAM_IDS = [\"canvas2d\", \"lightweight-charts\", \"uplot\", \"echarts\", \"konva\"] as const;\n\n/**\n * A bundled-adapter id the installer can rewrite the seam for (one of\n * {@link SEAM_IDS}).\n *\n * @since 0.1\n * @stable\n * @example\n * import type { SeamId } from \"@invinite-org/create-chartlang\";\n * const id: SeamId = \"echarts\";\n * void id;\n */\nexport type SeamId = (typeof SEAM_IDS)[number];\n\nconst HEADER = `// Copyright (c) 2026 Invinite. Licensed under the MIT License.\n// See the LICENSE file in the repo root for full license text.\n//\n// ACTIVE CHART ADAPTER — the single point the create-chartlang installer\n// rewrites for the chosen library. Do NOT import a concrete\n// chartlang-example-*-adapter anywhere else; the rest of the app drives\n// charts through the abstract \\`ActiveAdapterHandle\\` + \\`createActiveAdapter\\`\n// + \\`runActiveLoop\\` exported here.\n\nimport type { AlertEmission, CandleEvent } from \"@invinite-org/chartlang-adapter-kit\"`;\n\nconst OPTS_TYPES = `/** Library-agnostic options ChartPane passes to {@link createActiveAdapter}. */\nexport type CreateAdapterOpts = Readonly<{\n container: HTMLElement\n candleSource: AsyncIterable<CandleEvent>\n interval?: string\n onAlert?: (a: AlertEmission) => void\n}>\n\n/** Options forwarded to {@link runActiveLoop} (cancellation via \\`signal\\`). */\nexport type RunActiveLoopOpts = Readonly<{ signal?: AbortSignal }>`;\n\nconst CANVAS2D_SEAM = `${HEADER}\nimport {\n createCanvas2dAdapter,\n type Canvas2dAdapterHandle,\n runRendererLoop,\n} from \"chartlang-example-canvas2d-adapter\"\n\nexport type ActiveAdapterHandle = Canvas2dAdapterHandle\n\nexport const ACTIVE_ADAPTER_ID = \"canvas2d\"\n\n${OPTS_TYPES}\n\n// canvas2d paints onto a <canvas>; the seam creates one inside the generic\n// container so ChartPane only ever provides a DOM node.\nexport function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {\n const canvas = opts.container.ownerDocument.createElement(\"canvas\")\n canvas.width = opts.container.clientWidth || 800\n canvas.height = opts.container.clientHeight || 480\n opts.container.replaceChildren(canvas)\n return createCanvas2dAdapter({\n canvas,\n candleSource: opts.candleSource,\n ...(opts.interval !== undefined ? { interval: opts.interval } : {}),\n ...(opts.onAlert !== undefined ? { onAlert: opts.onAlert } : {}),\n })\n}\n\nexport async function runActiveLoop(\n handle: ActiveAdapterHandle,\n opts: RunActiveLoopOpts = {},\n): Promise<void> {\n await runRendererLoop(handle, opts)\n}\n`;\n\nconst LWC_SEAM = `${HEADER}\nimport { createChart } from \"lightweight-charts\"\nimport {\n createLightweightChartsAdapter,\n type CreateLightweightChartsAdapterOpts,\n type LwcAdapterHandle,\n runRendererLoop,\n} from \"chartlang-example-lightweight-charts-adapter\"\n\nexport type ActiveAdapterHandle = LwcAdapterHandle\n\nexport const ACTIVE_ADAPTER_ID = \"lightweight-charts\"\n\n${OPTS_TYPES}\n\n// The real lightweight-charts \\`IChartApi\\` is wider than the slice the adapter\n// consumes (its internal \\`LwcChart\\`); the two do not structurally overlap for\n// TS, so go through \\`unknown\\` at the boundary.\nconst makeChart = (el: HTMLElement) =>\n createChart(el) as unknown as ReturnType<\n NonNullable<CreateLightweightChartsAdapterOpts[\"createChart\"]>\n >\n\nexport function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {\n return createLightweightChartsAdapter({\n container: opts.container,\n createChart: makeChart,\n candleSource: opts.candleSource,\n ...(opts.interval !== undefined ? { interval: opts.interval } : {}),\n ...(opts.onAlert !== undefined ? { onAlert: opts.onAlert } : {}),\n })\n}\n\nexport async function runActiveLoop(\n handle: ActiveAdapterHandle,\n opts: RunActiveLoopOpts = {},\n): Promise<void> {\n await runRendererLoop(handle, opts)\n}\n`;\n\nconst UPLOT_SEAM = `${HEADER}\nimport \"uplot/dist/uPlot.min.css\"\nimport {\n createUplotAdapter,\n type UplotAdapterHandle,\n runUplotLoop,\n} from \"chartlang-example-uplot-adapter\"\n\nexport type ActiveAdapterHandle = UplotAdapterHandle\n\nexport const ACTIVE_ADAPTER_ID = \"uplot\"\n\n${OPTS_TYPES}\n\nexport function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {\n return createUplotAdapter({\n target: opts.container,\n width: opts.container.clientWidth || 800,\n height: opts.container.clientHeight || 480,\n candleSource: opts.candleSource,\n ...(opts.interval !== undefined ? { interval: opts.interval } : {}),\n ...(opts.onAlert !== undefined ? { onAlert: opts.onAlert } : {}),\n })\n}\n\nexport async function runActiveLoop(\n handle: ActiveAdapterHandle,\n opts: RunActiveLoopOpts = {},\n): Promise<void> {\n await runUplotLoop(handle, opts)\n}\n`;\n\nconst ECHARTS_SEAM = `${HEADER}\nimport * as echarts from \"echarts\"\nimport {\n createEChartsAdapter,\n type EChartsAdapterHandle,\n type EChartsSurface,\n runEChartsLoop,\n} from \"chartlang-example-echarts-adapter\"\n\nexport type ActiveAdapterHandle = EChartsAdapterHandle\n\nexport const ACTIVE_ADAPTER_ID = \"echarts\"\n\n${OPTS_TYPES}\n\n// echarts needs a DOM container + sizing it does not own, so the factory —\n// not a raw container — is the seam: this module owns the echarts.init call.\n//\n// The adapter's \\`buildViewport\\` treats \\`convertToPixel\\` as \"returns undefined\n// when the chart isn't laid out yet\"; the real echarts instance instead THROWS\n// before its first laid-out \\`setOption\\` (reading \\`queryComponents\\` of an\n// undefined coordinate system). Wrap it so a pre-layout call returns undefined,\n// honouring the adapter contract and letting it fall back to a deterministic\n// viewport instead of killing the render loop.\nfunction makeEchartsSurface(container: HTMLElement): EChartsSurface {\n const chart = echarts.init(container)\n return {\n setOption: (option, setOpts) => chart.setOption(option, setOpts),\n resize: () => chart.resize(),\n dispose: () => chart.dispose(),\n convertToPixel: (finder, value) => {\n try {\n const px = chart.convertToPixel(finder, value as unknown as number[])\n return Array.isArray(px) ? ([px[0], px[1]] as const) : undefined\n } catch {\n return undefined\n }\n },\n }\n}\n\nexport function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {\n return createEChartsAdapter({\n echartsFactory: () => makeEchartsSurface(opts.container),\n candleSource: opts.candleSource,\n ...(opts.interval !== undefined ? { interval: opts.interval } : {}),\n ...(opts.onAlert !== undefined ? { onAlert: opts.onAlert } : {}),\n })\n}\n\nexport async function runActiveLoop(\n handle: ActiveAdapterHandle,\n opts: RunActiveLoopOpts = {},\n): Promise<void> {\n await runEChartsLoop(handle, opts)\n}\n`;\n\nconst KONVA_SEAM = `${HEADER}\nimport Konva from \"konva\"\nimport type { RunnerEmissions } from \"@invinite-org/chartlang-adapter-kit\"\nimport {\n type CreateKonvaAdapterOpts,\n createKonvaAdapter,\n feedCandleEvent,\n handleInterval,\n type KonvaAdapterHandle,\n} from \"chartlang-example-konva-adapter\"\n\nexport type ActiveAdapterHandle = KonvaAdapterHandle\n\nexport const ACTIVE_ADAPTER_ID = \"konva\"\n\n${OPTS_TYPES}\n\nexport function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {\n const width = opts.container.clientWidth || 800\n const height = opts.container.clientHeight || 480\n // The Konva adapter's StageConfig omits \\`container\\` (it renders headlessly\n // and lets the caller wire the DOM node), but the real Konva.Stage requires\n // one. Inject the seam's container by subclassing Stage so the adapter's\n // \\`new konva.Stage({ width, height })\\` mounts onto our DOM node. The real\n // Konva namespace is wider than the slice the adapter consumes, so cast it\n // to the adapter's own \\`konva\\` field type.\n const konva = {\n ...Konva,\n Stage: class extends Konva.Stage {\n constructor(config: { width: number; height: number }) {\n super({ ...config, container: opts.container as HTMLDivElement })\n }\n },\n } as unknown as CreateKonvaAdapterOpts[\"konva\"]\n return createKonvaAdapter({\n konva,\n stage: { width, height },\n candleSource: opts.candleSource,\n ...(opts.interval !== undefined ? { interval: opts.interval } : {}),\n })\n}\n\n// konva has no exported async loop; mirror the shared loop shape locally over\n// feedCandleEvent / handleInterval and forward alerts from each drain (its\n// factory carries no onAlert hook).\nexport async function runActiveLoop(\n handle: ActiveAdapterHandle,\n opts: RunActiveLoopOpts = {},\n): Promise<void> {\n const signal = opts.signal\n const aborted = (): boolean => signal?.aborted ?? false\n if (aborted()) return\n for await (const event of handle.candles({ interval: handleInterval(handle) })) {\n if (aborted()) return\n feedCandleEvent(handle, event)\n await handle.host.push(event)\n if (aborted()) return\n await new Promise<void>((r) => setTimeout(r, 0))\n if (aborted()) return\n const emissions: RunnerEmissions = await handle.host.drain()\n if (aborted()) return\n handle.onEmissions(emissions)\n }\n}\n`;\n\nconst SEAM_BODIES: Readonly<Record<SeamId, string>> = {\n canvas2d: CANVAS2D_SEAM,\n \"lightweight-charts\": LWC_SEAM,\n uplot: UPLOT_SEAM,\n echarts: ECHARTS_SEAM,\n konva: KONVA_SEAM,\n};\n\n/** The example-adapter package name for an id (the seam's import specifier). */\nfunction exampleAdapterPkg(id: SeamId): string {\n return `chartlang-example-${id}-adapter`;\n}\n\n/**\n * Type guard: is `value` one of the five bundled-adapter ids the installer\n * can rewrite the seam for?\n *\n * @since 0.1\n * @stable\n * @example\n * import { isSeamId } from \"@invinite-org/create-chartlang\";\n * if (isSeamId(\"echarts\")) {\n * // narrowed to SeamId\n * }\n */\nexport function isSeamId(value: string): value is SeamId {\n return (SEAM_IDS as ReadonlyArray<string>).includes(value);\n}\n\n/**\n * Produce the `activeAdapter.ts` body the installer writes for `id`, with the\n * example-adapter import specifier rewritten to the vendored local adapter\n * package name. The returned string is byte-identical to\n * `SEAM_VARIANTS[id].seamSource` after that single substitution — the parity\n * the matrix-proven SSOT guarantees.\n *\n * @since 0.1\n * @stable\n * @example\n * import { seamTemplateFor } from \"@invinite-org/create-chartlang\";\n * const body = seamTemplateFor(\"echarts\", \"@local/echarts-adapter\");\n * void body;\n */\nexport function seamTemplateFor(id: SeamId, adapterPkgName: string): string {\n return SEAM_BODIES[id].split(exampleAdapterPkg(id)).join(adapterPkgName);\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The monorepo `tsconfig.base.json` compiler options, baked verbatim so the
|
|
3
|
+
* standalone clone has a self-contained base to `extends`. Kept in sync with
|
|
4
|
+
* the repo-root file by the parity test.
|
|
5
|
+
*
|
|
6
|
+
* @since 0.1
|
|
7
|
+
* @stable
|
|
8
|
+
* @example
|
|
9
|
+
* import { STANDALONE_TSCONFIG_BASE } from "@invinite-org/create-chartlang";
|
|
10
|
+
* void STANDALONE_TSCONFIG_BASE.compilerOptions.strict;
|
|
11
|
+
*/
|
|
12
|
+
export declare const STANDALONE_TSCONFIG_BASE: {
|
|
13
|
+
readonly compilerOptions: {
|
|
14
|
+
readonly target: "ES2022";
|
|
15
|
+
readonly module: "NodeNext";
|
|
16
|
+
readonly moduleResolution: "NodeNext";
|
|
17
|
+
readonly lib: readonly ["ES2022", "DOM", "DOM.Iterable"];
|
|
18
|
+
readonly strict: true;
|
|
19
|
+
readonly noImplicitAny: true;
|
|
20
|
+
readonly noImplicitOverride: true;
|
|
21
|
+
readonly noImplicitReturns: true;
|
|
22
|
+
readonly noUnusedLocals: true;
|
|
23
|
+
readonly noUnusedParameters: true;
|
|
24
|
+
readonly noFallthroughCasesInSwitch: true;
|
|
25
|
+
readonly exactOptionalPropertyTypes: true;
|
|
26
|
+
readonly isolatedModules: true;
|
|
27
|
+
readonly esModuleInterop: true;
|
|
28
|
+
readonly forceConsistentCasingInFileNames: true;
|
|
29
|
+
readonly skipLibCheck: true;
|
|
30
|
+
readonly declaration: true;
|
|
31
|
+
readonly declarationMap: true;
|
|
32
|
+
readonly sourceMap: true;
|
|
33
|
+
readonly inlineSources: true;
|
|
34
|
+
readonly verbatimModuleSyntax: true;
|
|
35
|
+
readonly resolveJsonModule: true;
|
|
36
|
+
readonly noEmit: false;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Make the cloned starter's TypeScript config self-contained: write
|
|
41
|
+
* `<dir>/tsconfig.base.json` from {@link STANDALONE_TSCONFIG_BASE}, then repoint
|
|
42
|
+
* the cloned `<dir>/tsconfig.json` `extends` to the sibling
|
|
43
|
+
* `"./tsconfig.base.json"`. If the clone has no `tsconfig.json`, the base is
|
|
44
|
+
* still written and the repoint is skipped (so the vendored adapter's own
|
|
45
|
+
* `extends: "../../tsconfig.base.json"` still resolves to the clone-root base).
|
|
46
|
+
*
|
|
47
|
+
* @since 0.1
|
|
48
|
+
* @stable
|
|
49
|
+
* @example
|
|
50
|
+
* import { writeStandaloneTsconfig } from "@invinite-org/create-chartlang";
|
|
51
|
+
* await writeStandaloneTsconfig("/tmp/my-app");
|
|
52
|
+
*/
|
|
53
|
+
export declare function writeStandaloneTsconfig(dir: string): Promise<void>;
|
|
54
|
+
//# sourceMappingURL=starterTsconfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starterTsconfig.d.ts","sourceRoot":"","sources":["../src/starterTsconfig.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;CA0B3B,CAAC;AAOX;;;;;;;;;;;;;GAaG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAcxE"}
|