@elaraai/create-e3 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Copyright (c) 2025 Elara AI Pty Ltd
4
+ * Licensed under AGPL-3.0-or-later. See LICENSE for details.
5
+ *
6
+ * Static bin shim — exists in fresh checkout so pnpm install can create
7
+ * the bin symlink BEFORE the package is built. Dynamic-imports the
8
+ * built CLI entrypoint; argv flows through via process.argv.
9
+ */
10
+ await import('../dist/index.js');
package/dist/index.js CHANGED
@@ -39,15 +39,38 @@ var DOTFILE_RENAMES = {
39
39
  gitignore: ".gitignore",
40
40
  npmrc: ".npmrc"
41
41
  };
42
+ var MANIFEST_FILE = "template.json";
42
43
  function substituteTokens(content, names) {
43
44
  return content.replaceAll("__PROJECT_NAME__", names.projectName).replaceAll("__DISPLAY_NAME__", names.displayName).replaceAll("__WORKSPACE_NAME__", names.workspaceName);
44
45
  }
45
- function transformPackageJson(raw, names, version) {
46
+ function transformPackageJson(raw, names, version, manifest, enabled) {
46
47
  const pkg = JSON.parse(raw);
47
48
  pkg.name = `@elaraai/${names.projectName}`;
48
49
  pkg.description = names.displayName;
49
50
  pkg.version = "0.0.1";
50
51
  delete pkg.private;
52
+ if (manifest) {
53
+ const deps = pkg.dependencies;
54
+ const devDeps = pkg.devDependencies;
55
+ const scripts = pkg.scripts;
56
+ for (const [feature, spec] of Object.entries(manifest.features)) {
57
+ if (enabled(feature))
58
+ continue;
59
+ for (const d of spec.dependencies ?? [])
60
+ delete deps?.[d];
61
+ for (const d of spec.devDependencies ?? [])
62
+ delete devDeps?.[d];
63
+ for (const s of spec.scripts ?? [])
64
+ delete scripts?.[s];
65
+ }
66
+ for (const [name, variants] of Object.entries(manifest.scriptVariants ?? {})) {
67
+ const match = variants.find((v) => v.when.every(enabled));
68
+ if (match)
69
+ scripts[name] = match.value;
70
+ else
71
+ delete scripts?.[name];
72
+ }
73
+ }
51
74
  const pin = `^${version}`;
52
75
  for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
53
76
  const deps = pkg[field];
@@ -77,6 +100,12 @@ function extOf(path) {
77
100
  const dot = base.lastIndexOf(".");
78
101
  return dot <= 0 ? "" : base.slice(dot);
79
102
  }
103
+ function loadManifest(templateDir) {
104
+ const path = join(templateDir, MANIFEST_FILE);
105
+ if (!existsSync(path))
106
+ return null;
107
+ return JSON.parse(readFileSync(path, "utf8"));
108
+ }
80
109
  function scaffold(options) {
81
110
  const { kind, name, templateDir, version } = options;
82
111
  const cwd = options.cwd ?? process.cwd();
@@ -84,6 +113,23 @@ function scaffold(options) {
84
113
  if (!existsSync(templateDir)) {
85
114
  throw new Error(`Template directory not found: ${templateDir}`);
86
115
  }
116
+ const manifest = loadManifest(templateDir);
117
+ const enabled = (feature) => options.features?.[feature] ?? manifest?.features[feature]?.default ?? true;
118
+ const skip = /* @__PURE__ */ new Set();
119
+ const renames = {};
120
+ if (manifest) {
121
+ for (const [feature, spec] of Object.entries(manifest.features)) {
122
+ if (enabled(feature)) {
123
+ for (const f of spec.disable ?? [])
124
+ skip.add(f);
125
+ for (const [src, dest] of Object.entries(spec.rename ?? {}))
126
+ renames[src] = dest;
127
+ } else {
128
+ for (const f of spec.files ?? [])
129
+ skip.add(f);
130
+ }
131
+ }
132
+ }
87
133
  const names = deriveNames(name, cwd);
88
134
  const inPlace = name === ".";
89
135
  const projectDir = inPlace ? cwd : join(cwd, names.projectName);
@@ -92,17 +138,21 @@ function scaffold(options) {
92
138
  }
93
139
  mkdirSync(projectDir, { recursive: true });
94
140
  for (const srcPath of walk(templateDir)) {
95
- const rel = relative(templateDir, srcPath);
96
- const segments = rel.split(/[/\\]/);
141
+ const rel = relative(templateDir, srcPath).replaceAll("\\", "/");
142
+ if (rel === MANIFEST_FILE)
143
+ continue;
144
+ if (skip.has(rel))
145
+ continue;
146
+ const destRel = renames[rel] ?? rel;
147
+ const segments = destRel.split("/");
97
148
  const last = segments[segments.length - 1];
98
149
  segments[segments.length - 1] = DOTFILE_RENAMES[last] ?? last;
99
- const destRel = segments.join("/");
100
- const destPath = join(projectDir, destRel);
150
+ const destPath = join(projectDir, segments.join("/"));
101
151
  mkdirSync(join(destPath, ".."), { recursive: true });
102
152
  const baseName = segments[segments.length - 1];
103
153
  if (baseName === "package.json") {
104
- const manifest = transformPackageJson(readFileSync(srcPath, "utf8"), names, version);
105
- writeFileSync(destPath, substituteTokens(manifest, names));
154
+ const transformed = transformPackageJson(readFileSync(srcPath, "utf8"), names, version, manifest, enabled);
155
+ writeFileSync(destPath, substituteTokens(transformed, names));
106
156
  } else if (TEXT_REPLACE_EXT.has(extOf(srcPath)) || baseName.startsWith(".")) {
107
157
  writeFileSync(destPath, substituteTokens(readFileSync(srcPath, "utf8"), names));
108
158
  } else {
@@ -111,7 +161,7 @@ function scaffold(options) {
111
161
  }
112
162
  log(`Created ${names.projectName} (${kind}) at ${projectDir}`);
113
163
  if (options.install) {
114
- runInstall(kind, projectDir, log);
164
+ runInstall(kind, projectDir, enabled, log);
115
165
  }
116
166
  return { ...names, projectDir, inPlace };
117
167
  }
@@ -119,14 +169,14 @@ function hasCommand(cmd) {
119
169
  const probe = process.platform === "win32" ? "where" : "which";
120
170
  return spawnSync(probe, [cmd], { stdio: "ignore" }).status === 0;
121
171
  }
122
- function runInstall(kind, projectDir, log) {
172
+ function runInstall(kind, projectDir, enabled, log) {
123
173
  log("Installing Node dependencies (npm install)...");
124
174
  const npm = spawnSync("npm", ["install"], { cwd: projectDir, stdio: "inherit", shell: process.platform === "win32" });
125
175
  if (npm.status !== 0) {
126
176
  log("npm install failed \u2014 fix the issue and re-run `npm install`.");
127
177
  return;
128
178
  }
129
- if (kind === "e3") {
179
+ if (kind === "e3" && enabled("runner:east-py")) {
130
180
  if (hasCommand("uv")) {
131
181
  log("Installing Python dependencies (uv sync)...");
132
182
  const uv = spawnSync("uv", ["sync"], { cwd: projectDir, stdio: "inherit", shell: process.platform === "win32" });
@@ -139,15 +189,15 @@ function runInstall(kind, projectDir, log) {
139
189
  }
140
190
 
141
191
  // ../scaffold-core/dist/cli.js
142
- import { readFileSync as readFileSync2 } from "node:fs";
192
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
143
193
  import { dirname, join as join2 } from "node:path";
194
+ import { createInterface } from "node:readline/promises";
144
195
  import { fileURLToPath } from "node:url";
145
- function runCreateCli(kind, moduleUrl) {
196
+ async function runCreateCli(kind, moduleUrl) {
146
197
  const pkgRoot = join2(dirname(fileURLToPath(moduleUrl)), "..");
147
198
  const args = process.argv.slice(2);
148
199
  if (args.includes("--help") || args.includes("-h")) {
149
- console.log(`Usage: npm create @elaraai/${kind} <project-name> [-- --install|--no-install]`);
150
- console.log(` create-${kind} <project-name|.> [--install|--no-install]`);
200
+ printHelp(kind);
151
201
  return;
152
202
  }
153
203
  const version = JSON.parse(readFileSync2(join2(pkgRoot, "package.json"), "utf8")).version;
@@ -155,22 +205,111 @@ function runCreateCli(kind, moduleUrl) {
155
205
  const name = args.find((a) => !a.startsWith("-")) ?? ".";
156
206
  const install = args.includes("--install") ? true : args.includes("--no-install") ? false : Boolean(process.stdout.isTTY);
157
207
  try {
158
- const result = scaffold({ kind, name, templateDir, version, install });
159
- printNextSteps(kind, result.projectName, result.inPlace, install);
208
+ const features = await resolveFeatures(templateDir, args);
209
+ const result = scaffold({ kind, name, templateDir, version, install, features });
210
+ printNextSteps(kind, result.projectName, result.inPlace, install, features);
160
211
  } catch (err) {
161
212
  console.error(`error: ${err instanceof Error ? err.message : String(err)}`);
162
213
  process.exit(1);
163
214
  }
164
215
  }
165
- function printNextSteps(kind, projectName, inPlace, installed) {
216
+ async function resolveFeatures(templateDir, args) {
217
+ const manifestPath = join2(templateDir, "template.json");
218
+ if (!existsSync2(manifestPath))
219
+ return {};
220
+ const manifest = JSON.parse(readFileSync2(manifestPath, "utf8"));
221
+ const features = {};
222
+ for (const [key, spec] of Object.entries(manifest.features))
223
+ features[key] = spec.default ?? true;
224
+ const runnerKeys = Object.keys(manifest.features).filter((k) => k.startsWith("runner:"));
225
+ const runnerName = (key) => key.slice("runner:".length);
226
+ const runnersFlag = args.find((a) => a.startsWith("--runners="));
227
+ const selectionFlags = ["--tests", "--no-tests", "--ui", "--no-ui", "--eslint", "--no-eslint"].some((f) => args.includes(f)) || Boolean(runnersFlag);
228
+ if (args.includes("--tests"))
229
+ features["tests"] = true;
230
+ if (args.includes("--no-tests"))
231
+ features["tests"] = false;
232
+ if (args.includes("--ui"))
233
+ features["ui"] = true;
234
+ if (args.includes("--no-ui"))
235
+ features["ui"] = false;
236
+ if (args.includes("--eslint"))
237
+ features["eslint"] = true;
238
+ if (args.includes("--no-eslint"))
239
+ features["eslint"] = false;
240
+ if (args.includes("--editor-diagnostics"))
241
+ features["editor-diagnostics"] = true;
242
+ if (args.includes("--no-editor-diagnostics"))
243
+ features["editor-diagnostics"] = false;
244
+ if (runnersFlag) {
245
+ const chosen = new Set(runnersFlag.slice("--runners=".length).split(",").map((s) => s.trim()).filter(Boolean));
246
+ for (const key of runnerKeys)
247
+ features[key] = chosen.has(runnerName(key));
248
+ }
249
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY) && !selectionFlags;
250
+ if (!interactive)
251
+ return features;
252
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
253
+ try {
254
+ if ("tests" in manifest.features) {
255
+ features["tests"] = await askYesNo(rl, "Include tests?", features["tests"]);
256
+ }
257
+ if ("ui" in manifest.features) {
258
+ features["ui"] = await askYesNo(rl, "Include UI components (east-ui + e3-ui)?", features["ui"]);
259
+ }
260
+ if (runnerKeys.length > 0) {
261
+ const defaults = runnerKeys.filter((k) => features[k]).map(runnerName);
262
+ const answer = (await rl.question(`Runners to include (comma-separated) [${defaults.join(",")}]: `)).trim();
263
+ const picks = new Set(answer ? answer.split(",").map((s) => s.trim()).filter(Boolean) : defaults);
264
+ for (const key of runnerKeys)
265
+ features[key] = picks.has(runnerName(key));
266
+ }
267
+ if ("eslint" in manifest.features) {
268
+ features["eslint"] = await askYesNo(rl, "Include ESLint with the East lint rules?", features["eslint"]);
269
+ }
270
+ if ("editor-diagnostics" in manifest.features) {
271
+ features["editor-diagnostics"] = await askYesNo(rl, "Include East editor diagnostics (TypeScript language service plugin)?", features["editor-diagnostics"]);
272
+ }
273
+ } finally {
274
+ rl.close();
275
+ }
276
+ return features;
277
+ }
278
+ async function askYesNo(rl, question, def) {
279
+ const answer = (await rl.question(`${question} [${def ? "Y/n" : "y/N"}] `)).trim().toLowerCase();
280
+ if (answer === "")
281
+ return def;
282
+ return answer.startsWith("y");
283
+ }
284
+ function printHelp(kind) {
285
+ console.log(`Usage: npm create @elaraai/${kind} <project-name> [-- <options>]`);
286
+ console.log(` create-${kind} <project-name|.> [options]`);
287
+ console.log("");
288
+ console.log("Options:");
289
+ console.log(" --install | --no-install install dependencies after scaffolding (default: TTY)");
290
+ console.log(" --eslint | --no-eslint include ESLint with the East lint rules (default: yes)");
291
+ console.log(" --editor-diagnostics | --no-editor-diagnostics include the East tsserver plugin for editor squiggles (default: yes)");
292
+ if (kind === "e3") {
293
+ console.log(" --tests | --no-tests include test files (default: yes)");
294
+ console.log(" --ui | --no-ui include east-ui + e3-ui UI components (default: no)");
295
+ console.log(" --runners=east-node,east-c,east-py East runtimes to include (default: all)");
296
+ }
297
+ console.log("");
298
+ console.log("Run interactively (a TTY with no feature flags) to be prompted for these.");
299
+ }
300
+ function printNextSteps(kind, projectName, inPlace, installed, features) {
166
301
  console.log("");
167
302
  console.log("Next steps:");
168
303
  if (!inPlace)
169
304
  console.log(` cd ${projectName}`);
170
- if (!installed)
171
- console.log(kind === "e3" ? " npm run setup" : " npm install");
305
+ if (!installed) {
306
+ if (kind === "e3")
307
+ console.log(features["runner:east-py"] === false ? " npm install" : " npm run setup");
308
+ else
309
+ console.log(" npm install");
310
+ }
172
311
  console.log(kind === "e3" ? " npm run start" : " npm run test");
173
312
  }
174
313
 
175
314
  // src/index.ts
176
- runCreateCli("e3", import.meta.url);
315
+ await runCreateCli("e3", import.meta.url);
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@elaraai/create-e3",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Scaffold a new e3 project (BSL-1.1, Node + Python, durable execution): npm create @elaraai/e3",
5
5
  "type": "module",
6
6
  "bin": {
7
- "create-e3": "./dist/index.js"
7
+ "create-e3": "./bin/create-e3.mjs"
8
8
  },
9
9
  "files": [
10
+ "bin",
10
11
  "dist",
11
12
  "templates"
12
13
  ],
@@ -1,26 +1,44 @@
1
1
  # __DISPLAY_NAME__
2
2
 
3
- e3 project (BSL-1.1) — Node + Python, durable dataflow execution.
3
+ An e3 project (BSL-1.1) — typed East logic, durable dataflow execution.
4
+
5
+ East turns inputs and logic into **decisions**. This starter ships one: `reorder_qty`
6
+ in `src/index.ts` recommends how many units to reorder to bring stock (`on_hand`) up to
7
+ its target (`reorder_to`), never negative. Edit it, add inputs and tasks, and grow the
8
+ package from there.
4
9
 
5
10
  ## Setup
6
11
 
7
12
  ```bash
8
- npm run setup # npm install + uv sync (Node and Python deps)
13
+ npm run setup # install dependencies (npm install, plus `uv sync` if you kept the Python runner)
14
+ ```
15
+
16
+ ## Run
17
+
18
+ ```bash
19
+ npm run start # deploy from ./src/index.ts, then run the dataflow once
20
+ npm run watch # re-deploy and re-run on every save
21
+ npm run deploy # create the repo (if needed) and deploy without running
9
22
  ```
10
23
 
11
- ## Commands
24
+ The package is the default export of `src/index.ts`; the e3 CLI deploys it straight from source.
25
+
26
+ ## Test
12
27
 
13
28
  ```bash
14
29
  npm run build # compile TypeScript
15
- npm run test # build, export IR, run TS + Python tests
16
- npm run test:ts # TypeScript tests only
17
- npm run test:py # Python tests only (needs IR exported first)
18
- npm run deploy # create repo (if needed) + deploy from ./src/index.ts
19
- npm run start # deploy, then run the dataflow once
20
- npm run watch # auto-deploy + run on every save
30
+ npm run test # build, export the IR, and run the tests
31
+ ```
32
+
33
+ `npm run test` is present if you scaffolded with tests. With the Python (east-py) runner it also
34
+ runs the exported IR through the Python runtime; without it, tests run on Node only.
35
+
36
+ ## Other
37
+
38
+ ```bash
21
39
  npm run lint # lint sources
22
- npm run clean # remove build output, venv, repo, dependencies
40
+ npm run clean # remove build output, dependencies, and the local repo
23
41
  ```
24
42
 
25
- The package is defined in `src/index.ts` as the default export; `npm run
26
- deploy`/`start`/`watch` deploy it straight from source via the e3 CLI.
43
+ If you scaffolded with UI, `src/surface.tsx` holds a decision surface — a `ui()` task an operator
44
+ uses to observe and act on the recommendation, registered in the package next to the decision.
@@ -1,15 +1,23 @@
1
1
  import tseslint from "@typescript-eslint/eslint-plugin";
2
2
  import tsparser from "@typescript-eslint/parser";
3
+ import east from "@elaraai/eslint-plugin-east";
3
4
 
4
5
  export default [
5
6
  { ignores: ["dist/", "node_modules/", ".venv/"] },
6
7
  {
7
- files: ["src/**/*.ts"],
8
+ files: ["src/**/*.ts", "src/**/*.tsx"],
8
9
  languageOptions: {
9
10
  parser: tsparser,
10
- parserOptions: { ecmaVersion: "latest", sourceType: "module" },
11
+ // Type-aware linting: the East rules read the TypeScript program, so the
12
+ // parser must build it (`projectService` discovers the nearest tsconfig).
13
+ parserOptions: {
14
+ projectService: true,
15
+ tsconfigRootDir: import.meta.dirname,
16
+ },
11
17
  },
12
- plugins: { "@typescript-eslint": tseslint },
13
- rules: {},
18
+ plugins: { "@typescript-eslint": tseslint, east },
19
+ // One rule runs the whole East idiom diagnostic set (prefer some()/none, no
20
+ // hand-rolled variants, prefer the <Tag> over Tag.Root(...), etc.).
21
+ rules: { "east/east-rules": "warn" },
14
22
  },
15
23
  ];
@@ -23,15 +23,21 @@
23
23
  "@elaraai/east-node-io": "workspace:*",
24
24
  "@elaraai/east-py-datascience": "workspace:*",
25
25
  "@elaraai/e3": "workspace:*",
26
- "@elaraai/e3-types": "workspace:*"
26
+ "@elaraai/e3-types": "workspace:*",
27
+ "@elaraai/east-ui": "workspace:*",
28
+ "@elaraai/e3-ui": "workspace:*"
27
29
  },
28
30
  "devDependencies": {
29
31
  "@elaraai/e3-cli": "workspace:*",
32
+ "@elaraai/east-node-cli": "workspace:*",
33
+ "@elaraai/east-c-cli": "workspace:*",
30
34
  "@types/node": "^22",
31
35
  "typescript": "^5",
32
36
  "eslint": "^9",
33
37
  "@typescript-eslint/eslint-plugin": "^8",
34
38
  "@typescript-eslint/parser": "^8",
39
+ "@elaraai/eslint-plugin-east": "workspace:*",
40
+ "@elaraai/tsserver-plugin-east": "workspace:*",
35
41
  "cross-env": "^7",
36
42
  "rimraf": "^6"
37
43
  },
@@ -5,9 +5,8 @@ requires-python = ">=3.11"
5
5
  version = "0.1.0"
6
6
  dependencies = [
7
7
  "elaraai-east-py",
8
- # Uncomment once these are published to PyPI:
9
- # "elaraai-east-py-std",
10
- # "elaraai-east-py-io",
8
+ "elaraai-east-py-std",
9
+ "elaraai-east-py-io",
11
10
  "elaraai-east-py-datascience",
12
11
  "elaraai-east-py-cli",
13
12
  "pytest",
@@ -1,15 +1,15 @@
1
1
  import { East } from "@elaraai/east";
2
2
  import { describeEast, Assert } from "@elaraai/east-node-std";
3
- import { greetFn } from "./index.js";
3
+ import { reorderFn } from "./index.js";
4
4
 
5
5
  describeEast("__DISPLAY_NAME__", (test) => {
6
- test("greet returns greeting message", ($) => {
7
- const result = $.let(greetFn("World"));
8
- $(Assert.equal(result, East.value("Hello, World!")));
6
+ test("reorders up to the target when below it", ($) => {
7
+ const qty = $.let(reorderFn(12n, 50n));
8
+ $(Assert.equal(qty, East.value(38n)));
9
9
  });
10
10
 
11
- test("greet with custom name", ($) => {
12
- const result = $.let(greetFn("East"));
13
- $(Assert.equal(result, East.value("Hello, East!")));
11
+ test("never recommends a negative reorder", ($) => {
12
+ const qty = $.let(reorderFn(80n, 50n));
13
+ $(Assert.equal(qty, East.value(0n)));
14
14
  });
15
15
  }, { exportOnly: true });
@@ -1,14 +1,20 @@
1
1
  import e3 from "@elaraai/e3";
2
- import { East, StringType } from "@elaraai/east";
2
+ import { East, IntegerType } from "@elaraai/east";
3
3
 
4
- export const nameInput = e3.input("name", StringType, "World!");
4
+ // In East a decision is a typed task over inputs. This one recommends how many
5
+ // units to reorder to bring stock up to its target level — never negative.
6
+ export const onHandInput = e3.input("on_hand", IntegerType, 12n);
7
+ export const targetInput = e3.input("reorder_to", IntegerType, 50n);
5
8
 
6
- export const greetFn = East.function(
7
- [StringType],
8
- StringType,
9
- ($, name) => East.str`Hello, ${name}!`,
9
+ export const reorderFn = East.function(
10
+ [IntegerType, IntegerType],
11
+ IntegerType,
12
+ ($, onHand, target) => {
13
+ const gap = $.let(target.subtract(onHand));
14
+ $.return(East.greater(gap, 0n).ifElse(() => gap, () => 0n));
15
+ },
10
16
  );
11
17
 
12
- export const greet = e3.task("greet", [nameInput], greetFn);
18
+ export const reorderQty = e3.task("reorder_qty", [onHandInput, targetInput], reorderFn);
13
19
 
14
- export default e3.package("__PROJECT_NAME__", "1.0.0", greet);
20
+ export default e3.package("__PROJECT_NAME__", "1.0.0", reorderQty);
@@ -0,0 +1,22 @@
1
+ import e3 from "@elaraai/e3";
2
+ import { East, IntegerType } from "@elaraai/east";
3
+
4
+ import { surface } from "./surface.js";
5
+
6
+ // In East a decision is a typed task over inputs. This one recommends how many
7
+ // units to reorder to bring stock up to its target level — never negative.
8
+ export const onHandInput = e3.input("on_hand", IntegerType, 12n);
9
+ export const targetInput = e3.input("reorder_to", IntegerType, 50n);
10
+
11
+ export const reorderFn = East.function(
12
+ [IntegerType, IntegerType],
13
+ IntegerType,
14
+ ($, onHand, target) => {
15
+ const gap = $.let(target.subtract(onHand));
16
+ $.return(East.greater(gap, 0n).ifElse(() => gap, () => 0n));
17
+ },
18
+ );
19
+
20
+ export const reorderQty = e3.task("reorder_qty", [onHandInput, targetInput], reorderFn);
21
+
22
+ export default e3.package("__PROJECT_NAME__", "1.0.0", reorderQty, surface);
@@ -0,0 +1,16 @@
1
+ import { East } from "@elaraai/east";
2
+ import { ui } from "@elaraai/e3-ui";
3
+ import { Box, Text, UIComponentType } from "@elaraai/east-ui";
4
+
5
+ // A decision surface for an operator to observe and act on the recommendation.
6
+ // east-ui / e3-ui JSX authoring is still being finalised — build this out: bind
7
+ // reorderQty and the inputs with Data.bind, then add the Observe / Decide controls.
8
+ export const surface = ui(
9
+ "surface",
10
+ [],
11
+ East.function([], UIComponentType, (_$) => (
12
+ <Box padding="6">
13
+ <Text textStyle="heading-md">__DISPLAY_NAME__</Text>
14
+ </Box>
15
+ )),
16
+ );
@@ -0,0 +1,55 @@
1
+ {
2
+ "features": {
3
+ "tests": {
4
+ "default": true,
5
+ "files": ["src/index.spec.ts", "tests/test_unit.py"],
6
+ "scripts": ["test:ts", "test:export", "test:py"]
7
+ },
8
+ "ui": {
9
+ "default": false,
10
+ "files": ["src/surface.tsx", "src/index.ui.ts"],
11
+ "disable": ["src/index.ts"],
12
+ "rename": { "src/index.ui.ts": "src/index.ts" },
13
+ "dependencies": ["@elaraai/east-ui", "@elaraai/e3-ui"]
14
+ },
15
+ "runner:east-node": {
16
+ "default": true,
17
+ "devDependencies": ["@elaraai/east-node-cli"]
18
+ },
19
+ "runner:east-c": {
20
+ "default": true,
21
+ "devDependencies": ["@elaraai/east-c-cli"]
22
+ },
23
+ "runner:east-py": {
24
+ "default": true,
25
+ "files": ["pyproject.toml", ".python-version", "tests/test_unit.py"],
26
+ "scripts": ["test:py"],
27
+ "dependencies": ["@elaraai/east-py-datascience"]
28
+ },
29
+ "eslint": {
30
+ "default": true,
31
+ "files": ["eslint.config.js"],
32
+ "scripts": ["lint"],
33
+ "devDependencies": [
34
+ "eslint",
35
+ "@typescript-eslint/eslint-plugin",
36
+ "@typescript-eslint/parser",
37
+ "@elaraai/eslint-plugin-east"
38
+ ]
39
+ },
40
+ "editor-diagnostics": {
41
+ "default": true,
42
+ "devDependencies": ["@elaraai/tsserver-plugin-east"]
43
+ }
44
+ },
45
+ "scriptVariants": {
46
+ "test": [
47
+ { "when": ["tests", "runner:east-py"], "value": "npm run build && npm run test:export && npm run test:py" },
48
+ { "when": ["tests"], "value": "npm run build && npm run test:export" }
49
+ ],
50
+ "setup": [
51
+ { "when": ["runner:east-py"], "value": "npm install && uv sync" },
52
+ { "when": [], "value": "npm install" }
53
+ ]
54
+ }
55
+ }
@@ -2,6 +2,7 @@
2
2
  "exclude": ["dist"],
3
3
  "compilerOptions": {
4
4
  "outDir": "./dist",
5
+ "rootDir": "./src",
5
6
  "module": "nodenext",
6
7
  "target": "esnext",
7
8
  "lib": ["esnext", "es2024"],
@@ -13,12 +14,14 @@
13
14
  "exactOptionalPropertyTypes": true,
14
15
  "strict": true,
15
16
  "jsx": "react-jsx",
17
+ "jsxImportSource": "@elaraai/east-ui",
16
18
  "verbatimModuleSyntax": true,
17
19
  "isolatedModules": true,
18
20
  "noUncheckedSideEffectImports": true,
19
21
  "moduleDetection": "force",
20
22
  "skipLibCheck": true,
21
23
  "noErrorTruncation": true,
22
- "incremental": true
24
+ "incremental": true,
25
+ "plugins": [{ "name": "@elaraai/tsserver-plugin-east" }]
23
26
  }
24
27
  }