@invinite-org/create-chartlang 0.1.1 → 0.1.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # create-chartlang
2
2
 
3
+ ## 0.1.2
4
+
5
+ ### Patch Changes
6
+
7
+ - ab8b218: Change the default chart library from `echarts` to `canvas2d`. When you run
8
+ `npm create @invinite-org/chartlang@latest` without `--library` (or accept the
9
+ prompt default / pass `--yes`), the installer now scaffolds the dependency-free
10
+ `canvas2d` adapter instead of `echarts`. The other four libraries
11
+ (`echarts`, `lightweight-charts`, `uplot`, `konva`) are still available via the
12
+ prompt or `--library <id>`.
13
+ - a66b28d: Fix the lightweight-charts starter rendering blank ("Assertion failed" in
14
+ lightweight-charts 5.x `addSeriesImpl`). The lightweight-charts seam overrode
15
+ the adapter's `createChart` with the raw `IChartApi` (force-cast through
16
+ `unknown`), bypassing the adapter's internal wrapper that maps its string-keyed
17
+ `addSeries("Candlestick" | "Line", …)` calls onto v5's series-definition API.
18
+ The seam now omits the override so the adapter uses its own (correct) default
19
+ `createChart`, matching how the site demo driver mounts it. The chosen library
20
+ is still installed (the adapter imports `lightweight-charts` internally).
21
+ - Updated dependencies [08cba38]
22
+ - Updated dependencies [1efb49c]
23
+ - Updated dependencies [1efb49c]
24
+ - @invinite-org/chartlang-cli@1.3.1
25
+
3
26
  ## 0.1.1
4
27
 
5
28
  ### Patch Changes
package/README.md CHANGED
@@ -3,8 +3,8 @@
3
3
  > **Stability: experimental.**
4
4
 
5
5
  The `npm create @invinite-org/chartlang` installer — scaffold a runnable chartlang starter
6
- app (TanStack Start editor + live chart + EODData + SQLite) with your chosen
7
- chart library wired in.
6
+ app (TanStack Start editor + live chart + Yahoo Finance market data + SQLite)
7
+ with your chosen chart library wired in. Market data needs no API key.
8
8
 
9
9
  ## Usage
10
10
 
@@ -110,7 +110,7 @@ export type ResolvedAdapter = Readonly<{
110
110
  */
111
111
  export declare function resolveAdapter(id: string): ResolvedAdapter;
112
112
  /**
113
- * Render the library-choice prompt list from the registry, echarts first
113
+ * Render the library-choice prompt list from the registry, canvas2d first
114
114
  * (the default) then the rest in registry order. Pure: no IO.
115
115
  *
116
116
  * @since 0.1
@@ -1 +1 @@
1
- {"version":3,"file":"createApp.d.ts","sourceRoot":"","sources":["../src/createApp.ts"],"names":[],"mappings":"AAQA,OAAO,EAGH,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EAC5B,MAAM,6BAA6B,CAAC;AASrC,QAAA,MAAM,gBAAgB,yCAA0C,CAAC;AACjE,KAAK,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAsBxD;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,qDAAqD,CAAC;AAEtF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,QAAQ,GAAG;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,KAAK,IAAI,IAAI,CAAC;CACjB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAChC,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAEH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEhE;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACvC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,MAAM,QAAQ,CAAC;IAC/B,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,EAAE,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE,CAAC,CAAC;AAMH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACnC,MAAM,EAAE,sBAAsB,CAAC;IAC/B,IAAI,EAAE,oBAAoB,CAAC;CAC9B,CAAC,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,CAO1D;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,aAAa,CAAC,oBAAoB,CAAC,GAAG,MAAM,CAU1F;AAUD;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACnC,MAAM,EAAE,sBAAsB,GAC/B,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAgBlC;AA2BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAoBtF;AA8ID;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CACvB,EAAE,EAAE,QAAQ,CAAC;IAAE,YAAY,EAAE,YAAY,CAAC;IAAC,UAAU,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAA;CAAE,CAAC,GAC5F,mBAAmB,CASrB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,kBAAkB,CACpC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,IAAI,EAAE,mBAAmB,GAC1B,OAAO,CAAC,IAAI,CAAC,CA6Ef"}
1
+ {"version":3,"file":"createApp.d.ts","sourceRoot":"","sources":["../src/createApp.ts"],"names":[],"mappings":"AAQA,OAAO,EAGH,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EAC5B,MAAM,6BAA6B,CAAC;AASrC,QAAA,MAAM,gBAAgB,yCAA0C,CAAC;AACjE,KAAK,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAsBxD;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,qDAAqD,CAAC;AAEtF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,QAAQ,GAAG;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,KAAK,IAAI,IAAI,CAAC;CACjB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAChC,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAEH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEhE;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IACvC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,MAAM,QAAQ,CAAC;IAC/B,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,EAAE,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE,CAAC,CAAC;AAMH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACnC,MAAM,EAAE,sBAAsB,CAAC;IAC/B,IAAI,EAAE,oBAAoB,CAAC;CAC9B,CAAC,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,eAAe,CAO1D;AAUD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,aAAa,CAAC,oBAAoB,CAAC,GAAG,MAAM,CAU1F;AAUD;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACnC,MAAM,EAAE,sBAAsB,GAC/B,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAgBlC;AA2BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAoBtF;AAiJD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CACvB,EAAE,EAAE,QAAQ,CAAC;IAAE,YAAY,EAAE,YAAY,CAAC;IAAC,UAAU,EAAE,mBAAmB,CAAC,YAAY,CAAC,CAAA;CAAE,CAAC,GAC5F,mBAAmB,CASrB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,kBAAkB,CACpC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,IAAI,EAAE,mBAAmB,GAC1B,OAAO,CAAC,IAAI,CAAC,CA6Ef"}
package/dist/createApp.js CHANGED
@@ -19,7 +19,7 @@ const NPMRC_FILE = ".npmrc";
19
19
  // so the clone opts into `legacy-peer-deps` instead of bumping any esbuild.
20
20
  const NPMRC_CONTENTS = "# vite@8's optional esbuild peer (^0.27||^0.28) conflicts with the\n# chartlang-compiler esbuild dep (^0.24); the app runs fine on 0.24.\nlegacy-peer-deps=true\n";
21
21
  const DEFAULT_TARGET = "./chartlang-starter";
22
- const DEFAULT_LIBRARY = "echarts";
22
+ const DEFAULT_LIBRARY = "canvas2d";
23
23
  const SEAM_PATH = ["src", "lib", "chart", "activeAdapter.ts"];
24
24
  const ENV_EXAMPLE = ".env.example";
25
25
  const ENV_FILE = ".env";
@@ -73,7 +73,7 @@ async function isNonEmptyDir(path) {
73
73
  }
74
74
  }
75
75
  /**
76
- * Render the library-choice prompt list from the registry, echarts first
76
+ * Render the library-choice prompt list from the registry, canvas2d first
77
77
  * (the default) then the rest in registry order. Pure: no IO.
78
78
  *
79
79
  * @since 0.1
@@ -194,12 +194,15 @@ async function stripRepoArtefacts(dir) {
194
194
  }
195
195
  function defaultEnv() {
196
196
  return [
197
- "# chartlang starter environment. `.env` is git-ignored.",
197
+ "# Copy to `.env` and fill in. `.env` is git-ignored; this sample is committed.",
198
+ "",
199
+ "# Local SQLite file (saved scripts + cached daily bars). The DB auto-creates,",
200
+ "# migrates, and seeds on first boot — no manual step.",
198
201
  "DATABASE_URL=file:./data/starter.db",
199
202
  "",
200
- "# EODData API key free tier: 100 calls/day, daily EOD, US symbols.",
201
- "# Register at https://eoddata.com/myaccount/api.aspx",
202
- "EODDATA_API_KEY=",
203
+ "# Market data needs NO API key: the starter loads daily US bars from Yahoo",
204
+ "# Finance (free, unmetered) and caches each symbol in SQLite, so a re-open /",
205
+ "# re-compile costs zero network calls.",
203
206
  "",
204
207
  ].join("\n");
205
208
  }
@@ -222,7 +225,7 @@ function renderNextSteps(dir, pm, installed) {
222
225
  if (!installed) {
223
226
  lines.push(` ${pm} install`);
224
227
  }
225
- lines.push(" # add your free EODData key to .env (EODDATA_API_KEY=)", ` ${pm} run dev`, "", "Switch chart libraries later:", " npx @invinite-org/chartlang-cli add-adapter <id>", " # then edit src/lib/chart/activeAdapter.ts", "");
228
+ lines.push(" # market data comes from Yahoo Finance no API key needed", ` ${pm} run dev`, "", "Switch chart libraries later:", " npx @invinite-org/chartlang-cli add-adapter <id>", " # then edit src/lib/chart/activeAdapter.ts", "");
226
229
  return lines.join("\n");
227
230
  }
228
231
  function parse(argv) {
@@ -1 +1 @@
1
- {"version":3,"file":"createApp.js","sourceRoot":"","sources":["../src/createApp.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,+DAA+D;AAE/D,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,WAAW,CAAC;AACxF,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EACH,gBAAgB,EAChB,gBAAgB,GAGnB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAe,QAAQ,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,MAAM,oBAAoB,GAAG,cAAc,CAAC;AAC5C,MAAM,eAAe,GAAG,0BAA0B,CAAC;AACnD,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAU,CAAC;AAGjE,MAAM,UAAU,GAAG,QAAQ,CAAC;AAC5B,+EAA+E;AAC/E,iFAAiF;AACjF,gFAAgF;AAChF,4EAA4E;AAC5E,MAAM,cAAc,GAChB,mKAAmK,CAAC;AAExK,MAAM,cAAc,GAAG,qBAAqB,CAAC;AAC7C,MAAM,eAAe,GAAW,SAAS,CAAC;AAC1C,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC;AAC9D,MAAM,WAAW,GAAG,cAAc,CAAC;AACnC,MAAM,QAAQ,GAAG,MAAM,CAAC;AAExB,0EAA0E;AAC1E,2EAA2E;AAC3E,uEAAuE;AACvE,oEAAoE;AACpE,MAAM,aAAa,GAAG,CAAC,WAAW,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,sBAAsB,CAAC,CAAC;AAE9F;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,kDAAkD,CAAC;AAyEtF,SAAS,gBAAgB,CAAC,KAAa;IACnC,OAAQ,gBAA0C,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACvE,CAAC;AAkBD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU;IACrC,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACvD,IAAI,MAAM,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,yCAAyC,EAAE,GAAG,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC5B,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,IAAY;IACrC,IAAI,CAAC;QACD,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAA6C;IAC9E,MAAM,OAAO,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,CAAC,yBAAyB,EAAE,EAAE,CAAC,CAAC;IAC9C,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,KAAK,eAAe,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;QACxE,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,EAAE,MAAM,IAAI,CAAC,WAAW,KAAK,GAAG,IAAI,MAAM,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,eAAe,CACpB,QAA6C;IAE7C,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,eAAe,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,eAAe,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,uBAAuB,CACnC,MAA8B;IAE9B,wEAAwE;IACxE,yEAAyE;IACzE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,IAAI,CAG7D,CAAC;IACF,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC;QAChE,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;YACtD,IAAI,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;gBACnC,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;YACtB,CAAC;QACL,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,KAAa;IACnC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,KAAK;SACP,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC;SAChC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC;SAC1B,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AACjC,CAAC;AAUD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,0BAA0B,CAAC,QAAgB,EAAE,SAAiB;IAC1E,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACnE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAuB,CAAC;IACpD,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACzB,GAAG,CAAC,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1B,GAAG,CAAC,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC5C,CAAC;IACD,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC5B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7C,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC5B,KAAK,CAAC,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAChD,CAAC;YACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC7B,KAAK,CAAC,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAClD,CAAC;QACL,CAAC;IACL,CAAC;IACD,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;AAC/C,CAAC;AAED,yFAAyF;AACzF,KAAK,UAAU,oBAAoB,CAC/B,MAA8B,EAC9B,GAAW,EACX,SAAiB;IAEjB,KAAK,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9C,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,MAAM,GAAG,GACL,OAAO,KAAK,cAAc,CAAC,CAAC,CAAC,0BAA0B,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC5F,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,GAAW;IACzC,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;QAChC,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;AACL,CAAC;AAED,SAAS,UAAU;IACf,OAAO;QACH,yDAAyD;QACzD,qCAAqC;QACrC,EAAE;QACF,sEAAsE;QACtE,sDAAsD;QACtD,kBAAkB;QAClB,EAAE;KACL,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,GAAW;IACjC,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC;AACnE,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,GAAW;IAC/B,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAC3C,IAAI,QAAQ,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,CAAC;QACD,QAAQ,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACL,6EAA6E;IACjF,CAAC;IACD,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,eAAe,CAAC,GAAW,EAAE,EAAkB,EAAE,SAAkB;IACxE,MAAM,KAAK,GAAG,CAAC,gCAAgC,GAAG,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,QAAQ,GAAG,EAAE,CAAC,CAAC;IACxF,IAAI,CAAC,SAAS,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAClC,CAAC;IACD,KAAK,CAAC,IAAI,CACN,0DAA0D,EAC1D,KAAK,EAAE,UAAU,EACjB,EAAE,EACF,+BAA+B,EAC/B,oDAAoD,EACpD,8CAA8C,EAC9C,EAAE,CACL,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAUD,SAAS,KAAK,CAAC,IAA2B;IACtC,2EAA2E;IAC3E,yEAAyE;IACzE,kEAAkE;IAClE,MAAM,MAAM,GAAG,SAAS,CAAC;QACrB,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE;QAClB,OAAO,EAAE;YACL,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC3B,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACtB,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;YACjD,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;YACxC,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;SAC7C;QACD,gBAAgB,EAAE,IAAI;QACtB,MAAM,EAAE,IAAI;KACf,CAAC,CAAC;IACH,OAAO;QACH,GAAG,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,cAAc;QAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO;QAC9B,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,KAAK;QAC7B,OAAO,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;QACrC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK;KAChD,CAAC;AACN,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,cAAc,CACzB,QAA4B,EAC5B,GAAY,EACZ,IAAyB;IAEzB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QACzB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,CAAC,KAAK,CACb,6BAA6B,QAAQ,kBAAkB,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAC1G,CAAC;YACF,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,OAAO,QAAQ,CAAC;IACpB,CAAC;IACD,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,eAAe,CAAC;IAC3B,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;IACvC,IAAI,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,gBAAgB,CAAC,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,QAAQ,CAAC,YAAY,eAAe,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAClF,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;YAChB,OAAO,eAAe,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACpB,IAAI,CAAC,MAAM,CAAC,KAAK,CACb,2BAA2B,MAAM,kBAAkB,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CACtG,CAAC;YACF,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;YAAS,CAAC;QACP,QAAQ,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,WAAW,CACvB,EAA2F;IAE3F,OAAO;QACH,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI;QACnC,cAAc,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QACvF,YAAY,EAAE,EAAE,CAAC,YAAY;QAC7B,UAAU,EAAE,EAAE,CAAC,UAAU;KAC5B,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACpC,IAA2B,EAC3B,IAAyB;IAEzB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAEzB,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,KAAK,CACb,wBAAwB,IAAI,CAAC,EAAE,uBAAuB,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CACxF,CAAC;QACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACX,CAAC;IACD,MAAM,EAAE,GAAmB,IAAI,CAAC,EAAE,CAAC;IAEnC,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACzF,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,aAAa,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;QAChD,IAAI,CAAC,MAAM,CAAC,KAAK,CACb,sCAAsC,SAAS,6BAA6B,CAC/E,CAAC;QACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACX,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACnE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACX,CAAC;IAED,uEAAuE;IACvE,uDAAuD;IACvD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAEjD,MAAM,IAAI,CAAC,YAAY,CAAC;QACpB,MAAM,EAAE,GAAG,mBAAmB,GAAG,iBAAiB,EAAE;QACpD,GAAG,EAAE,SAAS;KACjB,CAAC,CAAC;IACH,MAAM,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAEpC,uEAAuE;IACvE,yEAAyE;IACzE,wCAAwC;IACxC,MAAM,uBAAuB,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC;IAE5B,MAAM,SAAS,GAAG,UAAU,OAAO,UAAU,CAAC;IAC9C,MAAM,mBAAmB,GAAG,UAAU,OAAO,UAAU,CAAC;IACxD,MAAM,oBAAoB,CACtB,MAAM,EACN,IAAI,CAAC,SAAS,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EACxC,mBAAmB,CACtB,CAAC;IAEF,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,SAAS,CAAC,CAAC;IAC/C,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAE5C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,yBAAyB,CAAC;QACxC,MAAM,EAAE,SAAS;QACjB,WAAW,EAAE,QAAQ,CAAC,SAAS,CAAC;QAChC,SAAS,EAAE,OAAO;QAClB,YAAY,EAAE,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO;QAC3D,iBAAiB,EAAE,IAAI,CAAC,YAAY;QACpC,mBAAmB;QACnB,mBAAmB,EAAE,UAAU,SAAS,EAAE;QAC1C,cAAc,EAAE,uBAAuB,CAAC,MAAM,CAAC;KAClD,CAAC,CAAC;IACH,MAAM,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAE5C,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAC;IAE1B,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,SAAS,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;AACpE,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\nimport { mkdir, readFile, readdir, rm, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, join, resolve as resolvePath } from \"node:path\";\nimport { createInterface } from \"node:readline/promises\";\nimport { parseArgs } from \"node:util\";\n\nimport {\n ADAPTER_REGISTRY,\n BUNDLED_ADAPTERS,\n type GeneratedAdapterBundle,\n type GeneratedAdapterMeta,\n} from \"@invinite-org/chartlang-cli\";\n\nimport { STARTER_CLONE_REF } from \"./chartlangVersions.js\";\nimport { rewriteStarterPackageJson } from \"./rewritePackageJson.js\";\nimport { type SeamId, isSeamId, seamTemplateFor } from \"./seamTemplates.js\";\nimport { writeStandaloneTsconfig } from \"./starterTsconfig.js\";\n\nconst PKG_NAME_PLACEHOLDER = \"__PKG_NAME__\";\nconst CHARTLANG_SCOPE = \"@invinite-org/chartlang-\";\nconst PACKAGE_MANAGERS = [\"npm\", \"pnpm\", \"yarn\", \"bun\"] as const;\ntype PackageManager = (typeof PACKAGE_MANAGERS)[number];\n\nconst NPMRC_FILE = \".npmrc\";\n// Published `@invinite-org/chartlang-compiler` depends on `esbuild@^0.24`, but\n// `vite@8`'s optional peer wants `esbuild@^0.27 || ^0.28`. The app builds + runs\n// fine on esbuild 0.24 — npm's strict optional-peer check is the only blocker —\n// so the clone opts into `legacy-peer-deps` instead of bumping any esbuild.\nconst NPMRC_CONTENTS =\n \"# vite@8's optional esbuild peer (^0.27||^0.28) conflicts with the\\n# chartlang-compiler esbuild dep (^0.24); the app runs fine on 0.24.\\nlegacy-peer-deps=true\\n\";\n\nconst DEFAULT_TARGET = \"./chartlang-starter\";\nconst DEFAULT_LIBRARY: SeamId = \"echarts\";\nconst SEAM_PATH = [\"src\", \"lib\", \"chart\", \"activeAdapter.ts\"];\nconst ENV_EXAMPLE = \".env.example\";\nconst ENV_FILE = \".env\";\n\n// Repo-internal artefacts that must not ship to the user's clone. The e2e\n// suite lives under `tests/`; `playwright.config.ts` references it (a mock\n// server + global setup), so it is stripped too — otherwise the cloned\n// project ships a `playwright.config.ts` pointing at deleted files.\nconst STRIP_ENTRIES = [\"CLAUDE.md\", \"tests\", \".changeset\", \".github\", \"playwright.config.ts\"];\n\n/**\n * The starter source giget clones, minus the ref suffix. The installer\n * appends {@link STARTER_CLONE_REF} to pin the matching tagged tree.\n *\n * @since 0.1\n * @stable\n * @example\n * import { STARTER_SOURCE_BASE } from \"@invinite-org/create-chartlang\";\n * void STARTER_SOURCE_BASE;\n */\nexport const STARTER_SOURCE_BASE = \"github:outraday-org/chartlang/apps/react-starter\";\n\n/**\n * A minimal `node:readline/promises`-like surface — the subset the\n * interactive library prompt needs. Lets a test inject a fixed answer\n * instead of driving the real TTY. Mirrors the CLI's `Prompter`.\n *\n * @since 0.1\n * @stable\n * @example\n * import type { Prompter } from \"@invinite-org/create-chartlang\";\n * const p: Prompter = { question: async () => \"echarts\", close: () => {} };\n * void p;\n */\nexport type Prompter = {\n question(query: string): Promise<string>;\n close(): void;\n};\n\n/**\n * Where + how to clone the starter. Passed to a {@link CloneStarter} so the\n * caller (or a test) decides the implementation — production wires giget's\n * `downloadTemplate`; tests write a fixture tree.\n *\n * @since 0.1\n * @stable\n * @example\n * import type { CloneRequest } from \"@invinite-org/create-chartlang\";\n * declare const req: CloneRequest;\n * void req.dir;\n */\nexport type CloneRequest = Readonly<{\n /** The giget source (`STARTER_SOURCE_BASE` + `STARTER_CLONE_REF`). */\n source: string;\n /** The absolute target directory to clone into. */\n dir: string;\n}>;\n\n/**\n * Clone the starter tree into `req.dir`. Injected so the network clone stays\n * out of the unit tests (the only networked step in the flow).\n *\n * @since 0.1\n * @stable\n * @example\n * import type { CloneStarter } from \"@invinite-org/create-chartlang\";\n * const clone: CloneStarter = async () => {};\n * void clone;\n */\nexport type CloneStarter = (req: CloneRequest) => Promise<void>;\n\n/**\n * The injectable IO seam for {@link runCreateChartlang} — output streams,\n * whether stdin is a TTY (gates the interactive prompt), a prompter factory,\n * the clone implementation, and the install runner. Tests override every\n * field so the flow runs offline with no real stdin/network.\n *\n * @since 0.1\n * @stable\n * @example\n * import type { CreateChartlangDeps } from \"@invinite-org/create-chartlang\";\n * declare const deps: CreateChartlangDeps;\n * void deps.isTTY;\n */\nexport type CreateChartlangDeps = Readonly<{\n stdout: NodeJS.WritableStream;\n stderr: NodeJS.WritableStream;\n isTTY: boolean;\n createPrompter: () => Prompter;\n cloneStarter: CloneStarter;\n runInstall: (pm: PackageManager, dir: string) => Promise<void>;\n}>;\n\nfunction isPackageManager(value: string): value is PackageManager {\n return (PACKAGE_MANAGERS as ReadonlyArray<string>).includes(value);\n}\n\n/**\n * A resolved adapter: its offline bundle + its registry metadata, the pair\n * {@link resolveAdapter} returns for a bundled id.\n *\n * @since 0.1\n * @stable\n * @example\n * import type { ResolvedAdapter } from \"@invinite-org/create-chartlang\";\n * declare const r: ResolvedAdapter;\n * void r.bundle.id;\n */\nexport type ResolvedAdapter = Readonly<{\n bundle: GeneratedAdapterBundle;\n meta: GeneratedAdapterMeta;\n}>;\n\n/**\n * Look up an adapter's bundle + registry metadata by id, throwing if either is\n * missing. The `runCreateChartlang` flow only calls this with an `isSeamId`-\n * validated id (so it never throws in production), but the guard fails loudly\n * if `SEAM_IDS` ever drifts from the generated bundle/registry set.\n *\n * @since 0.1\n * @stable\n * @example\n * import { resolveAdapter } from \"@invinite-org/create-chartlang\";\n * const { bundle, meta } = resolveAdapter(\"echarts\");\n * void bundle.id;\n * void meta.library;\n */\nexport function resolveAdapter(id: string): ResolvedAdapter {\n const bundle = BUNDLED_ADAPTERS.find((b) => b.id === id);\n const meta = ADAPTER_REGISTRY.find((m) => m.id === id);\n if (bundle === undefined || meta === undefined) {\n throw new Error(`no bundle/registry entry for adapter \"${id}\"`);\n }\n return { bundle, meta };\n}\n\nasync function isNonEmptyDir(path: string): Promise<boolean> {\n try {\n return (await readdir(path)).length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Render the library-choice prompt list from the registry, echarts first\n * (the default) then the rest in registry order. Pure: no IO.\n *\n * @since 0.1\n * @stable\n * @example\n * import { renderLibraryChoices } from \"@invinite-org/create-chartlang\";\n * const text = renderLibraryChoices([]);\n * void text;\n */\nexport function renderLibraryChoices(registry: ReadonlyArray<GeneratedAdapterMeta>): string {\n const ordered = orderedRegistry(registry);\n const lines = [\"Choose a chart library:\", \"\"];\n for (const meta of ordered) {\n const suffix = meta.id === DEFAULT_LIBRARY ? \" (default)\" : \"\";\n const lib = meta.library === \"(none)\" ? \"no runtime dep\" : meta.library;\n lines.push(` ${meta.id} — ${meta.displayName} (${lib})${suffix}`);\n }\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\nfunction orderedRegistry(\n registry: ReadonlyArray<GeneratedAdapterMeta>,\n): ReadonlyArray<GeneratedAdapterMeta> {\n const def = registry.filter((m) => m.id === DEFAULT_LIBRARY);\n const rest = registry.filter((m) => m.id !== DEFAULT_LIBRARY);\n return [...def, ...rest];\n}\n\n/**\n * Harvest the published `@invinite-org/chartlang-*` `^`-ranges from a bundle's\n * own (generator-pinned) `package.json` deps + devDeps. These take precedence\n * over the baked manifest when rewriting the starter's workspace deps.\n *\n * @since 0.1\n * @stable\n * @example\n * import { bundleChartlangVersions } from \"@invinite-org/create-chartlang\";\n * const map = bundleChartlangVersions({ id: \"x\", files: {} });\n * void map;\n */\nexport function bundleChartlangVersions(\n bundle: GeneratedAdapterBundle,\n): Readonly<Record<string, string>> {\n // Every generated bundle carries a `package.json` (the generator always\n // emits one), so `?? \"{}\"` is only a parse-safety floor, never the path.\n const parsed = JSON.parse(bundle.files[\"package.json\"] ?? \"{}\") as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const out: Record<string, string> = {};\n for (const block of [parsed.dependencies, parsed.devDependencies]) {\n for (const [name, range] of Object.entries(block ?? {})) {\n if (name.startsWith(CHARTLANG_SCOPE)) {\n out[name] = range;\n }\n }\n }\n return out;\n}\n\n/**\n * Repoint a single `main`/`types`/`exports` entry string from the bundle's\n * unbuilt `./dist/*.{js,d.ts}` to the vendored TypeScript source `./src/*.ts`.\n * The vendored bundle ships only `src/` (create-chartlang never builds it), so\n * Vite + tsc resolve the adapter directly from source with NO build step. A\n * non-`./dist/` value is returned unchanged.\n */\nfunction repointDistToSrc(value: string): string {\n if (!value.startsWith(\"./dist/\")) {\n return value;\n }\n return value\n .replace(/^\\.\\/dist\\//, \"./src/\")\n .replace(/\\.d\\.ts$/, \".ts\")\n .replace(/\\.js$/, \".ts\");\n}\n\ntype VendoredExportEntry = { types?: string; import?: string };\ntype VendoredAdapterPkg = {\n main?: string;\n types?: string;\n exports?: Record<string, VendoredExportEntry>;\n [key: string]: unknown;\n};\n\n/**\n * Rewrite the vendored adapter `package.json`: substitute the `__PKG_NAME__`\n * placeholder, then repoint `main`/`types` and every `exports` entry's\n * `types`/`import` from `./dist/*` to the vendored `./src/*.ts` source. This is\n * an intentional DIVERGENCE from `cli add-adapter`, which keeps the\n * dist-pointing bundle (it expects a build); create-chartlang vendors source\n * only and never builds, so the manifest must resolve straight from `src/`.\n * Absent fields are left untouched.\n *\n * @since 0.1\n * @stable\n * @example\n * import { repointVendoredPackageJson } from \"@invinite-org/create-chartlang\";\n * const next = repointVendoredPackageJson('{\"main\":\"./dist/index.js\"}', \"@local/x-adapter\");\n * void next;\n */\nexport function repointVendoredPackageJson(contents: string, localName: string): string {\n const named = contents.split(PKG_NAME_PLACEHOLDER).join(localName);\n const pkg = JSON.parse(named) as VendoredAdapterPkg;\n if (pkg.main !== undefined) {\n pkg.main = repointDistToSrc(pkg.main);\n }\n if (pkg.types !== undefined) {\n pkg.types = repointDistToSrc(pkg.types);\n }\n if (pkg.exports !== undefined) {\n for (const entry of Object.values(pkg.exports)) {\n if (entry.types !== undefined) {\n entry.types = repointDistToSrc(entry.types);\n }\n if (entry.import !== undefined) {\n entry.import = repointDistToSrc(entry.import);\n }\n }\n }\n return `${JSON.stringify(pkg, null, 4)}\\n`;\n}\n\n/** Write a vendored adapter bundle, substituting the local name (Windows-safe paths). */\nasync function writeVendoredAdapter(\n bundle: GeneratedAdapterBundle,\n dir: string,\n localName: string,\n): Promise<void> {\n for (const [relPath, contents] of Object.entries(bundle.files)) {\n const dest = join(dir, ...relPath.split(\"/\"));\n await mkdir(dirname(dest), { recursive: true });\n const out =\n relPath === \"package.json\" ? repointVendoredPackageJson(contents, localName) : contents;\n await writeFile(dest, out, \"utf8\");\n }\n}\n\nasync function stripRepoArtefacts(dir: string): Promise<void> {\n for (const entry of STRIP_ENTRIES) {\n await rm(join(dir, entry), { recursive: true, force: true });\n }\n}\n\nfunction defaultEnv(): string {\n return [\n \"# chartlang starter environment. `.env` is git-ignored.\",\n \"DATABASE_URL=file:./data/starter.db\",\n \"\",\n \"# EODData API key — free tier: 100 calls/day, daily EOD, US symbols.\",\n \"# Register at https://eoddata.com/myaccount/api.aspx\",\n \"EODDATA_API_KEY=\",\n \"\",\n ].join(\"\\n\");\n}\n\nasync function writeNpmrc(dir: string): Promise<void> {\n await writeFile(join(dir, NPMRC_FILE), NPMRC_CONTENTS, \"utf8\");\n}\n\nasync function writeEnv(dir: string): Promise<void> {\n const examplePath = join(dir, ENV_EXAMPLE);\n let contents = defaultEnv();\n try {\n contents = await readFile(examplePath, \"utf8\");\n } catch {\n // No committed `.env.example` in the clone — fall back to the baked default.\n }\n await writeFile(join(dir, ENV_FILE), contents, \"utf8\");\n}\n\nfunction renderNextSteps(dir: string, pm: PackageManager, installed: boolean): string {\n const lines = [`Created chartlang starter in ${dir}`, \"\", \"Next steps:\", ` cd ${dir}`];\n if (!installed) {\n lines.push(` ${pm} install`);\n }\n lines.push(\n \" # add your free EODData key to .env (EODDATA_API_KEY=)\",\n ` ${pm} run dev`,\n \"\",\n \"Switch chart libraries later:\",\n \" npx @invinite-org/chartlang-cli add-adapter <id>\",\n \" # then edit src/lib/chart/activeAdapter.ts\",\n \"\",\n );\n return lines.join(\"\\n\");\n}\n\ntype ParsedArgs = Readonly<{\n dir: string;\n library: string | undefined;\n pm: string;\n install: boolean;\n yes: boolean;\n}>;\n\nfunction parse(argv: ReadonlyArray<string>): ParsedArgs {\n // `node:util.parseArgs` has no native `--no-<flag>` negation, so model the\n // documented `--no-install` opt-out as a `--no-install` boolean flag and\n // invert it (parity with the CLI's `--force`-style boolean opts).\n const parsed = parseArgs({\n args: argv.slice(),\n options: {\n library: { type: \"string\" },\n pm: { type: \"string\" },\n \"no-install\": { type: \"boolean\", default: false },\n yes: { type: \"boolean\", default: false },\n force: { type: \"boolean\", default: false },\n },\n allowPositionals: true,\n strict: true,\n });\n return {\n dir: parsed.positionals[0] ?? DEFAULT_TARGET,\n library: parsed.values.library,\n pm: parsed.values.pm ?? \"npm\",\n install: !parsed.values[\"no-install\"],\n yes: parsed.values.yes || parsed.values.force,\n };\n}\n\n/**\n * Resolve the chosen library id: an explicit `--library` (validated), else the\n * default on `--yes` / non-TTY, else an interactive prompt (default echarts on\n * an empty answer). Returns `undefined` (after writing an error) on an unknown\n * `--library`.\n */\nasync function resolveLibrary(\n explicit: string | undefined,\n yes: boolean,\n deps: CreateChartlangDeps,\n): Promise<SeamId | undefined> {\n if (explicit !== undefined) {\n if (!isSeamId(explicit)) {\n deps.stderr.write(\n `error: unknown --library \"${explicit}\" — valid ids: ${ADAPTER_REGISTRY.map((m) => m.id).join(\", \")}\\n`,\n );\n return undefined;\n }\n return explicit;\n }\n if (yes || !deps.isTTY) {\n return DEFAULT_LIBRARY;\n }\n const prompter = deps.createPrompter();\n try {\n deps.stdout.write(renderLibraryChoices(ADAPTER_REGISTRY));\n const answer = (await prompter.question(`Library [${DEFAULT_LIBRARY}]: `)).trim();\n if (answer === \"\") {\n return DEFAULT_LIBRARY;\n }\n if (!isSeamId(answer)) {\n deps.stderr.write(\n `error: unknown library \"${answer}\" — valid ids: ${ADAPTER_REGISTRY.map((m) => m.id).join(\", \")}\\n`,\n );\n return undefined;\n }\n return answer;\n } finally {\n prompter.close();\n }\n}\n\n/**\n * The production IO seam — real process streams, a `node:readline/promises`\n * prompter, a giget-backed clone, and a child-process install. The\n * `cloneStarter` argument is injected (giget lives in `index.ts`) so this\n * module carries no network dependency.\n *\n * @since 0.1\n * @stable\n * @example\n * import { defaultDeps } from \"@invinite-org/create-chartlang\";\n * const deps = defaultDeps({\n * cloneStarter: async () => {},\n * runInstall: async () => {},\n * });\n * deps.createPrompter().close();\n */\nexport function defaultDeps(\n io: Readonly<{ cloneStarter: CloneStarter; runInstall: CreateChartlangDeps[\"runInstall\"] }>,\n): CreateChartlangDeps {\n return {\n stdout: process.stdout,\n stderr: process.stderr,\n isTTY: process.stdin.isTTY === true,\n createPrompter: () => createInterface({ input: process.stdin, output: process.stdout }),\n cloneStarter: io.cloneStarter,\n runInstall: io.runInstall,\n };\n}\n\n/**\n * Scaffold a runnable chartlang starter: clone `apps/react-starter` from\n * GitHub (the one networked step), prompt for a chart library (default\n * echarts), vendor the chosen adapter from the CLI's offline bundle, rewrite\n * the single `activeAdapter.ts` seam + the `package.json` workspace deps, strip\n * repo-internal artefacts, write `.env`, optionally install, and print next\n * steps. Sets `process.exitCode = 1` on an unknown library or a non-empty\n * target dir without `--yes`.\n *\n * Flags: `[dir]` (default `./chartlang-starter`), `--library <id>`,\n * `--pm <npm|pnpm|yarn|bun>`, `--no-install`, `--yes` (accept defaults +\n * overwrite a non-empty dir).\n *\n * @since 0.1\n * @stable\n * @example\n * import { runCreateChartlang, defaultDeps } from \"@invinite-org/create-chartlang\";\n * await runCreateChartlang([\"my-app\", \"--library\", \"echarts\"], defaultDeps({\n * cloneStarter: async () => {},\n * runInstall: async () => {},\n * }));\n */\nexport async function runCreateChartlang(\n argv: ReadonlyArray<string>,\n deps: CreateChartlangDeps,\n): Promise<void> {\n const args = parse(argv);\n\n if (!isPackageManager(args.pm)) {\n deps.stderr.write(\n `error: invalid --pm \"${args.pm}\" — expected one of ${PACKAGE_MANAGERS.join(\", \")}\\n`,\n );\n process.exitCode = 1;\n return;\n }\n const pm: PackageManager = args.pm;\n\n const targetDir = isAbsolute(args.dir) ? args.dir : resolvePath(process.cwd(), args.dir);\n if (!args.yes && (await isNonEmptyDir(targetDir))) {\n deps.stderr.write(\n `error: target directory not empty: ${targetDir} (use --yes to overwrite)\\n`,\n );\n process.exitCode = 1;\n return;\n }\n\n const library = await resolveLibrary(args.library, args.yes, deps);\n if (library === undefined) {\n process.exitCode = 1;\n return;\n }\n\n // `library` is `isSeamId`-validated, so this resolves (the parity test\n // asserts every SeamId has a bundle + registry entry).\n const { bundle, meta } = resolveAdapter(library);\n\n await deps.cloneStarter({\n source: `${STARTER_SOURCE_BASE}${STARTER_CLONE_REF}`,\n dir: targetDir,\n });\n await stripRepoArtefacts(targetDir);\n\n // Make the clone standalone: bake the monorepo tsconfig base + repoint\n // `extends`, and opt into legacy-peer-deps so `npm install` resolves the\n // vite8/esbuild optional-peer conflict.\n await writeStandaloneTsconfig(targetDir);\n await writeNpmrc(targetDir);\n\n const vendorRel = `vendor/${library}-adapter`;\n const vendoredAdapterName = `@local/${library}-adapter`;\n await writeVendoredAdapter(\n bundle,\n join(targetDir, ...vendorRel.split(\"/\")),\n vendoredAdapterName,\n );\n\n const seamBody = seamTemplateFor(library, vendoredAdapterName);\n const seamDest = join(targetDir, ...SEAM_PATH);\n await mkdir(dirname(seamDest), { recursive: true });\n await writeFile(seamDest, seamBody, \"utf8\");\n\n const pkgPath = join(targetDir, \"package.json\");\n const pkgSource = await readFile(pkgPath, \"utf8\");\n const rewritten = rewriteStarterPackageJson({\n source: pkgSource,\n projectName: basename(targetDir),\n libraryId: library,\n chartLibrary: meta.library === \"(none)\" ? \"\" : meta.library,\n chartLibraryRange: meta.libraryRange,\n vendoredAdapterName,\n vendoredAdapterSpec: `file:./${vendorRel}`,\n bundleVersions: bundleChartlangVersions(bundle),\n });\n await writeFile(pkgPath, rewritten, \"utf8\");\n\n await writeEnv(targetDir);\n\n if (args.install) {\n await deps.runInstall(pm, targetDir);\n }\n\n deps.stdout.write(renderNextSteps(targetDir, pm, args.install));\n}\n"]}
1
+ {"version":3,"file":"createApp.js","sourceRoot":"","sources":["../src/createApp.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 { mkdir, readFile, readdir, rm, writeFile } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, join, resolve as resolvePath } from \"node:path\";\nimport { createInterface } from \"node:readline/promises\";\nimport { parseArgs } from \"node:util\";\n\nimport {\n ADAPTER_REGISTRY,\n BUNDLED_ADAPTERS,\n type GeneratedAdapterBundle,\n type GeneratedAdapterMeta,\n} from \"@invinite-org/chartlang-cli\";\n\nimport { STARTER_CLONE_REF } from \"./chartlangVersions.js\";\nimport { rewriteStarterPackageJson } from \"./rewritePackageJson.js\";\nimport { type SeamId, isSeamId, seamTemplateFor } from \"./seamTemplates.js\";\nimport { writeStandaloneTsconfig } from \"./starterTsconfig.js\";\n\nconst PKG_NAME_PLACEHOLDER = \"__PKG_NAME__\";\nconst CHARTLANG_SCOPE = \"@invinite-org/chartlang-\";\nconst PACKAGE_MANAGERS = [\"npm\", \"pnpm\", \"yarn\", \"bun\"] as const;\ntype PackageManager = (typeof PACKAGE_MANAGERS)[number];\n\nconst NPMRC_FILE = \".npmrc\";\n// Published `@invinite-org/chartlang-compiler` depends on `esbuild@^0.24`, but\n// `vite@8`'s optional peer wants `esbuild@^0.27 || ^0.28`. The app builds + runs\n// fine on esbuild 0.24 — npm's strict optional-peer check is the only blocker —\n// so the clone opts into `legacy-peer-deps` instead of bumping any esbuild.\nconst NPMRC_CONTENTS =\n \"# vite@8's optional esbuild peer (^0.27||^0.28) conflicts with the\\n# chartlang-compiler esbuild dep (^0.24); the app runs fine on 0.24.\\nlegacy-peer-deps=true\\n\";\n\nconst DEFAULT_TARGET = \"./chartlang-starter\";\nconst DEFAULT_LIBRARY: SeamId = \"canvas2d\";\nconst SEAM_PATH = [\"src\", \"lib\", \"chart\", \"activeAdapter.ts\"];\nconst ENV_EXAMPLE = \".env.example\";\nconst ENV_FILE = \".env\";\n\n// Repo-internal artefacts that must not ship to the user's clone. The e2e\n// suite lives under `tests/`; `playwright.config.ts` references it (a mock\n// server + global setup), so it is stripped too — otherwise the cloned\n// project ships a `playwright.config.ts` pointing at deleted files.\nconst STRIP_ENTRIES = [\"CLAUDE.md\", \"tests\", \".changeset\", \".github\", \"playwright.config.ts\"];\n\n/**\n * The starter source giget clones, minus the ref suffix. The installer\n * appends {@link STARTER_CLONE_REF} to pin the matching tagged tree.\n *\n * @since 0.1\n * @stable\n * @example\n * import { STARTER_SOURCE_BASE } from \"@invinite-org/create-chartlang\";\n * void STARTER_SOURCE_BASE;\n */\nexport const STARTER_SOURCE_BASE = \"github:outraday-org/chartlang/apps/react-starter\";\n\n/**\n * A minimal `node:readline/promises`-like surface — the subset the\n * interactive library prompt needs. Lets a test inject a fixed answer\n * instead of driving the real TTY. Mirrors the CLI's `Prompter`.\n *\n * @since 0.1\n * @stable\n * @example\n * import type { Prompter } from \"@invinite-org/create-chartlang\";\n * const p: Prompter = { question: async () => \"echarts\", close: () => {} };\n * void p;\n */\nexport type Prompter = {\n question(query: string): Promise<string>;\n close(): void;\n};\n\n/**\n * Where + how to clone the starter. Passed to a {@link CloneStarter} so the\n * caller (or a test) decides the implementation — production wires giget's\n * `downloadTemplate`; tests write a fixture tree.\n *\n * @since 0.1\n * @stable\n * @example\n * import type { CloneRequest } from \"@invinite-org/create-chartlang\";\n * declare const req: CloneRequest;\n * void req.dir;\n */\nexport type CloneRequest = Readonly<{\n /** The giget source (`STARTER_SOURCE_BASE` + `STARTER_CLONE_REF`). */\n source: string;\n /** The absolute target directory to clone into. */\n dir: string;\n}>;\n\n/**\n * Clone the starter tree into `req.dir`. Injected so the network clone stays\n * out of the unit tests (the only networked step in the flow).\n *\n * @since 0.1\n * @stable\n * @example\n * import type { CloneStarter } from \"@invinite-org/create-chartlang\";\n * const clone: CloneStarter = async () => {};\n * void clone;\n */\nexport type CloneStarter = (req: CloneRequest) => Promise<void>;\n\n/**\n * The injectable IO seam for {@link runCreateChartlang} — output streams,\n * whether stdin is a TTY (gates the interactive prompt), a prompter factory,\n * the clone implementation, and the install runner. Tests override every\n * field so the flow runs offline with no real stdin/network.\n *\n * @since 0.1\n * @stable\n * @example\n * import type { CreateChartlangDeps } from \"@invinite-org/create-chartlang\";\n * declare const deps: CreateChartlangDeps;\n * void deps.isTTY;\n */\nexport type CreateChartlangDeps = Readonly<{\n stdout: NodeJS.WritableStream;\n stderr: NodeJS.WritableStream;\n isTTY: boolean;\n createPrompter: () => Prompter;\n cloneStarter: CloneStarter;\n runInstall: (pm: PackageManager, dir: string) => Promise<void>;\n}>;\n\nfunction isPackageManager(value: string): value is PackageManager {\n return (PACKAGE_MANAGERS as ReadonlyArray<string>).includes(value);\n}\n\n/**\n * A resolved adapter: its offline bundle + its registry metadata, the pair\n * {@link resolveAdapter} returns for a bundled id.\n *\n * @since 0.1\n * @stable\n * @example\n * import type { ResolvedAdapter } from \"@invinite-org/create-chartlang\";\n * declare const r: ResolvedAdapter;\n * void r.bundle.id;\n */\nexport type ResolvedAdapter = Readonly<{\n bundle: GeneratedAdapterBundle;\n meta: GeneratedAdapterMeta;\n}>;\n\n/**\n * Look up an adapter's bundle + registry metadata by id, throwing if either is\n * missing. The `runCreateChartlang` flow only calls this with an `isSeamId`-\n * validated id (so it never throws in production), but the guard fails loudly\n * if `SEAM_IDS` ever drifts from the generated bundle/registry set.\n *\n * @since 0.1\n * @stable\n * @example\n * import { resolveAdapter } from \"@invinite-org/create-chartlang\";\n * const { bundle, meta } = resolveAdapter(\"echarts\");\n * void bundle.id;\n * void meta.library;\n */\nexport function resolveAdapter(id: string): ResolvedAdapter {\n const bundle = BUNDLED_ADAPTERS.find((b) => b.id === id);\n const meta = ADAPTER_REGISTRY.find((m) => m.id === id);\n if (bundle === undefined || meta === undefined) {\n throw new Error(`no bundle/registry entry for adapter \"${id}\"`);\n }\n return { bundle, meta };\n}\n\nasync function isNonEmptyDir(path: string): Promise<boolean> {\n try {\n return (await readdir(path)).length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Render the library-choice prompt list from the registry, canvas2d first\n * (the default) then the rest in registry order. Pure: no IO.\n *\n * @since 0.1\n * @stable\n * @example\n * import { renderLibraryChoices } from \"@invinite-org/create-chartlang\";\n * const text = renderLibraryChoices([]);\n * void text;\n */\nexport function renderLibraryChoices(registry: ReadonlyArray<GeneratedAdapterMeta>): string {\n const ordered = orderedRegistry(registry);\n const lines = [\"Choose a chart library:\", \"\"];\n for (const meta of ordered) {\n const suffix = meta.id === DEFAULT_LIBRARY ? \" (default)\" : \"\";\n const lib = meta.library === \"(none)\" ? \"no runtime dep\" : meta.library;\n lines.push(` ${meta.id} — ${meta.displayName} (${lib})${suffix}`);\n }\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\nfunction orderedRegistry(\n registry: ReadonlyArray<GeneratedAdapterMeta>,\n): ReadonlyArray<GeneratedAdapterMeta> {\n const def = registry.filter((m) => m.id === DEFAULT_LIBRARY);\n const rest = registry.filter((m) => m.id !== DEFAULT_LIBRARY);\n return [...def, ...rest];\n}\n\n/**\n * Harvest the published `@invinite-org/chartlang-*` `^`-ranges from a bundle's\n * own (generator-pinned) `package.json` deps + devDeps. These take precedence\n * over the baked manifest when rewriting the starter's workspace deps.\n *\n * @since 0.1\n * @stable\n * @example\n * import { bundleChartlangVersions } from \"@invinite-org/create-chartlang\";\n * const map = bundleChartlangVersions({ id: \"x\", files: {} });\n * void map;\n */\nexport function bundleChartlangVersions(\n bundle: GeneratedAdapterBundle,\n): Readonly<Record<string, string>> {\n // Every generated bundle carries a `package.json` (the generator always\n // emits one), so `?? \"{}\"` is only a parse-safety floor, never the path.\n const parsed = JSON.parse(bundle.files[\"package.json\"] ?? \"{}\") as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const out: Record<string, string> = {};\n for (const block of [parsed.dependencies, parsed.devDependencies]) {\n for (const [name, range] of Object.entries(block ?? {})) {\n if (name.startsWith(CHARTLANG_SCOPE)) {\n out[name] = range;\n }\n }\n }\n return out;\n}\n\n/**\n * Repoint a single `main`/`types`/`exports` entry string from the bundle's\n * unbuilt `./dist/*.{js,d.ts}` to the vendored TypeScript source `./src/*.ts`.\n * The vendored bundle ships only `src/` (create-chartlang never builds it), so\n * Vite + tsc resolve the adapter directly from source with NO build step. A\n * non-`./dist/` value is returned unchanged.\n */\nfunction repointDistToSrc(value: string): string {\n if (!value.startsWith(\"./dist/\")) {\n return value;\n }\n return value\n .replace(/^\\.\\/dist\\//, \"./src/\")\n .replace(/\\.d\\.ts$/, \".ts\")\n .replace(/\\.js$/, \".ts\");\n}\n\ntype VendoredExportEntry = { types?: string; import?: string };\ntype VendoredAdapterPkg = {\n main?: string;\n types?: string;\n exports?: Record<string, VendoredExportEntry>;\n [key: string]: unknown;\n};\n\n/**\n * Rewrite the vendored adapter `package.json`: substitute the `__PKG_NAME__`\n * placeholder, then repoint `main`/`types` and every `exports` entry's\n * `types`/`import` from `./dist/*` to the vendored `./src/*.ts` source. This is\n * an intentional DIVERGENCE from `cli add-adapter`, which keeps the\n * dist-pointing bundle (it expects a build); create-chartlang vendors source\n * only and never builds, so the manifest must resolve straight from `src/`.\n * Absent fields are left untouched.\n *\n * @since 0.1\n * @stable\n * @example\n * import { repointVendoredPackageJson } from \"@invinite-org/create-chartlang\";\n * const next = repointVendoredPackageJson('{\"main\":\"./dist/index.js\"}', \"@local/x-adapter\");\n * void next;\n */\nexport function repointVendoredPackageJson(contents: string, localName: string): string {\n const named = contents.split(PKG_NAME_PLACEHOLDER).join(localName);\n const pkg = JSON.parse(named) as VendoredAdapterPkg;\n if (pkg.main !== undefined) {\n pkg.main = repointDistToSrc(pkg.main);\n }\n if (pkg.types !== undefined) {\n pkg.types = repointDistToSrc(pkg.types);\n }\n if (pkg.exports !== undefined) {\n for (const entry of Object.values(pkg.exports)) {\n if (entry.types !== undefined) {\n entry.types = repointDistToSrc(entry.types);\n }\n if (entry.import !== undefined) {\n entry.import = repointDistToSrc(entry.import);\n }\n }\n }\n return `${JSON.stringify(pkg, null, 4)}\\n`;\n}\n\n/** Write a vendored adapter bundle, substituting the local name (Windows-safe paths). */\nasync function writeVendoredAdapter(\n bundle: GeneratedAdapterBundle,\n dir: string,\n localName: string,\n): Promise<void> {\n for (const [relPath, contents] of Object.entries(bundle.files)) {\n const dest = join(dir, ...relPath.split(\"/\"));\n await mkdir(dirname(dest), { recursive: true });\n const out =\n relPath === \"package.json\" ? repointVendoredPackageJson(contents, localName) : contents;\n await writeFile(dest, out, \"utf8\");\n }\n}\n\nasync function stripRepoArtefacts(dir: string): Promise<void> {\n for (const entry of STRIP_ENTRIES) {\n await rm(join(dir, entry), { recursive: true, force: true });\n }\n}\n\nfunction defaultEnv(): string {\n return [\n \"# Copy to `.env` and fill in. `.env` is git-ignored; this sample is committed.\",\n \"\",\n \"# Local SQLite file (saved scripts + cached daily bars). The DB auto-creates,\",\n \"# migrates, and seeds on first boot — no manual step.\",\n \"DATABASE_URL=file:./data/starter.db\",\n \"\",\n \"# Market data needs NO API key: the starter loads daily US bars from Yahoo\",\n \"# Finance (free, unmetered) and caches each symbol in SQLite, so a re-open /\",\n \"# re-compile costs zero network calls.\",\n \"\",\n ].join(\"\\n\");\n}\n\nasync function writeNpmrc(dir: string): Promise<void> {\n await writeFile(join(dir, NPMRC_FILE), NPMRC_CONTENTS, \"utf8\");\n}\n\nasync function writeEnv(dir: string): Promise<void> {\n const examplePath = join(dir, ENV_EXAMPLE);\n let contents = defaultEnv();\n try {\n contents = await readFile(examplePath, \"utf8\");\n } catch {\n // No committed `.env.example` in the clone — fall back to the baked default.\n }\n await writeFile(join(dir, ENV_FILE), contents, \"utf8\");\n}\n\nfunction renderNextSteps(dir: string, pm: PackageManager, installed: boolean): string {\n const lines = [`Created chartlang starter in ${dir}`, \"\", \"Next steps:\", ` cd ${dir}`];\n if (!installed) {\n lines.push(` ${pm} install`);\n }\n lines.push(\n \" # market data comes from Yahoo Finance — no API key needed\",\n ` ${pm} run dev`,\n \"\",\n \"Switch chart libraries later:\",\n \" npx @invinite-org/chartlang-cli add-adapter <id>\",\n \" # then edit src/lib/chart/activeAdapter.ts\",\n \"\",\n );\n return lines.join(\"\\n\");\n}\n\ntype ParsedArgs = Readonly<{\n dir: string;\n library: string | undefined;\n pm: string;\n install: boolean;\n yes: boolean;\n}>;\n\nfunction parse(argv: ReadonlyArray<string>): ParsedArgs {\n // `node:util.parseArgs` has no native `--no-<flag>` negation, so model the\n // documented `--no-install` opt-out as a `--no-install` boolean flag and\n // invert it (parity with the CLI's `--force`-style boolean opts).\n const parsed = parseArgs({\n args: argv.slice(),\n options: {\n library: { type: \"string\" },\n pm: { type: \"string\" },\n \"no-install\": { type: \"boolean\", default: false },\n yes: { type: \"boolean\", default: false },\n force: { type: \"boolean\", default: false },\n },\n allowPositionals: true,\n strict: true,\n });\n return {\n dir: parsed.positionals[0] ?? DEFAULT_TARGET,\n library: parsed.values.library,\n pm: parsed.values.pm ?? \"npm\",\n install: !parsed.values[\"no-install\"],\n yes: parsed.values.yes || parsed.values.force,\n };\n}\n\n/**\n * Resolve the chosen library id: an explicit `--library` (validated), else the\n * default on `--yes` / non-TTY, else an interactive prompt (default echarts on\n * an empty answer). Returns `undefined` (after writing an error) on an unknown\n * `--library`.\n */\nasync function resolveLibrary(\n explicit: string | undefined,\n yes: boolean,\n deps: CreateChartlangDeps,\n): Promise<SeamId | undefined> {\n if (explicit !== undefined) {\n if (!isSeamId(explicit)) {\n deps.stderr.write(\n `error: unknown --library \"${explicit}\" — valid ids: ${ADAPTER_REGISTRY.map((m) => m.id).join(\", \")}\\n`,\n );\n return undefined;\n }\n return explicit;\n }\n if (yes || !deps.isTTY) {\n return DEFAULT_LIBRARY;\n }\n const prompter = deps.createPrompter();\n try {\n deps.stdout.write(renderLibraryChoices(ADAPTER_REGISTRY));\n const answer = (await prompter.question(`Library [${DEFAULT_LIBRARY}]: `)).trim();\n if (answer === \"\") {\n return DEFAULT_LIBRARY;\n }\n if (!isSeamId(answer)) {\n deps.stderr.write(\n `error: unknown library \"${answer}\" — valid ids: ${ADAPTER_REGISTRY.map((m) => m.id).join(\", \")}\\n`,\n );\n return undefined;\n }\n return answer;\n } finally {\n prompter.close();\n }\n}\n\n/**\n * The production IO seam — real process streams, a `node:readline/promises`\n * prompter, a giget-backed clone, and a child-process install. The\n * `cloneStarter` argument is injected (giget lives in `index.ts`) so this\n * module carries no network dependency.\n *\n * @since 0.1\n * @stable\n * @example\n * import { defaultDeps } from \"@invinite-org/create-chartlang\";\n * const deps = defaultDeps({\n * cloneStarter: async () => {},\n * runInstall: async () => {},\n * });\n * deps.createPrompter().close();\n */\nexport function defaultDeps(\n io: Readonly<{ cloneStarter: CloneStarter; runInstall: CreateChartlangDeps[\"runInstall\"] }>,\n): CreateChartlangDeps {\n return {\n stdout: process.stdout,\n stderr: process.stderr,\n isTTY: process.stdin.isTTY === true,\n createPrompter: () => createInterface({ input: process.stdin, output: process.stdout }),\n cloneStarter: io.cloneStarter,\n runInstall: io.runInstall,\n };\n}\n\n/**\n * Scaffold a runnable chartlang starter: clone `apps/react-starter` from\n * GitHub (the one networked step), prompt for a chart library (default\n * echarts), vendor the chosen adapter from the CLI's offline bundle, rewrite\n * the single `activeAdapter.ts` seam + the `package.json` workspace deps, strip\n * repo-internal artefacts, write `.env`, optionally install, and print next\n * steps. Sets `process.exitCode = 1` on an unknown library or a non-empty\n * target dir without `--yes`.\n *\n * Flags: `[dir]` (default `./chartlang-starter`), `--library <id>`,\n * `--pm <npm|pnpm|yarn|bun>`, `--no-install`, `--yes` (accept defaults +\n * overwrite a non-empty dir).\n *\n * @since 0.1\n * @stable\n * @example\n * import { runCreateChartlang, defaultDeps } from \"@invinite-org/create-chartlang\";\n * await runCreateChartlang([\"my-app\", \"--library\", \"echarts\"], defaultDeps({\n * cloneStarter: async () => {},\n * runInstall: async () => {},\n * }));\n */\nexport async function runCreateChartlang(\n argv: ReadonlyArray<string>,\n deps: CreateChartlangDeps,\n): Promise<void> {\n const args = parse(argv);\n\n if (!isPackageManager(args.pm)) {\n deps.stderr.write(\n `error: invalid --pm \"${args.pm}\" — expected one of ${PACKAGE_MANAGERS.join(\", \")}\\n`,\n );\n process.exitCode = 1;\n return;\n }\n const pm: PackageManager = args.pm;\n\n const targetDir = isAbsolute(args.dir) ? args.dir : resolvePath(process.cwd(), args.dir);\n if (!args.yes && (await isNonEmptyDir(targetDir))) {\n deps.stderr.write(\n `error: target directory not empty: ${targetDir} (use --yes to overwrite)\\n`,\n );\n process.exitCode = 1;\n return;\n }\n\n const library = await resolveLibrary(args.library, args.yes, deps);\n if (library === undefined) {\n process.exitCode = 1;\n return;\n }\n\n // `library` is `isSeamId`-validated, so this resolves (the parity test\n // asserts every SeamId has a bundle + registry entry).\n const { bundle, meta } = resolveAdapter(library);\n\n await deps.cloneStarter({\n source: `${STARTER_SOURCE_BASE}${STARTER_CLONE_REF}`,\n dir: targetDir,\n });\n await stripRepoArtefacts(targetDir);\n\n // Make the clone standalone: bake the monorepo tsconfig base + repoint\n // `extends`, and opt into legacy-peer-deps so `npm install` resolves the\n // vite8/esbuild optional-peer conflict.\n await writeStandaloneTsconfig(targetDir);\n await writeNpmrc(targetDir);\n\n const vendorRel = `vendor/${library}-adapter`;\n const vendoredAdapterName = `@local/${library}-adapter`;\n await writeVendoredAdapter(\n bundle,\n join(targetDir, ...vendorRel.split(\"/\")),\n vendoredAdapterName,\n );\n\n const seamBody = seamTemplateFor(library, vendoredAdapterName);\n const seamDest = join(targetDir, ...SEAM_PATH);\n await mkdir(dirname(seamDest), { recursive: true });\n await writeFile(seamDest, seamBody, \"utf8\");\n\n const pkgPath = join(targetDir, \"package.json\");\n const pkgSource = await readFile(pkgPath, \"utf8\");\n const rewritten = rewriteStarterPackageJson({\n source: pkgSource,\n projectName: basename(targetDir),\n libraryId: library,\n chartLibrary: meta.library === \"(none)\" ? \"\" : meta.library,\n chartLibraryRange: meta.libraryRange,\n vendoredAdapterName,\n vendoredAdapterSpec: `file:./${vendorRel}`,\n bundleVersions: bundleChartlangVersions(bundle),\n });\n await writeFile(pkgPath, rewritten, \"utf8\");\n\n await writeEnv(targetDir);\n\n if (args.install) {\n await deps.runInstall(pm, targetDir);\n }\n\n deps.stdout.write(renderNextSteps(targetDir, pm, args.install));\n}\n"]}
@@ -1 +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"}
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+R/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"}
@@ -59,13 +59,26 @@ ${OPTS_TYPES}
59
59
  // canvas2d paints onto a <canvas>; the seam creates one inside the generic
60
60
  // container so ChartPane only ever provides a DOM node.
61
61
  export function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {
62
+ const cssWidth = opts.container.clientWidth || 800
63
+ const cssHeight = opts.container.clientHeight || 480
64
+ // Back the canvas at device-pixel resolution so the chart stays crisp on
65
+ // HiDPI / retina screens; CSS keeps it laid out at the container's CSS size.
66
+ // The adapter draws into the full backing store and only re-scales its
67
+ // pan/zoom pointer math by \`devicePixelRatio\`, so the render is unchanged.
68
+ const dpr = opts.container.ownerDocument.defaultView?.devicePixelRatio ?? 1
62
69
  const canvas = opts.container.ownerDocument.createElement("canvas")
63
- canvas.width = opts.container.clientWidth || 800
64
- canvas.height = opts.container.clientHeight || 480
70
+ canvas.width = Math.round(cssWidth * dpr)
71
+ canvas.height = Math.round(cssHeight * dpr)
72
+ canvas.style.width = \`\${cssWidth}px\`
73
+ canvas.style.height = \`\${cssHeight}px\`
65
74
  opts.container.replaceChildren(canvas)
66
75
  return createCanvas2dAdapter({
67
76
  canvas,
68
77
  candleSource: opts.candleSource,
78
+ devicePixelRatio: dpr,
79
+ // Frame the most recent ~120 bars by default (TradingView-style); the
80
+ // full history stays in memory and scrollable via pan / zoom-out.
81
+ initialVisibleBars: 120,
69
82
  ...(opts.interval !== undefined ? { interval: opts.interval } : {}),
70
83
  ...(opts.onAlert !== undefined ? { onAlert: opts.onAlert } : {}),
71
84
  })
@@ -79,10 +92,8 @@ export async function runActiveLoop(
79
92
  }
80
93
  `;
81
94
  const LWC_SEAM = `${HEADER}
82
- import { createChart } from "lightweight-charts"
83
95
  import {
84
96
  createLightweightChartsAdapter,
85
- type CreateLightweightChartsAdapterOpts,
86
97
  type LwcAdapterHandle,
87
98
  runRendererLoop,
88
99
  } from "chartlang-example-lightweight-charts-adapter"
@@ -93,19 +104,13 @@ export const ACTIVE_ADAPTER_ID = "lightweight-charts"
93
104
 
94
105
  ${OPTS_TYPES}
95
106
 
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
107
  export function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {
105
108
  return createLightweightChartsAdapter({
106
109
  container: opts.container,
107
- createChart: makeChart,
108
110
  candleSource: opts.candleSource,
111
+ // Frame the most recent ~120 bars by default (TradingView-style); the
112
+ // full history stays in memory and scrollable via pan / zoom-out.
113
+ initialVisibleBars: 120,
109
114
  ...(opts.interval !== undefined ? { interval: opts.interval } : {}),
110
115
  ...(opts.onAlert !== undefined ? { onAlert: opts.onAlert } : {}),
111
116
  })
@@ -138,6 +143,9 @@ export function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandl
138
143
  width: opts.container.clientWidth || 800,
139
144
  height: opts.container.clientHeight || 480,
140
145
  candleSource: opts.candleSource,
146
+ // Frame the most recent ~120 bars by default (TradingView-style); the
147
+ // full history stays in memory and scrollable via pan / zoom-out.
148
+ initialVisibleBars: 120,
141
149
  ...(opts.interval !== undefined ? { interval: opts.interval } : {}),
142
150
  ...(opts.onAlert !== undefined ? { onAlert: opts.onAlert } : {}),
143
151
  })
@@ -195,6 +203,9 @@ export function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandl
195
203
  return createEChartsAdapter({
196
204
  echartsFactory: () => makeEchartsSurface(opts.container),
197
205
  candleSource: opts.candleSource,
206
+ // Frame the most recent ~120 bars by default (TradingView-style); the
207
+ // full history stays in memory and scrollable via pan / zoom-out.
208
+ initialVisibleBars: 120,
198
209
  ...(opts.interval !== undefined ? { interval: opts.interval } : {}),
199
210
  ...(opts.onAlert !== undefined ? { onAlert: opts.onAlert } : {}),
200
211
  })
@@ -224,39 +235,43 @@ export const ACTIVE_ADAPTER_ID = "konva"
224
235
 
225
236
  ${OPTS_TYPES}
226
237
 
238
+ // The Konva adapter renders alerts as part of its declared capability surface
239
+ // but paints no alert UI, and its factory carries no onAlert hook — so the seam
240
+ // forwards alerts itself from the drain loop. onAlert is captured per handle
241
+ // here and invoked in runActiveLoop (which, unlike the factory, sees emissions).
242
+ const KONVA_ON_ALERT = new WeakMap<ActiveAdapterHandle, (a: AlertEmission) => void>()
243
+
227
244
  export function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {
228
245
  const width = opts.container.clientWidth || 800
229
246
  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({
247
+ // The real Konva namespace is wider than the structural slice the adapter
248
+ // consumes, so cast it to the adapter's own \`konva\` field type. Passing
249
+ // \`container\` lets the adapter mount the stage on our DOM node AND attach
250
+ // its wheel-zoom / drag-pan / dblclick-reset interaction to it.
251
+ const konva = Konva as unknown as CreateKonvaAdapterOpts["konva"]
252
+ const handle = createKonvaAdapter({
245
253
  konva,
254
+ container: opts.container,
246
255
  stage: { width, height },
247
256
  candleSource: opts.candleSource,
257
+ // Frame the most recent ~120 bars by default (TradingView-style); the
258
+ // full history stays in memory and scrollable via pan / zoom-out.
259
+ initialVisibleBars: 120,
248
260
  ...(opts.interval !== undefined ? { interval: opts.interval } : {}),
249
261
  })
262
+ if (opts.onAlert !== undefined) KONVA_ON_ALERT.set(handle, opts.onAlert)
263
+ return handle
250
264
  }
251
265
 
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).
266
+ // konva has no exported async loop that forwards alerts; mirror the shared loop
267
+ // shape locally over feedCandleEvent / handleInterval and forward each drain's
268
+ // alerts to the seam-captured onAlert (its factory carries no hook).
255
269
  export async function runActiveLoop(
256
270
  handle: ActiveAdapterHandle,
257
271
  opts: RunActiveLoopOpts = {},
258
272
  ): Promise<void> {
259
273
  const signal = opts.signal
274
+ const onAlert = KONVA_ON_ALERT.get(handle)
260
275
  const aborted = (): boolean => signal?.aborted ?? false
261
276
  if (aborted()) return
262
277
  for await (const event of handle.candles({ interval: handleInterval(handle) })) {
@@ -269,6 +284,7 @@ export async function runActiveLoop(
269
284
  const emissions: RunnerEmissions = await handle.host.drain()
270
285
  if (aborted()) return
271
286
  handle.onEmissions(emissions)
287
+ if (onAlert !== undefined) for (const alert of emissions.alerts) onAlert(alert)
272
288
  }
273
289
  }
274
290
  `;
@@ -1 +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"]}
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCX,CAAC;AAEF,MAAM,QAAQ,GAAG,GAAG,MAAM;;;;;;;;;;;EAWxB,UAAU;;;;;;;;;;;;;;;;;;;;CAoBX,CAAC;AAEF,MAAM,UAAU,GAAG,GAAG,MAAM;;;;;;;;;;;;EAY1B,UAAU;;;;;;;;;;;;;;;;;;;;;;CAsBX,CAAC;AAEF,MAAM,YAAY,GAAG,GAAG,MAAM;;;;;;;;;;;;;EAa5B,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8CX,CAAC;AAEF,MAAM,UAAU,GAAG,GAAG,MAAM;;;;;;;;;;;;;;;EAe1B,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsDX,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 cssWidth = opts.container.clientWidth || 800\n const cssHeight = opts.container.clientHeight || 480\n // Back the canvas at device-pixel resolution so the chart stays crisp on\n // HiDPI / retina screens; CSS keeps it laid out at the container's CSS size.\n // The adapter draws into the full backing store and only re-scales its\n // pan/zoom pointer math by \\`devicePixelRatio\\`, so the render is unchanged.\n const dpr = opts.container.ownerDocument.defaultView?.devicePixelRatio ?? 1\n const canvas = opts.container.ownerDocument.createElement(\"canvas\")\n canvas.width = Math.round(cssWidth * dpr)\n canvas.height = Math.round(cssHeight * dpr)\n canvas.style.width = \\`\\${cssWidth}px\\`\n canvas.style.height = \\`\\${cssHeight}px\\`\n opts.container.replaceChildren(canvas)\n return createCanvas2dAdapter({\n canvas,\n candleSource: opts.candleSource,\n devicePixelRatio: dpr,\n // Frame the most recent ~120 bars by default (TradingView-style); the\n // full history stays in memory and scrollable via pan / zoom-out.\n initialVisibleBars: 120,\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 {\n createLightweightChartsAdapter,\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\nexport function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {\n return createLightweightChartsAdapter({\n container: opts.container,\n candleSource: opts.candleSource,\n // Frame the most recent ~120 bars by default (TradingView-style); the\n // full history stays in memory and scrollable via pan / zoom-out.\n initialVisibleBars: 120,\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 // Frame the most recent ~120 bars by default (TradingView-style); the\n // full history stays in memory and scrollable via pan / zoom-out.\n initialVisibleBars: 120,\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 // Frame the most recent ~120 bars by default (TradingView-style); the\n // full history stays in memory and scrollable via pan / zoom-out.\n initialVisibleBars: 120,\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\n// The Konva adapter renders alerts as part of its declared capability surface\n// but paints no alert UI, and its factory carries no onAlert hook — so the seam\n// forwards alerts itself from the drain loop. onAlert is captured per handle\n// here and invoked in runActiveLoop (which, unlike the factory, sees emissions).\nconst KONVA_ON_ALERT = new WeakMap<ActiveAdapterHandle, (a: AlertEmission) => void>()\n\nexport function createActiveAdapter(opts: CreateAdapterOpts): ActiveAdapterHandle {\n const width = opts.container.clientWidth || 800\n const height = opts.container.clientHeight || 480\n // The real Konva namespace is wider than the structural slice the adapter\n // consumes, so cast it to the adapter's own \\`konva\\` field type. Passing\n // \\`container\\` lets the adapter mount the stage on our DOM node AND attach\n // its wheel-zoom / drag-pan / dblclick-reset interaction to it.\n const konva = Konva as unknown as CreateKonvaAdapterOpts[\"konva\"]\n const handle = createKonvaAdapter({\n konva,\n container: opts.container,\n stage: { width, height },\n candleSource: opts.candleSource,\n // Frame the most recent ~120 bars by default (TradingView-style); the\n // full history stays in memory and scrollable via pan / zoom-out.\n initialVisibleBars: 120,\n ...(opts.interval !== undefined ? { interval: opts.interval } : {}),\n })\n if (opts.onAlert !== undefined) KONVA_ON_ALERT.set(handle, opts.onAlert)\n return handle\n}\n\n// konva has no exported async loop that forwards alerts; mirror the shared loop\n// shape locally over feedCandleEvent / handleInterval and forward each drain's\n// alerts to the seam-captured onAlert (its factory carries no hook).\nexport async function runActiveLoop(\n handle: ActiveAdapterHandle,\n opts: RunActiveLoopOpts = {},\n): Promise<void> {\n const signal = opts.signal\n const onAlert = KONVA_ON_ALERT.get(handle)\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 if (onAlert !== undefined) for (const alert of emissions.alerts) onAlert(alert)\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invinite-org/create-chartlang",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "npm create @invinite-org/chartlang — scaffold a runnable chartlang starter app (TanStack Start + your chosen chart library).",
@@ -23,7 +23,7 @@
23
23
  ],
24
24
  "dependencies": {
25
25
  "giget": "^1.2.3",
26
- "@invinite-org/chartlang-cli": "^1.3.0"
26
+ "@invinite-org/chartlang-cli": "^1.3.1"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^20.0.0"