@mmapp/react-compiler 0.1.0-alpha.6 → 0.1.0-alpha.9
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/dist/auth-3UK75242.mjs +17 -0
- package/dist/babel/index.d.mts +2 -2
- package/dist/babel/index.d.ts +2 -2
- package/dist/babel/index.js +5 -5
- package/dist/babel/index.mjs +1 -1
- package/dist/chunk-5FTDWKHH.mjs +244 -0
- package/dist/chunk-7JRAEFRB.mjs +7510 -0
- package/dist/chunk-7T6Q5KAA.mjs +7506 -0
- package/dist/chunk-ABYPKRSB.mjs +215 -0
- package/dist/chunk-BZEXUPDH.mjs +175 -0
- package/dist/chunk-HRYR54PT.mjs +175 -0
- package/dist/chunk-J3M4GUS7.mjs +161 -0
- package/dist/chunk-JRGFBWTN.mjs +2918 -0
- package/dist/chunk-O4AUS7EU.mjs +148 -0
- package/dist/chunk-R2DD5GTY.mjs +186 -0
- package/dist/chunk-UDDTWG5J.mjs +734 -0
- package/dist/chunk-VLTKQDJ3.mjs +244 -0
- package/dist/chunk-WVYY32LD.mjs +939 -0
- package/dist/chunk-XDVM4YHX.mjs +3450 -0
- package/dist/chunk-Z2G5RZ4H.mjs +186 -0
- package/dist/chunk-ZE3KCHBM.mjs +2918 -0
- package/dist/cli/index.js +4350 -3206
- package/dist/cli/index.mjs +77 -179
- package/dist/codemod/cli.js +1 -1
- package/dist/codemod/cli.mjs +1 -1
- package/dist/codemod/index.d.mts +3 -3
- package/dist/codemod/index.d.ts +3 -3
- package/dist/codemod/index.js +1 -1
- package/dist/codemod/index.mjs +1 -1
- package/dist/deploy-YAJGW6II.mjs +9 -0
- package/dist/dev-server-CrQ041KP.d.mts +79 -0
- package/dist/dev-server-CrQ041KP.d.ts +79 -0
- package/dist/dev-server.d.mts +2 -2
- package/dist/dev-server.d.ts +2 -2
- package/dist/dev-server.js +1218 -86
- package/dist/dev-server.mjs +4 -4
- package/dist/envelope-ChEkuHij.d.mts +265 -0
- package/dist/envelope-ChEkuHij.d.ts +265 -0
- package/dist/envelope.d.mts +2 -2
- package/dist/envelope.d.ts +2 -2
- package/dist/envelope.js +5 -5
- package/dist/envelope.mjs +2 -2
- package/dist/index-CEKyyazf.d.mts +104 -0
- package/dist/index-CEKyyazf.d.ts +104 -0
- package/dist/index.d.mts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +1266 -342
- package/dist/index.mjs +8 -8
- package/dist/init-7FJENUDK.mjs +407 -0
- package/dist/project-compiler-NNK32MPG.mjs +10 -0
- package/dist/project-compiler-ZB4RUYVL.mjs +10 -0
- package/dist/project-decompiler-U55HQUHW.mjs +7 -0
- package/dist/pull-KOL2QAYQ.mjs +109 -0
- package/dist/seed-KOGEPGOJ.mjs +154 -0
- package/dist/server-VW6UPCHO.mjs +277 -0
- package/dist/testing/index.d.mts +8 -8
- package/dist/testing/index.d.ts +8 -8
- package/dist/testing/index.js +5 -5
- package/dist/testing/index.mjs +1 -1
- package/dist/verify-BYHUKARQ.mjs +1833 -0
- package/dist/verify-OQDEQYMS.mjs +1833 -0
- package/dist/vite/index.d.mts +1 -1
- package/dist/vite/index.d.ts +1 -1
- package/dist/vite/index.js +5 -5
- package/dist/vite/index.mjs +2 -2
- package/examples/authentication/main.workflow.tsx +1 -1
- package/examples/authentication/mm.config.ts +1 -1
- package/examples/authentication/pages/LoginPage.tsx +2 -2
- package/examples/authentication/pages/SignupPage.tsx +2 -2
- package/examples/counter.workflow.tsx +1 -1
- package/examples/dashboard.workflow.tsx +1 -1
- package/examples/invoice-approval/actions/invoice.server.ts +1 -1
- package/examples/invoice-approval/main.workflow.tsx +1 -1
- package/examples/invoice-approval/mm.config.ts +1 -1
- package/examples/invoice-approval/pages/InvoiceDetailPage.tsx +1 -1
- package/examples/invoice-approval/pages/InvoiceFormPage.tsx +1 -1
- package/examples/invoice-approval/pages/InvoiceListPage.tsx +1 -1
- package/examples/todo-app.workflow.tsx +1 -1
- package/examples/uber-app/actions/matching.server.ts +1 -1
- package/examples/uber-app/actions/notifications.server.ts +1 -1
- package/examples/uber-app/actions/payments.server.ts +1 -1
- package/examples/uber-app/actions/pricing.server.ts +1 -1
- package/examples/uber-app/app/admin/analytics.tsx +2 -2
- package/examples/uber-app/app/admin/fleet.tsx +2 -2
- package/examples/uber-app/app/admin/surge-pricing.tsx +2 -2
- package/examples/uber-app/app/driver/dashboard.tsx +2 -2
- package/examples/uber-app/app/driver/earnings.tsx +2 -2
- package/examples/uber-app/app/driver/navigation.tsx +2 -2
- package/examples/uber-app/app/driver/ride-acceptance.tsx +2 -2
- package/examples/uber-app/app/rider/home.tsx +2 -2
- package/examples/uber-app/app/rider/payment-methods.tsx +2 -2
- package/examples/uber-app/app/rider/ride-history.tsx +2 -2
- package/examples/uber-app/app/rider/ride-tracking.tsx +2 -2
- package/examples/uber-app/components/DriverCard.tsx +1 -1
- package/examples/uber-app/components/MapView.tsx +3 -3
- package/examples/uber-app/components/RatingStars.tsx +2 -2
- package/examples/uber-app/components/RideCard.tsx +1 -1
- package/examples/uber-app/mm.config.ts +1 -1
- package/examples/uber-app/workflows/dispute-resolution.workflow.tsx +2 -2
- package/examples/uber-app/workflows/driver-onboarding.workflow.tsx +2 -2
- package/examples/uber-app/workflows/payment-processing.workflow.tsx +2 -2
- package/examples/uber-app/workflows/ride-request.workflow.tsx +2 -2
- package/package.json +9 -13
package/dist/index.js
CHANGED
|
@@ -2680,7 +2680,7 @@ var import_player_core, PROP_RULES, COMPONENT_RENAMES, RENAME_EXTRA_CONFIG, emit
|
|
|
2680
2680
|
var init_pure_form_emitter = __esm({
|
|
2681
2681
|
"src/babel/emitters/pure-form-emitter.ts"() {
|
|
2682
2682
|
"use strict";
|
|
2683
|
-
import_player_core = require("@
|
|
2683
|
+
import_player_core = require("@mmapp/player-core");
|
|
2684
2684
|
init_transition_extractor();
|
|
2685
2685
|
init_experience_transform();
|
|
2686
2686
|
PROP_RULES = {
|
|
@@ -5220,7 +5220,7 @@ function extractImperativeWorkflow(path, state) {
|
|
|
5220
5220
|
const source = node.source.value;
|
|
5221
5221
|
for (const spec of node.specifiers) {
|
|
5222
5222
|
const localName = spec.local.name;
|
|
5223
|
-
if (source.includes("@mmapp/react") || source.includes("@
|
|
5223
|
+
if (source.includes("@mmapp/react") || source.includes("@mmapp/react")) {
|
|
5224
5224
|
stdLibImports.add(localName);
|
|
5225
5225
|
} else {
|
|
5226
5226
|
importedFns.set(localName, source);
|
|
@@ -7413,7 +7413,7 @@ function createVisitor(options = {}) {
|
|
|
7413
7413
|
if (mode === "strict" && STRICT_BANNED_HOOKS[hookName]) {
|
|
7414
7414
|
const error = {
|
|
7415
7415
|
code: STRICT_BANNED_HOOKS[hookName],
|
|
7416
|
-
message: `${hookName}() is not allowed in strict mode. Use @
|
|
7416
|
+
message: `${hookName}() is not allowed in strict mode. Use @mmapp/react effect hooks instead.`,
|
|
7417
7417
|
line: path.node.loc?.start.line,
|
|
7418
7418
|
column: path.node.loc?.start.column,
|
|
7419
7419
|
severity: "error"
|
|
@@ -7505,12 +7505,12 @@ function createVisitor(options = {}) {
|
|
|
7505
7505
|
}
|
|
7506
7506
|
}
|
|
7507
7507
|
if (mode !== "strict") return;
|
|
7508
|
-
if (source.startsWith("@
|
|
7508
|
+
if (source.startsWith("@mmapp/") || source.startsWith("@mmapp/") || source === "react" || source.startsWith("react/") || source.startsWith(".") || source.startsWith("/")) {
|
|
7509
7509
|
return;
|
|
7510
7510
|
}
|
|
7511
7511
|
const error = {
|
|
7512
7512
|
code: "STRICT_FORBIDDEN_IMPORT",
|
|
7513
|
-
message: `Import from '${source}' is not allowed in strict mode. Only @
|
|
7513
|
+
message: `Import from '${source}' is not allowed in strict mode. Only @mmapp/* and relative imports are permitted.`,
|
|
7514
7514
|
line: path.node.loc?.start.line,
|
|
7515
7515
|
column: path.node.loc?.start.column,
|
|
7516
7516
|
severity: "error"
|
|
@@ -10616,6 +10616,552 @@ var init_project_compiler = __esm({
|
|
|
10616
10616
|
}
|
|
10617
10617
|
});
|
|
10618
10618
|
|
|
10619
|
+
// src/cli/auth.ts
|
|
10620
|
+
var auth_exports = {};
|
|
10621
|
+
__export(auth_exports, {
|
|
10622
|
+
loadCredentials: () => loadCredentials,
|
|
10623
|
+
login: () => login,
|
|
10624
|
+
logout: () => logout,
|
|
10625
|
+
resolveToken: () => resolveToken,
|
|
10626
|
+
saveCredentials: () => saveCredentials,
|
|
10627
|
+
whoami: () => whoami
|
|
10628
|
+
});
|
|
10629
|
+
function loadCredentials() {
|
|
10630
|
+
if (!(0, import_fs4.existsSync)(CREDENTIALS_PATH)) return {};
|
|
10631
|
+
try {
|
|
10632
|
+
return JSON.parse((0, import_fs4.readFileSync)(CREDENTIALS_PATH, "utf-8"));
|
|
10633
|
+
} catch {
|
|
10634
|
+
return {};
|
|
10635
|
+
}
|
|
10636
|
+
}
|
|
10637
|
+
function saveCredentials(apiUrl, token) {
|
|
10638
|
+
(0, import_fs4.mkdirSync)(MMRC_DIR, { recursive: true, mode: 448 });
|
|
10639
|
+
const creds = loadCredentials();
|
|
10640
|
+
creds[apiUrl] = { token, savedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
10641
|
+
(0, import_fs4.writeFileSync)(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), "utf-8");
|
|
10642
|
+
try {
|
|
10643
|
+
const { chmodSync } = require("fs");
|
|
10644
|
+
chmodSync(CREDENTIALS_PATH, 384);
|
|
10645
|
+
} catch {
|
|
10646
|
+
}
|
|
10647
|
+
}
|
|
10648
|
+
function removeCredentials(apiUrl) {
|
|
10649
|
+
if (!(0, import_fs4.existsSync)(CREDENTIALS_PATH)) return;
|
|
10650
|
+
if (!apiUrl) {
|
|
10651
|
+
(0, import_fs4.unlinkSync)(CREDENTIALS_PATH);
|
|
10652
|
+
return;
|
|
10653
|
+
}
|
|
10654
|
+
const creds = loadCredentials();
|
|
10655
|
+
delete creds[apiUrl];
|
|
10656
|
+
if (Object.keys(creds).length === 0) {
|
|
10657
|
+
(0, import_fs4.unlinkSync)(CREDENTIALS_PATH);
|
|
10658
|
+
} else {
|
|
10659
|
+
(0, import_fs4.writeFileSync)(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), "utf-8");
|
|
10660
|
+
}
|
|
10661
|
+
}
|
|
10662
|
+
function resolveToken(apiUrl, explicitToken) {
|
|
10663
|
+
if (explicitToken) return explicitToken;
|
|
10664
|
+
const envToken = process.env.MMRC_TOKEN;
|
|
10665
|
+
if (envToken) return envToken;
|
|
10666
|
+
const creds = loadCredentials();
|
|
10667
|
+
const entry = creds[apiUrl];
|
|
10668
|
+
if (entry?.token) return entry.token;
|
|
10669
|
+
return null;
|
|
10670
|
+
}
|
|
10671
|
+
function prompt(question, hidden = false) {
|
|
10672
|
+
return new Promise((resolve3) => {
|
|
10673
|
+
const rl = (0, import_readline.createInterface)({
|
|
10674
|
+
input: process.stdin,
|
|
10675
|
+
output: process.stdout
|
|
10676
|
+
});
|
|
10677
|
+
if (hidden && process.stdin.isTTY) {
|
|
10678
|
+
const origWrite = process.stdout.write.bind(process.stdout);
|
|
10679
|
+
process.stdout.write = ((chunk, encodingOrCb, cb) => {
|
|
10680
|
+
if (typeof chunk === "string" && chunk.includes(question)) {
|
|
10681
|
+
return origWrite(chunk, encodingOrCb, cb);
|
|
10682
|
+
}
|
|
10683
|
+
if (typeof chunk === "string" && !chunk.includes("\n")) {
|
|
10684
|
+
return true;
|
|
10685
|
+
}
|
|
10686
|
+
return origWrite(chunk, encodingOrCb, cb);
|
|
10687
|
+
});
|
|
10688
|
+
rl.question(question, (answer) => {
|
|
10689
|
+
process.stdout.write = origWrite;
|
|
10690
|
+
console.log();
|
|
10691
|
+
rl.close();
|
|
10692
|
+
resolve3(answer.trim());
|
|
10693
|
+
});
|
|
10694
|
+
} else {
|
|
10695
|
+
rl.question(question, (answer) => {
|
|
10696
|
+
rl.close();
|
|
10697
|
+
resolve3(answer.trim());
|
|
10698
|
+
});
|
|
10699
|
+
}
|
|
10700
|
+
});
|
|
10701
|
+
}
|
|
10702
|
+
async function login(apiUrl, email, password) {
|
|
10703
|
+
if (!email) {
|
|
10704
|
+
email = await prompt("Email: ");
|
|
10705
|
+
}
|
|
10706
|
+
if (!password) {
|
|
10707
|
+
password = await prompt("Password: ", true);
|
|
10708
|
+
}
|
|
10709
|
+
if (!email || !password) {
|
|
10710
|
+
throw new Error("Email and password are required");
|
|
10711
|
+
}
|
|
10712
|
+
console.log(`[mmrc] Logging in to ${apiUrl}...`);
|
|
10713
|
+
const res = await fetch(`${apiUrl}/auth/login`, {
|
|
10714
|
+
method: "POST",
|
|
10715
|
+
headers: { "Content-Type": "application/json" },
|
|
10716
|
+
body: JSON.stringify({ email, password })
|
|
10717
|
+
});
|
|
10718
|
+
if (!res.ok) {
|
|
10719
|
+
const text = await res.text();
|
|
10720
|
+
throw new Error(`Login failed: ${res.status} ${text}`);
|
|
10721
|
+
}
|
|
10722
|
+
const data = await res.json();
|
|
10723
|
+
const token = data.token ?? data.access_token ?? data.accessToken;
|
|
10724
|
+
if (!token) {
|
|
10725
|
+
throw new Error("Login succeeded but no token in response");
|
|
10726
|
+
}
|
|
10727
|
+
saveCredentials(apiUrl, token);
|
|
10728
|
+
const userName = data.name ?? data.user?.name ?? data.user?.email ?? email;
|
|
10729
|
+
console.log(`[mmrc] Logged in as ${userName}`);
|
|
10730
|
+
console.log(`[mmrc] Token saved to ${CREDENTIALS_PATH}`);
|
|
10731
|
+
return token;
|
|
10732
|
+
}
|
|
10733
|
+
function logout(apiUrl) {
|
|
10734
|
+
if (apiUrl) {
|
|
10735
|
+
removeCredentials(apiUrl);
|
|
10736
|
+
console.log(`[mmrc] Credentials removed for ${apiUrl}`);
|
|
10737
|
+
} else {
|
|
10738
|
+
removeCredentials();
|
|
10739
|
+
console.log("[mmrc] All saved credentials removed");
|
|
10740
|
+
}
|
|
10741
|
+
}
|
|
10742
|
+
async function whoami(apiUrl) {
|
|
10743
|
+
const token = resolveToken(apiUrl);
|
|
10744
|
+
if (!token) {
|
|
10745
|
+
console.log("[mmrc] Not logged in. Run `mmrc login` to authenticate.");
|
|
10746
|
+
return;
|
|
10747
|
+
}
|
|
10748
|
+
try {
|
|
10749
|
+
const res = await fetch(`${apiUrl}/auth/me`, {
|
|
10750
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
10751
|
+
});
|
|
10752
|
+
if (!res.ok) {
|
|
10753
|
+
if (res.status === 401) {
|
|
10754
|
+
console.log("[mmrc] Token is expired or invalid. Run `mmrc login` to re-authenticate.");
|
|
10755
|
+
return;
|
|
10756
|
+
}
|
|
10757
|
+
console.log(`[mmrc] Failed to fetch user info: ${res.status}`);
|
|
10758
|
+
return;
|
|
10759
|
+
}
|
|
10760
|
+
const user = await res.json();
|
|
10761
|
+
console.log(`[mmrc] Logged in to ${apiUrl}`);
|
|
10762
|
+
console.log(` Name: ${user.name ?? "(unknown)"}`);
|
|
10763
|
+
console.log(` Email: ${user.email ?? "(unknown)"}`);
|
|
10764
|
+
if (user.id) console.log(` ID: ${user.id}`);
|
|
10765
|
+
if (user.role) console.log(` Role: ${user.role}`);
|
|
10766
|
+
} catch (error) {
|
|
10767
|
+
console.log(`[mmrc] Could not connect to ${apiUrl}: ${error.message}`);
|
|
10768
|
+
}
|
|
10769
|
+
}
|
|
10770
|
+
var import_fs4, import_path3, import_os, import_readline, MMRC_DIR, CREDENTIALS_PATH;
|
|
10771
|
+
var init_auth = __esm({
|
|
10772
|
+
"src/cli/auth.ts"() {
|
|
10773
|
+
"use strict";
|
|
10774
|
+
import_fs4 = require("fs");
|
|
10775
|
+
import_path3 = require("path");
|
|
10776
|
+
import_os = require("os");
|
|
10777
|
+
import_readline = require("readline");
|
|
10778
|
+
MMRC_DIR = (0, import_path3.join)((0, import_os.homedir)(), ".mmrc");
|
|
10779
|
+
CREDENTIALS_PATH = (0, import_path3.join)(MMRC_DIR, "credentials.json");
|
|
10780
|
+
}
|
|
10781
|
+
});
|
|
10782
|
+
|
|
10783
|
+
// src/cli/deploy.ts
|
|
10784
|
+
var deploy_exports = {};
|
|
10785
|
+
__export(deploy_exports, {
|
|
10786
|
+
deploy: () => deploy,
|
|
10787
|
+
syncServices: () => syncServices
|
|
10788
|
+
});
|
|
10789
|
+
async function deploy(options) {
|
|
10790
|
+
const dir = options.dir ?? "dist/workflows";
|
|
10791
|
+
const targetLabel = options.targetName ? ` (target: ${options.targetName})` : "";
|
|
10792
|
+
console.log(`[mindmatrix-react] Deploying workflows from ${dir} to ${options.apiUrl}${targetLabel}...`);
|
|
10793
|
+
const files = await (0, import_glob2.glob)(`${dir}/**/*.workflow.json`);
|
|
10794
|
+
if (files.length === 0) {
|
|
10795
|
+
console.log(`[mindmatrix-react] No workflow files found in ${dir}`);
|
|
10796
|
+
return { created: [], updated: [], versioned: [], skipped: [], failed: [] };
|
|
10797
|
+
}
|
|
10798
|
+
const result = {
|
|
10799
|
+
created: [],
|
|
10800
|
+
updated: [],
|
|
10801
|
+
versioned: [],
|
|
10802
|
+
skipped: [],
|
|
10803
|
+
failed: []
|
|
10804
|
+
};
|
|
10805
|
+
const deployedIRs = [];
|
|
10806
|
+
for (const file2 of files) {
|
|
10807
|
+
try {
|
|
10808
|
+
const ir = JSON.parse((0, import_fs5.readFileSync)(file2, "utf-8"));
|
|
10809
|
+
const slug = ir.slug;
|
|
10810
|
+
if (options.dryRun) {
|
|
10811
|
+
console.log(` [dry-run] Would deploy ${slug} (${(0, import_path4.basename)(file2)})`);
|
|
10812
|
+
continue;
|
|
10813
|
+
}
|
|
10814
|
+
const existing = await fetchExistingDefinition(options.apiUrl, options.token, slug);
|
|
10815
|
+
if (!existing) {
|
|
10816
|
+
await createDefinition(options.apiUrl, options.token, ir);
|
|
10817
|
+
console.log(` \u2713 ${slug} (created)`);
|
|
10818
|
+
result.created.push(slug);
|
|
10819
|
+
} else if (existing.instanceCount === 0 || options.force) {
|
|
10820
|
+
await updateDefinition(options.apiUrl, options.token, existing.id, ir);
|
|
10821
|
+
console.log(` \u2713 ${slug} (updated)`);
|
|
10822
|
+
result.updated.push(slug);
|
|
10823
|
+
} else {
|
|
10824
|
+
const newVersion = bumpVersion(existing.version);
|
|
10825
|
+
ir.version = newVersion;
|
|
10826
|
+
await createDefinition(options.apiUrl, options.token, ir);
|
|
10827
|
+
console.log(` \u2713 ${slug} v${newVersion} (new version, ${existing.instanceCount} instances on v${existing.version})`);
|
|
10828
|
+
result.versioned.push(slug);
|
|
10829
|
+
}
|
|
10830
|
+
deployedIRs.push(ir);
|
|
10831
|
+
} catch (error) {
|
|
10832
|
+
const slug = (0, import_path4.basename)(file2).replace(".workflow.json", "");
|
|
10833
|
+
const msg = error.message;
|
|
10834
|
+
console.error(` \u2717 ${slug}: ${msg}`);
|
|
10835
|
+
result.failed.push({ slug, error: msg });
|
|
10836
|
+
}
|
|
10837
|
+
}
|
|
10838
|
+
if (!options.dryRun) {
|
|
10839
|
+
for (const ir of deployedIRs) {
|
|
10840
|
+
const serviceResult = await syncServices(options.apiUrl, options.token, ir);
|
|
10841
|
+
if (serviceResult) {
|
|
10842
|
+
if (!result.services) {
|
|
10843
|
+
result.services = { registered: [], updated: [], failed: [] };
|
|
10844
|
+
}
|
|
10845
|
+
result.services.registered.push(...serviceResult.registered);
|
|
10846
|
+
result.services.updated.push(...serviceResult.updated);
|
|
10847
|
+
result.services.failed.push(...serviceResult.failed);
|
|
10848
|
+
}
|
|
10849
|
+
}
|
|
10850
|
+
}
|
|
10851
|
+
if (options.reportFile) {
|
|
10852
|
+
(0, import_fs5.writeFileSync)(options.reportFile, JSON.stringify(result, null, 2), "utf-8");
|
|
10853
|
+
console.log(`
|
|
10854
|
+
Report written to ${options.reportFile}`);
|
|
10855
|
+
}
|
|
10856
|
+
const total = result.created.length + result.updated.length + result.versioned.length;
|
|
10857
|
+
console.log(
|
|
10858
|
+
`
|
|
10859
|
+
[mindmatrix-react] Deployed ${total} workflows (${result.created.length} created, ${result.updated.length} updated, ${result.versioned.length} versioned, ${result.failed.length} failed)`
|
|
10860
|
+
);
|
|
10861
|
+
if (result.services) {
|
|
10862
|
+
const svcTotal = result.services.registered.length + result.services.updated.length;
|
|
10863
|
+
if (svcTotal > 0 || result.services.failed.length > 0) {
|
|
10864
|
+
console.log(
|
|
10865
|
+
`[mindmatrix-react] Synced ${svcTotal} services (${result.services.registered.length} registered, ${result.services.updated.length} updated, ${result.services.failed.length} failed)`
|
|
10866
|
+
);
|
|
10867
|
+
}
|
|
10868
|
+
}
|
|
10869
|
+
return result;
|
|
10870
|
+
}
|
|
10871
|
+
async function fetchExistingDefinition(apiUrl, token, slug) {
|
|
10872
|
+
try {
|
|
10873
|
+
const res = await fetch(`${apiUrl}/workflow/definitions?slug=${encodeURIComponent(slug)}`, {
|
|
10874
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
10875
|
+
});
|
|
10876
|
+
if (!res.ok) return null;
|
|
10877
|
+
const data = await res.json();
|
|
10878
|
+
const definitions = Array.isArray(data) ? data : data.items ?? data.data;
|
|
10879
|
+
if (!definitions || definitions.length === 0) return null;
|
|
10880
|
+
const def = definitions[0];
|
|
10881
|
+
return {
|
|
10882
|
+
id: def.id,
|
|
10883
|
+
slug: def.slug,
|
|
10884
|
+
version: def.version || "0.1.0",
|
|
10885
|
+
instanceCount: def.instanceCount ?? def.instance_count ?? 0
|
|
10886
|
+
};
|
|
10887
|
+
} catch {
|
|
10888
|
+
return null;
|
|
10889
|
+
}
|
|
10890
|
+
}
|
|
10891
|
+
async function createDefinition(apiUrl, token, ir) {
|
|
10892
|
+
const res = await fetch(`${apiUrl}/workflow/definitions`, {
|
|
10893
|
+
method: "POST",
|
|
10894
|
+
headers: {
|
|
10895
|
+
"Content-Type": "application/json",
|
|
10896
|
+
Authorization: `Bearer ${token}`
|
|
10897
|
+
},
|
|
10898
|
+
body: JSON.stringify(ir)
|
|
10899
|
+
});
|
|
10900
|
+
if (!res.ok) {
|
|
10901
|
+
const errorText = await res.text();
|
|
10902
|
+
throw new Error(`Create failed: ${res.status} ${errorText}`);
|
|
10903
|
+
}
|
|
10904
|
+
}
|
|
10905
|
+
async function updateDefinition(apiUrl, token, id, ir) {
|
|
10906
|
+
const { slug: _slug, ...updatePayload } = ir;
|
|
10907
|
+
const res = await fetch(`${apiUrl}/workflow/definitions/${id}`, {
|
|
10908
|
+
method: "PATCH",
|
|
10909
|
+
headers: {
|
|
10910
|
+
"Content-Type": "application/json",
|
|
10911
|
+
Authorization: `Bearer ${token}`
|
|
10912
|
+
},
|
|
10913
|
+
body: JSON.stringify(updatePayload)
|
|
10914
|
+
});
|
|
10915
|
+
if (!res.ok) {
|
|
10916
|
+
const errorText = await res.text();
|
|
10917
|
+
throw new Error(`Update failed: ${res.status} ${errorText}`);
|
|
10918
|
+
}
|
|
10919
|
+
}
|
|
10920
|
+
function bumpVersion(version) {
|
|
10921
|
+
const parts = version.split(".");
|
|
10922
|
+
if (parts.length !== 3) return `${version}.1`;
|
|
10923
|
+
const patch = parseInt(parts[2], 10) || 0;
|
|
10924
|
+
return `${parts[0]}.${parts[1]}.${patch + 1}`;
|
|
10925
|
+
}
|
|
10926
|
+
async function syncServices(apiUrl, token, ir) {
|
|
10927
|
+
const metadata = ir.metadata;
|
|
10928
|
+
const orchestration = metadata?.orchestration;
|
|
10929
|
+
const services = orchestration?.services;
|
|
10930
|
+
if (!services || Object.keys(services).length === 0) return null;
|
|
10931
|
+
const result = { registered: [], updated: [], failed: [] };
|
|
10932
|
+
for (const [name, config] of Object.entries(services)) {
|
|
10933
|
+
try {
|
|
10934
|
+
const registration = {
|
|
10935
|
+
name,
|
|
10936
|
+
connection: {
|
|
10937
|
+
type: config.type || "webhook",
|
|
10938
|
+
url: config.url,
|
|
10939
|
+
queue: config.queue
|
|
10940
|
+
},
|
|
10941
|
+
actions: config.actions || [],
|
|
10942
|
+
labels: config.labels || {}
|
|
10943
|
+
};
|
|
10944
|
+
const existing = await fetchExistingService(apiUrl, token, name);
|
|
10945
|
+
if (existing) {
|
|
10946
|
+
await fetch(`${apiUrl}/services/${existing.id}`, {
|
|
10947
|
+
method: "PATCH",
|
|
10948
|
+
headers: {
|
|
10949
|
+
"Content-Type": "application/json",
|
|
10950
|
+
Authorization: `Bearer ${token}`
|
|
10951
|
+
},
|
|
10952
|
+
body: JSON.stringify(registration)
|
|
10953
|
+
});
|
|
10954
|
+
console.log(` \u2713 service: ${name} (updated)`);
|
|
10955
|
+
result.updated.push(name);
|
|
10956
|
+
} else {
|
|
10957
|
+
const res = await fetch(`${apiUrl}/services`, {
|
|
10958
|
+
method: "POST",
|
|
10959
|
+
headers: {
|
|
10960
|
+
"Content-Type": "application/json",
|
|
10961
|
+
Authorization: `Bearer ${token}`
|
|
10962
|
+
},
|
|
10963
|
+
body: JSON.stringify(registration)
|
|
10964
|
+
});
|
|
10965
|
+
if (!res.ok) {
|
|
10966
|
+
const errorText = await res.text();
|
|
10967
|
+
throw new Error(`Register failed: ${res.status} ${errorText}`);
|
|
10968
|
+
}
|
|
10969
|
+
console.log(` \u2713 service: ${name} (registered)`);
|
|
10970
|
+
result.registered.push(name);
|
|
10971
|
+
}
|
|
10972
|
+
} catch (error) {
|
|
10973
|
+
const msg = error.message;
|
|
10974
|
+
console.error(` \u2717 service: ${name}: ${msg}`);
|
|
10975
|
+
result.failed.push(name);
|
|
10976
|
+
}
|
|
10977
|
+
}
|
|
10978
|
+
return result;
|
|
10979
|
+
}
|
|
10980
|
+
async function fetchExistingService(apiUrl, token, name) {
|
|
10981
|
+
try {
|
|
10982
|
+
const res = await fetch(`${apiUrl}/services?name=${encodeURIComponent(name)}`, {
|
|
10983
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
10984
|
+
});
|
|
10985
|
+
if (!res.ok) return null;
|
|
10986
|
+
const data = await res.json();
|
|
10987
|
+
const servicesList = Array.isArray(data) ? data : data.services ?? data.items ?? data.data;
|
|
10988
|
+
if (!servicesList || servicesList.length === 0) return null;
|
|
10989
|
+
const found = servicesList.find((s) => s.name === name);
|
|
10990
|
+
return found ? { id: found.id, name: found.name } : null;
|
|
10991
|
+
} catch {
|
|
10992
|
+
return null;
|
|
10993
|
+
}
|
|
10994
|
+
}
|
|
10995
|
+
var import_glob2, import_fs5, import_path4;
|
|
10996
|
+
var init_deploy = __esm({
|
|
10997
|
+
"src/cli/deploy.ts"() {
|
|
10998
|
+
"use strict";
|
|
10999
|
+
import_glob2 = require("glob");
|
|
11000
|
+
import_fs5 = require("fs");
|
|
11001
|
+
import_path4 = require("path");
|
|
11002
|
+
}
|
|
11003
|
+
});
|
|
11004
|
+
|
|
11005
|
+
// src/cli/seed.ts
|
|
11006
|
+
var seed_exports = {};
|
|
11007
|
+
__export(seed_exports, {
|
|
11008
|
+
generateFieldValue: () => generateFieldValue,
|
|
11009
|
+
generateInstanceData: () => generateInstanceData,
|
|
11010
|
+
seedInstances: () => seedInstances
|
|
11011
|
+
});
|
|
11012
|
+
function randomInt(min, max) {
|
|
11013
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
11014
|
+
}
|
|
11015
|
+
function randomPick(arr) {
|
|
11016
|
+
return arr[Math.floor(Math.random() * arr.length)];
|
|
11017
|
+
}
|
|
11018
|
+
function loremSentence(words = 5) {
|
|
11019
|
+
const result = [];
|
|
11020
|
+
for (let i = 0; i < words; i++) result.push(randomPick(LOREM_WORDS));
|
|
11021
|
+
const s = result.join(" ");
|
|
11022
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
11023
|
+
}
|
|
11024
|
+
function generateFieldValue(field, index) {
|
|
11025
|
+
if (field.default_value !== void 0 && field.default_value !== null) {
|
|
11026
|
+
return field.default_value;
|
|
11027
|
+
}
|
|
11028
|
+
if (field.options && field.options.length > 0) {
|
|
11029
|
+
return field.options[index % field.options.length].value;
|
|
11030
|
+
}
|
|
11031
|
+
const ft = (field.field_type ?? field.type ?? "text").toLowerCase();
|
|
11032
|
+
switch (ft) {
|
|
11033
|
+
case "text":
|
|
11034
|
+
case "string":
|
|
11035
|
+
case "textarea":
|
|
11036
|
+
case "rich_text": {
|
|
11037
|
+
const name = field.name.toLowerCase();
|
|
11038
|
+
if (name.includes("email")) return `${randomPick(FIRST_NAMES).toLowerCase()}${++_counter}@${randomPick(DOMAINS)}`;
|
|
11039
|
+
if (name.includes("name") && name.includes("first")) return randomPick(FIRST_NAMES);
|
|
11040
|
+
if (name.includes("name") && name.includes("last")) return randomPick(LAST_NAMES);
|
|
11041
|
+
if (name.includes("name") || name === "title") return `${randomPick(FIRST_NAMES)} ${randomPick(LAST_NAMES)}`;
|
|
11042
|
+
if (name.includes("company") || name.includes("org")) return randomPick(COMPANIES);
|
|
11043
|
+
if (name.includes("phone")) return `+1-555-${randomInt(100, 999)}-${randomInt(1e3, 9999)}`;
|
|
11044
|
+
if (name.includes("url") || name.includes("website")) return `https://${randomPick(DOMAINS)}/${field.name}`;
|
|
11045
|
+
if (name.includes("description") || name.includes("note") || name.includes("comment")) return loremSentence(randomInt(8, 15));
|
|
11046
|
+
if (name.includes("address")) return `${randomInt(100, 9999)} ${loremSentence(2)} St`;
|
|
11047
|
+
return loremSentence(randomInt(2, 5));
|
|
11048
|
+
}
|
|
11049
|
+
case "number":
|
|
11050
|
+
case "numeric":
|
|
11051
|
+
case "integer":
|
|
11052
|
+
case "int": {
|
|
11053
|
+
const name = field.name.toLowerCase();
|
|
11054
|
+
if (name.includes("price") || name.includes("amount") || name.includes("cost") || name.includes("salary"))
|
|
11055
|
+
return randomInt(1e3, 99999) / 100;
|
|
11056
|
+
if (name.includes("age")) return randomInt(18, 65);
|
|
11057
|
+
if (name.includes("quantity") || name.includes("count") || name.includes("qty")) return randomInt(1, 100);
|
|
11058
|
+
if (name.includes("percent") || name.includes("rate")) return randomInt(1, 100);
|
|
11059
|
+
return randomInt(1, 1e3);
|
|
11060
|
+
}
|
|
11061
|
+
case "currency":
|
|
11062
|
+
case "decimal":
|
|
11063
|
+
return randomInt(100, 999999) / 100;
|
|
11064
|
+
case "boolean":
|
|
11065
|
+
case "bool":
|
|
11066
|
+
case "checkbox":
|
|
11067
|
+
case "toggle":
|
|
11068
|
+
return index % 2 === 0;
|
|
11069
|
+
case "date":
|
|
11070
|
+
case "datetime":
|
|
11071
|
+
case "timestamptz": {
|
|
11072
|
+
const d = /* @__PURE__ */ new Date();
|
|
11073
|
+
d.setDate(d.getDate() - randomInt(0, 365));
|
|
11074
|
+
return d.toISOString().split("T")[0];
|
|
11075
|
+
}
|
|
11076
|
+
case "select":
|
|
11077
|
+
case "enum":
|
|
11078
|
+
case "multi_select":
|
|
11079
|
+
return field.options?.[index % (field.options?.length || 1)]?.value ?? "option_1";
|
|
11080
|
+
case "email":
|
|
11081
|
+
return `${randomPick(FIRST_NAMES).toLowerCase()}${++_counter}@${randomPick(DOMAINS)}`;
|
|
11082
|
+
case "url":
|
|
11083
|
+
return `https://${randomPick(DOMAINS)}/${field.name}`;
|
|
11084
|
+
case "json":
|
|
11085
|
+
case "object":
|
|
11086
|
+
return {};
|
|
11087
|
+
case "array":
|
|
11088
|
+
return [];
|
|
11089
|
+
default:
|
|
11090
|
+
return loremSentence(3);
|
|
11091
|
+
}
|
|
11092
|
+
}
|
|
11093
|
+
function generateInstanceData(fields, index) {
|
|
11094
|
+
const data = {};
|
|
11095
|
+
for (const field of fields) {
|
|
11096
|
+
if (field.name === "id" || field.name === "created_at" || field.name === "updated_at") continue;
|
|
11097
|
+
data[field.name] = generateFieldValue(field, index);
|
|
11098
|
+
}
|
|
11099
|
+
return data;
|
|
11100
|
+
}
|
|
11101
|
+
async function seedInstances(options) {
|
|
11102
|
+
const dir = options.dir ?? "dist/workflows";
|
|
11103
|
+
const count = options.count ?? 7;
|
|
11104
|
+
const result = { created: 0, definitions: 0, errors: [] };
|
|
11105
|
+
const files = await (0, import_glob3.glob)(`${dir}/**/*.workflow.json`);
|
|
11106
|
+
if (files.length === 0) return result;
|
|
11107
|
+
for (const file2 of files) {
|
|
11108
|
+
let def;
|
|
11109
|
+
try {
|
|
11110
|
+
def = JSON.parse((0, import_fs6.readFileSync)(file2, "utf-8"));
|
|
11111
|
+
} catch {
|
|
11112
|
+
continue;
|
|
11113
|
+
}
|
|
11114
|
+
if (!def.slug || !def.fields || def.fields.length === 0) continue;
|
|
11115
|
+
const startState = def.states?.find((s) => s.state_type === "START")?.name ?? def.states?.[0]?.name ?? "initial";
|
|
11116
|
+
let defCreated = 0;
|
|
11117
|
+
for (let i = 0; i < count; i++) {
|
|
11118
|
+
const stateData = generateInstanceData(def.fields, i);
|
|
11119
|
+
try {
|
|
11120
|
+
const resp = await fetch(`${options.apiUrl}/workflow/instances`, {
|
|
11121
|
+
method: "POST",
|
|
11122
|
+
headers: {
|
|
11123
|
+
"Content-Type": "application/json",
|
|
11124
|
+
"Authorization": `Bearer ${options.token}`
|
|
11125
|
+
},
|
|
11126
|
+
body: JSON.stringify({
|
|
11127
|
+
definition_slug: def.slug,
|
|
11128
|
+
initial_state: startState,
|
|
11129
|
+
state_data: stateData
|
|
11130
|
+
})
|
|
11131
|
+
});
|
|
11132
|
+
if (resp.ok) {
|
|
11133
|
+
defCreated++;
|
|
11134
|
+
} else {
|
|
11135
|
+
const errText = await resp.text().catch(() => resp.statusText);
|
|
11136
|
+
result.errors.push(`${def.slug}[${i}]: ${resp.status} ${errText}`);
|
|
11137
|
+
}
|
|
11138
|
+
} catch (e) {
|
|
11139
|
+
result.errors.push(`${def.slug}[${i}]: ${e instanceof Error ? e.message : String(e)}`);
|
|
11140
|
+
}
|
|
11141
|
+
}
|
|
11142
|
+
if (defCreated > 0) {
|
|
11143
|
+
result.definitions++;
|
|
11144
|
+
result.created += defCreated;
|
|
11145
|
+
console.log(` [seed] ${def.slug}: ${defCreated} instances created`);
|
|
11146
|
+
}
|
|
11147
|
+
}
|
|
11148
|
+
return result;
|
|
11149
|
+
}
|
|
11150
|
+
var import_fs6, import_glob3, FIRST_NAMES, LAST_NAMES, COMPANIES, LOREM_WORDS, DOMAINS, _counter;
|
|
11151
|
+
var init_seed = __esm({
|
|
11152
|
+
"src/cli/seed.ts"() {
|
|
11153
|
+
"use strict";
|
|
11154
|
+
import_fs6 = require("fs");
|
|
11155
|
+
import_glob3 = require("glob");
|
|
11156
|
+
FIRST_NAMES = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank", "Grace", "Henry"];
|
|
11157
|
+
LAST_NAMES = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis"];
|
|
11158
|
+
COMPANIES = ["Acme Corp", "TechStart Inc", "GlobalTrade Ltd", "DataFlow Systems", "CloudPeak"];
|
|
11159
|
+
LOREM_WORDS = ["lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit", "sed", "do", "eiusmod", "tempor"];
|
|
11160
|
+
DOMAINS = ["example.com", "test.org", "demo.io", "sample.net"];
|
|
11161
|
+
_counter = 0;
|
|
11162
|
+
}
|
|
11163
|
+
});
|
|
11164
|
+
|
|
10619
11165
|
// src/index.ts
|
|
10620
11166
|
var index_exports = {};
|
|
10621
11167
|
__export(index_exports, {
|
|
@@ -11116,7 +11662,7 @@ function buildJSXTree(node, imports, symbols) {
|
|
|
11116
11662
|
}
|
|
11117
11663
|
const componentName = node.component;
|
|
11118
11664
|
if (REACT_ATOMS.has(componentName)) {
|
|
11119
|
-
addImport(imports, "@
|
|
11665
|
+
addImport(imports, "@mmapp/react", componentName);
|
|
11120
11666
|
}
|
|
11121
11667
|
const attributes = [];
|
|
11122
11668
|
if (node.config) {
|
|
@@ -11161,7 +11707,7 @@ function buildJSXTree(node, imports, symbols) {
|
|
|
11161
11707
|
isSelfClosing
|
|
11162
11708
|
);
|
|
11163
11709
|
if (node.visible_when) {
|
|
11164
|
-
addImport(imports, "@
|
|
11710
|
+
addImport(imports, "@mmapp/react", "Show");
|
|
11165
11711
|
return t8.jsxElement(
|
|
11166
11712
|
t8.jsxOpeningElement(
|
|
11167
11713
|
t8.jsxIdentifier("Show"),
|
|
@@ -11242,7 +11788,7 @@ function fieldTypeAnnotation(field) {
|
|
|
11242
11788
|
}
|
|
11243
11789
|
function emitFieldDeclarations(fields, imports) {
|
|
11244
11790
|
if (fields.length === 0) return [];
|
|
11245
|
-
addImport2(imports, "@
|
|
11791
|
+
addImport2(imports, "@mmapp/react", "useState");
|
|
11246
11792
|
const statements = [];
|
|
11247
11793
|
for (const field of fields) {
|
|
11248
11794
|
if (field.computed) continue;
|
|
@@ -11306,7 +11852,7 @@ function emitStateEffects(states, imports) {
|
|
|
11306
11852
|
const statements = [];
|
|
11307
11853
|
for (const state of states) {
|
|
11308
11854
|
if (state.on_enter.length > 0) {
|
|
11309
|
-
addImport2(imports, "@
|
|
11855
|
+
addImport2(imports, "@mmapp/react", "useOnEnter");
|
|
11310
11856
|
statements.push(
|
|
11311
11857
|
t9.expressionStatement(
|
|
11312
11858
|
t9.callExpression(t9.identifier("useOnEnter"), [
|
|
@@ -11317,7 +11863,7 @@ function emitStateEffects(states, imports) {
|
|
|
11317
11863
|
);
|
|
11318
11864
|
}
|
|
11319
11865
|
if (state.on_exit.length > 0) {
|
|
11320
|
-
addImport2(imports, "@
|
|
11866
|
+
addImport2(imports, "@mmapp/react", "useOnExit");
|
|
11321
11867
|
statements.push(
|
|
11322
11868
|
t9.expressionStatement(
|
|
11323
11869
|
t9.callExpression(t9.identifier("useOnExit"), [
|
|
@@ -11328,7 +11874,7 @@ function emitStateEffects(states, imports) {
|
|
|
11328
11874
|
);
|
|
11329
11875
|
}
|
|
11330
11876
|
if (state.during.length > 0) {
|
|
11331
|
-
addImport2(imports, "@
|
|
11877
|
+
addImport2(imports, "@mmapp/react", "useWhileIn");
|
|
11332
11878
|
for (const during of state.during) {
|
|
11333
11879
|
const args = [
|
|
11334
11880
|
t9.stringLiteral(state.name)
|
|
@@ -11349,7 +11895,7 @@ function emitStateEffects(states, imports) {
|
|
|
11349
11895
|
}
|
|
11350
11896
|
function emitTransitionDeclarations(transitions, imports) {
|
|
11351
11897
|
if (transitions.length === 0) return [];
|
|
11352
|
-
addImport2(imports, "@
|
|
11898
|
+
addImport2(imports, "@mmapp/react", "useTransition");
|
|
11353
11899
|
const correctedTransitions = correctFromFields(transitions);
|
|
11354
11900
|
const statements = [];
|
|
11355
11901
|
for (const transition of correctedTransitions) {
|
|
@@ -11584,7 +12130,7 @@ function emitSingleCondition(cond) {
|
|
|
11584
12130
|
function emitRoleDeclarations(metadata, imports) {
|
|
11585
12131
|
const roleDeps = metadata?.roleDependencies;
|
|
11586
12132
|
if (!roleDeps || roleDeps.length === 0) return [];
|
|
11587
|
-
addImport2(imports, "@
|
|
12133
|
+
addImport2(imports, "@mmapp/react", "useRole");
|
|
11588
12134
|
const statements = [];
|
|
11589
12135
|
for (const role of roleDeps) {
|
|
11590
12136
|
const varName = `is${toPascalCase(role)}`;
|
|
@@ -11605,7 +12151,7 @@ function emitQueryDeclarations(metadata, imports) {
|
|
|
11605
12151
|
const modelImports = metadata?.modelImports;
|
|
11606
12152
|
const addedModelImports = /* @__PURE__ */ new Set();
|
|
11607
12153
|
if (dataSources && dataSources.length > 0) {
|
|
11608
|
-
addImport2(imports, "@
|
|
12154
|
+
addImport2(imports, "@mmapp/react", "useQuery");
|
|
11609
12155
|
const usedVarNames = /* @__PURE__ */ new Set();
|
|
11610
12156
|
for (const ds of dataSources) {
|
|
11611
12157
|
if (ds.type !== "workflow") continue;
|
|
@@ -11672,7 +12218,7 @@ function emitQueryDeclarations(metadata, imports) {
|
|
|
11672
12218
|
}
|
|
11673
12219
|
const mutationTargets = metadata?.mutationTargets;
|
|
11674
12220
|
if (mutationTargets && mutationTargets.length > 0) {
|
|
11675
|
-
addImport2(imports, "@
|
|
12221
|
+
addImport2(imports, "@mmapp/react", "useMutation");
|
|
11676
12222
|
for (const slug of mutationTargets) {
|
|
11677
12223
|
const modelPath = modelImports?.[slug];
|
|
11678
12224
|
const modelVarName2 = modelPath ? `${toCamelCase2(slug.replace(/-/g, "_"))}Model` : void 0;
|
|
@@ -11697,7 +12243,7 @@ function emitQueryDeclarations(metadata, imports) {
|
|
|
11697
12243
|
function emitChangeWatchers(metadata, imports) {
|
|
11698
12244
|
const watchers = metadata?.fieldWatchers;
|
|
11699
12245
|
if (!watchers || watchers.length === 0) return [];
|
|
11700
|
-
addImport2(imports, "@
|
|
12246
|
+
addImport2(imports, "@mmapp/react", "useOnChange");
|
|
11701
12247
|
const statements = [];
|
|
11702
12248
|
for (const watcher of watchers) {
|
|
11703
12249
|
statements.push(
|
|
@@ -11713,7 +12259,7 @@ function emitChangeWatchers(metadata, imports) {
|
|
|
11713
12259
|
}
|
|
11714
12260
|
function emitEventSubscriptions(onEvent, imports) {
|
|
11715
12261
|
if (!onEvent || onEvent.length === 0) return [];
|
|
11716
|
-
addImport2(imports, "@
|
|
12262
|
+
addImport2(imports, "@mmapp/react", "useOnEvent");
|
|
11717
12263
|
const statements = [];
|
|
11718
12264
|
for (const sub of onEvent) {
|
|
11719
12265
|
const actions = sub.actions.map((a, i) => ({
|
|
@@ -11740,7 +12286,7 @@ function emitEventSubscriptions(onEvent, imports) {
|
|
|
11740
12286
|
function emitTransitionEffects(metadata, imports) {
|
|
11741
12287
|
const effects = metadata?.transitionEffects;
|
|
11742
12288
|
if (!effects || effects.length === 0) return [];
|
|
11743
|
-
addImport2(imports, "@
|
|
12289
|
+
addImport2(imports, "@mmapp/react", "useOnTransition");
|
|
11744
12290
|
const statements = [];
|
|
11745
12291
|
for (const effect of effects) {
|
|
11746
12292
|
statements.push(
|
|
@@ -11771,7 +12317,7 @@ function emitLocalDefaultDeclarations(experience, existingFields, imports) {
|
|
|
11771
12317
|
const camelName = toCamelCase2(key);
|
|
11772
12318
|
if (emittedCamelNames.has(camelName)) continue;
|
|
11773
12319
|
emittedCamelNames.add(camelName);
|
|
11774
|
-
addImport2(imports, "@
|
|
12320
|
+
addImport2(imports, "@mmapp/react", "useState");
|
|
11775
12321
|
const setter = `set${toPascalCase(key)}`;
|
|
11776
12322
|
let defaultExpr;
|
|
11777
12323
|
if (value === void 0 || value === null) {
|
|
@@ -11984,7 +12530,7 @@ function generateServerActionFile(actions) {
|
|
|
11984
12530
|
` * Each receives a TransitionContext with instance data and utilities.`,
|
|
11985
12531
|
` */`,
|
|
11986
12532
|
``,
|
|
11987
|
-
`import type { TransitionContext } from '@
|
|
12533
|
+
`import type { TransitionContext } from '@mmapp/react';`,
|
|
11988
12534
|
``
|
|
11989
12535
|
];
|
|
11990
12536
|
for (const action of actions) {
|
|
@@ -12057,7 +12603,7 @@ function generateConfigFile(def) {
|
|
|
12057
12603
|
` * Blueprint configuration for "${def.name || pascalCase(def.slug)}".`,
|
|
12058
12604
|
` */`,
|
|
12059
12605
|
``,
|
|
12060
|
-
`import { defineBlueprint } from '@
|
|
12606
|
+
`import { defineBlueprint } from '@mmapp/react';`,
|
|
12061
12607
|
``,
|
|
12062
12608
|
`export default defineBlueprint({`,
|
|
12063
12609
|
` slug: '${def.slug}',`,
|
|
@@ -12152,7 +12698,7 @@ function generateMainFile(def, pages, hasModel, serverActionNames, options) {
|
|
|
12152
12698
|
reactImports.add("Stack");
|
|
12153
12699
|
if (reactImports.size > 0) {
|
|
12154
12700
|
const sorted = [...reactImports].sort();
|
|
12155
|
-
importLines.push(`import { ${sorted.join(", ")} } from '@
|
|
12701
|
+
importLines.push(`import { ${sorted.join(", ")} } from '@mmapp/react';`);
|
|
12156
12702
|
}
|
|
12157
12703
|
if (hasModel) {
|
|
12158
12704
|
const typeName = pascalCase(slug);
|
|
@@ -13349,27 +13895,580 @@ async function startWatchMode(options) {
|
|
|
13349
13895
|
console.log(`
|
|
13350
13896
|
[mindmatrix-react] Watching ${srcDir} for changes... (Ctrl+C to stop)
|
|
13351
13897
|
`);
|
|
13352
|
-
fsWatch(srcDir, { recursive: true }, (_event, filename) => {
|
|
13353
|
-
if (!filename) return;
|
|
13354
|
-
if (!filename.match(/\.(tsx?|jsx?)$/)) return;
|
|
13355
|
-
if (filename.includes("node_modules") || filename.includes("dist")) return;
|
|
13356
|
-
if (debounce) clearTimeout(debounce);
|
|
13357
|
-
debounce = setTimeout(async () => {
|
|
13358
|
-
const ts = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
13359
|
-
console.log(`
|
|
13360
|
-
[${ts}] Change detected: ${filename} \u2014 recompiling...`);
|
|
13361
|
-
try {
|
|
13362
|
-
await build({ ...options, watch: false, skipTypeCheck: true });
|
|
13363
|
-
} catch (e) {
|
|
13364
|
-
console.error(`[mindmatrix-react] Rebuild failed:`, e.message);
|
|
13365
|
-
}
|
|
13366
|
-
}, 300);
|
|
13367
|
-
});
|
|
13368
|
-
return new Promise(() => {
|
|
13369
|
-
});
|
|
13898
|
+
fsWatch(srcDir, { recursive: true }, (_event, filename) => {
|
|
13899
|
+
if (!filename) return;
|
|
13900
|
+
if (!filename.match(/\.(tsx?|jsx?)$/)) return;
|
|
13901
|
+
if (filename.includes("node_modules") || filename.includes("dist")) return;
|
|
13902
|
+
if (debounce) clearTimeout(debounce);
|
|
13903
|
+
debounce = setTimeout(async () => {
|
|
13904
|
+
const ts = (/* @__PURE__ */ new Date()).toLocaleTimeString();
|
|
13905
|
+
console.log(`
|
|
13906
|
+
[${ts}] Change detected: ${filename} \u2014 recompiling...`);
|
|
13907
|
+
try {
|
|
13908
|
+
await build({ ...options, watch: false, skipTypeCheck: true });
|
|
13909
|
+
} catch (e) {
|
|
13910
|
+
console.error(`[mindmatrix-react] Rebuild failed:`, e.message);
|
|
13911
|
+
}
|
|
13912
|
+
}, 300);
|
|
13913
|
+
});
|
|
13914
|
+
return new Promise(() => {
|
|
13915
|
+
});
|
|
13916
|
+
}
|
|
13917
|
+
|
|
13918
|
+
// src/cli/local-server.ts
|
|
13919
|
+
var http = __toESM(require("http"));
|
|
13920
|
+
var import_node_crypto = require("crypto");
|
|
13921
|
+
var MemoryStore = class {
|
|
13922
|
+
constructor() {
|
|
13923
|
+
this.definitions = /* @__PURE__ */ new Map();
|
|
13924
|
+
this.instances = /* @__PURE__ */ new Map();
|
|
13925
|
+
this.slugIndex = /* @__PURE__ */ new Map();
|
|
13926
|
+
}
|
|
13927
|
+
// slug → id
|
|
13928
|
+
// ── Definitions ──────────────────────────────────────────────────────
|
|
13929
|
+
createDefinition(input) {
|
|
13930
|
+
if (this.slugIndex.has(input.slug)) {
|
|
13931
|
+
const existing = this.definitions.get(this.slugIndex.get(input.slug));
|
|
13932
|
+
if (existing) return existing;
|
|
13933
|
+
}
|
|
13934
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
13935
|
+
const def = {
|
|
13936
|
+
id: input.id ?? (0, import_node_crypto.randomUUID)(),
|
|
13937
|
+
slug: input.slug,
|
|
13938
|
+
name: input.name,
|
|
13939
|
+
version: input.version ?? "1.0.0",
|
|
13940
|
+
description: input.description ?? null,
|
|
13941
|
+
category: input.category ?? "workflow",
|
|
13942
|
+
fields: input.fields ?? [],
|
|
13943
|
+
states: input.states ?? [],
|
|
13944
|
+
transitions: input.transitions ?? [],
|
|
13945
|
+
roles: input.roles ?? [],
|
|
13946
|
+
experience: input.experience ?? null,
|
|
13947
|
+
metadata: input.metadata ?? {},
|
|
13948
|
+
child_definitions: input.child_definitions ?? [],
|
|
13949
|
+
is_immutable: input.is_immutable ?? false,
|
|
13950
|
+
tags: input.tags ?? [],
|
|
13951
|
+
inline_tags: input.inline_tags ?? [],
|
|
13952
|
+
created_at: now,
|
|
13953
|
+
updated_at: now
|
|
13954
|
+
};
|
|
13955
|
+
this.definitions.set(def.id, def);
|
|
13956
|
+
this.slugIndex.set(def.slug, def.id);
|
|
13957
|
+
return def;
|
|
13958
|
+
}
|
|
13959
|
+
getDefinition(idOrSlug) {
|
|
13960
|
+
const byId = this.definitions.get(idOrSlug);
|
|
13961
|
+
if (byId) return byId;
|
|
13962
|
+
const id = this.slugIndex.get(idOrSlug);
|
|
13963
|
+
if (id) return this.definitions.get(id);
|
|
13964
|
+
return void 0;
|
|
13965
|
+
}
|
|
13966
|
+
listDefinitions(opts) {
|
|
13967
|
+
let items = Array.from(this.definitions.values());
|
|
13968
|
+
if (opts?.category) {
|
|
13969
|
+
items = items.filter((d) => d.category === opts.category);
|
|
13970
|
+
}
|
|
13971
|
+
const total = items.length;
|
|
13972
|
+
const offset = opts?.offset ?? 0;
|
|
13973
|
+
const limit = opts?.limit ?? 50;
|
|
13974
|
+
items = items.slice(offset, offset + limit);
|
|
13975
|
+
return { items, total };
|
|
13976
|
+
}
|
|
13977
|
+
patchDefinition(id, patch) {
|
|
13978
|
+
const def = this.definitions.get(id);
|
|
13979
|
+
if (!def) return void 0;
|
|
13980
|
+
Object.assign(def, patch, { updated_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
13981
|
+
return def;
|
|
13982
|
+
}
|
|
13983
|
+
deleteDefinition(id) {
|
|
13984
|
+
const def = this.definitions.get(id);
|
|
13985
|
+
if (!def) return false;
|
|
13986
|
+
this.slugIndex.delete(def.slug);
|
|
13987
|
+
this.definitions.delete(id);
|
|
13988
|
+
return true;
|
|
13989
|
+
}
|
|
13990
|
+
// ── Instances ────────────────────────────────────────────────────────
|
|
13991
|
+
createInstance(input) {
|
|
13992
|
+
const def = this.getDefinition(input.definition_id) ?? this.getDefinition(input.definition_slug);
|
|
13993
|
+
if (!def) return null;
|
|
13994
|
+
const initialState = def.states.find((s) => s.type === "START" || s.type === "initial");
|
|
13995
|
+
const stateName = initialState?.name ?? "initial";
|
|
13996
|
+
const stateData = {};
|
|
13997
|
+
for (const field of def.fields) {
|
|
13998
|
+
if (field.default_value !== void 0) {
|
|
13999
|
+
stateData[field.name] = field.default_value;
|
|
14000
|
+
}
|
|
14001
|
+
}
|
|
14002
|
+
Object.assign(stateData, input.state_data ?? {});
|
|
14003
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14004
|
+
const inst = {
|
|
14005
|
+
id: (0, import_node_crypto.randomUUID)(),
|
|
14006
|
+
definition_id: def.id,
|
|
14007
|
+
definition_slug: def.slug,
|
|
14008
|
+
current_state: stateName,
|
|
14009
|
+
state_data: stateData,
|
|
14010
|
+
execution_lock_version: 0,
|
|
14011
|
+
event_log: [{
|
|
14012
|
+
event_type: "transition",
|
|
14013
|
+
message: `Instance created in state '${stateName}'`,
|
|
14014
|
+
timestamp: now
|
|
14015
|
+
}],
|
|
14016
|
+
created_at: now,
|
|
14017
|
+
updated_at: now
|
|
14018
|
+
};
|
|
14019
|
+
this.instances.set(inst.id, inst);
|
|
14020
|
+
return inst;
|
|
14021
|
+
}
|
|
14022
|
+
getInstance(id) {
|
|
14023
|
+
return this.instances.get(id);
|
|
14024
|
+
}
|
|
14025
|
+
listInstances(opts) {
|
|
14026
|
+
let items = Array.from(this.instances.values());
|
|
14027
|
+
if (opts?.definition_id) {
|
|
14028
|
+
items = items.filter((i) => i.definition_id === opts.definition_id);
|
|
14029
|
+
}
|
|
14030
|
+
const total = items.length;
|
|
14031
|
+
const offset = opts?.offset ?? 0;
|
|
14032
|
+
const limit = opts?.limit ?? 50;
|
|
14033
|
+
items = items.slice(offset, offset + limit);
|
|
14034
|
+
return { items, total };
|
|
14035
|
+
}
|
|
14036
|
+
// ── Execute Action (Transition) ──────────────────────────────────────
|
|
14037
|
+
executeAction(input) {
|
|
14038
|
+
const def = this.getDefinition(input.definition_id);
|
|
14039
|
+
if (!def) return { success: false, error: "Definition not found" };
|
|
14040
|
+
let inst;
|
|
14041
|
+
if (input.instance_id) {
|
|
14042
|
+
const existing = this.instances.get(input.instance_id);
|
|
14043
|
+
if (!existing) return { success: false, error: "Instance not found" };
|
|
14044
|
+
inst = existing;
|
|
14045
|
+
} else {
|
|
14046
|
+
const created = this.createInstance({
|
|
14047
|
+
definition_id: def.id,
|
|
14048
|
+
definition_slug: def.slug,
|
|
14049
|
+
state_data: input.payload
|
|
14050
|
+
});
|
|
14051
|
+
if (!created) return { success: false, error: "Failed to create instance" };
|
|
14052
|
+
inst = created;
|
|
14053
|
+
}
|
|
14054
|
+
if (input.payload && input.instance_id) {
|
|
14055
|
+
Object.assign(inst.state_data, input.payload);
|
|
14056
|
+
}
|
|
14057
|
+
const transition = def.transitions.find((t27) => t27.name === input.action_name && t27.from.includes(inst.current_state));
|
|
14058
|
+
if (!transition) {
|
|
14059
|
+
return {
|
|
14060
|
+
success: false,
|
|
14061
|
+
instance_id: inst.id,
|
|
14062
|
+
from_state: inst.current_state,
|
|
14063
|
+
to_state: null,
|
|
14064
|
+
state_data: inst.state_data,
|
|
14065
|
+
error: `No transition '${input.action_name}' from state '${inst.current_state}'`
|
|
14066
|
+
};
|
|
14067
|
+
}
|
|
14068
|
+
const fromState = inst.current_state;
|
|
14069
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14070
|
+
const events = [];
|
|
14071
|
+
let lastEvalResult = null;
|
|
14072
|
+
events.push({ event_type: "transition", message: `Transition '${transition.name}' started: ${fromState} \u2192 ${transition.to}`, timestamp: now });
|
|
14073
|
+
for (const action of transition.actions ?? []) {
|
|
14074
|
+
try {
|
|
14075
|
+
if (action.type === "set_field") {
|
|
14076
|
+
const field = action.config?.field;
|
|
14077
|
+
if (action.config?.expression) {
|
|
14078
|
+
const expr = action.config.expression;
|
|
14079
|
+
const result = this.evaluateSimpleExpression(expr, inst.state_data);
|
|
14080
|
+
inst.state_data[field] = result;
|
|
14081
|
+
} else if (action.config?.value !== void 0) {
|
|
14082
|
+
inst.state_data[field] = action.config.value;
|
|
14083
|
+
}
|
|
14084
|
+
events.push({ event_type: "action_executed", message: `transition action 'set_field' succeeded`, timestamp: now });
|
|
14085
|
+
} else if (action.type === "eval") {
|
|
14086
|
+
const expr = action.config?.expression;
|
|
14087
|
+
lastEvalResult = this.evaluateSimpleExpression(expr, inst.state_data);
|
|
14088
|
+
events.push({ event_type: "action_executed", message: `transition action 'eval' succeeded`, timestamp: now });
|
|
14089
|
+
} else {
|
|
14090
|
+
events.push({ event_type: "action_executed", message: `transition action '${action.type}' succeeded (no-op in local mode)`, timestamp: now });
|
|
14091
|
+
}
|
|
14092
|
+
} catch (err) {
|
|
14093
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
14094
|
+
events.push({ event_type: "action_failed", message: `transition action '${action.type}' failed: ${msg}`, timestamp: now });
|
|
14095
|
+
return {
|
|
14096
|
+
success: false,
|
|
14097
|
+
instance_id: inst.id,
|
|
14098
|
+
from_state: fromState,
|
|
14099
|
+
to_state: null,
|
|
14100
|
+
state_data: inst.state_data,
|
|
14101
|
+
event_log: events,
|
|
14102
|
+
error: `transition action failed: ${msg}`
|
|
14103
|
+
};
|
|
14104
|
+
}
|
|
14105
|
+
}
|
|
14106
|
+
inst.current_state = transition.to;
|
|
14107
|
+
inst.execution_lock_version++;
|
|
14108
|
+
inst.updated_at = now;
|
|
14109
|
+
events.push({ event_type: "transition", message: `State changed: ${fromState} \u2192 ${transition.to}`, timestamp: now });
|
|
14110
|
+
inst.event_log.push(...events);
|
|
14111
|
+
return {
|
|
14112
|
+
success: true,
|
|
14113
|
+
instance_id: inst.id,
|
|
14114
|
+
from_state: fromState,
|
|
14115
|
+
to_state: transition.to,
|
|
14116
|
+
state_data: inst.state_data,
|
|
14117
|
+
result: lastEvalResult,
|
|
14118
|
+
event_log: events
|
|
14119
|
+
};
|
|
14120
|
+
}
|
|
14121
|
+
/**
|
|
14122
|
+
* Minimal expression evaluator for local dev mode.
|
|
14123
|
+
* Handles: field references, arithmetic, string literals, simple comparisons.
|
|
14124
|
+
* Does NOT handle: while loops, if/else, function calls, multi-statement blocks.
|
|
14125
|
+
* For full evaluation, use mm-napi when available.
|
|
14126
|
+
*/
|
|
14127
|
+
evaluateSimpleExpression(expr, context) {
|
|
14128
|
+
if (context[expr] !== void 0) return context[expr];
|
|
14129
|
+
const arithMatch = expr.match(/^(\w+)\s*([+\-*/])\s*(\d+(?:\.\d+)?)$/);
|
|
14130
|
+
if (arithMatch) {
|
|
14131
|
+
const [, field, op, numStr] = arithMatch;
|
|
14132
|
+
const left = Number(context[field] ?? 0);
|
|
14133
|
+
const right = Number(numStr);
|
|
14134
|
+
switch (op) {
|
|
14135
|
+
case "+":
|
|
14136
|
+
return left + right;
|
|
14137
|
+
case "-":
|
|
14138
|
+
return left - right;
|
|
14139
|
+
case "*":
|
|
14140
|
+
return left * right;
|
|
14141
|
+
case "/":
|
|
14142
|
+
return right !== 0 ? left / right : 0;
|
|
14143
|
+
}
|
|
14144
|
+
}
|
|
14145
|
+
if (/^\d+(\.\d+)?$/.test(expr.trim())) {
|
|
14146
|
+
return Number(expr.trim());
|
|
14147
|
+
}
|
|
14148
|
+
const strMatch = expr.match(/^["'](.*)["']$/);
|
|
14149
|
+
if (strMatch) return strMatch[1];
|
|
14150
|
+
try {
|
|
14151
|
+
const keys = Object.keys(context);
|
|
14152
|
+
const values = Object.values(context);
|
|
14153
|
+
const fn = new Function(...keys, `"use strict"; return (${expr});`);
|
|
14154
|
+
return fn(...values);
|
|
14155
|
+
} catch {
|
|
14156
|
+
return null;
|
|
14157
|
+
}
|
|
14158
|
+
}
|
|
14159
|
+
};
|
|
14160
|
+
async function startLocalServer(options = {}) {
|
|
14161
|
+
const { port = 4200, noAuth = true } = options;
|
|
14162
|
+
const store = new MemoryStore();
|
|
14163
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
14164
|
+
function json(res, status, body) {
|
|
14165
|
+
const data = JSON.stringify(body);
|
|
14166
|
+
res.writeHead(status, {
|
|
14167
|
+
"Content-Type": "application/json",
|
|
14168
|
+
"Access-Control-Allow-Origin": "*",
|
|
14169
|
+
"Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
|
|
14170
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
14171
|
+
"Content-Length": Buffer.byteLength(data)
|
|
14172
|
+
});
|
|
14173
|
+
res.end(data);
|
|
14174
|
+
}
|
|
14175
|
+
function readBody(req) {
|
|
14176
|
+
return new Promise((resolve3, reject) => {
|
|
14177
|
+
const chunks = [];
|
|
14178
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
14179
|
+
req.on("end", () => resolve3(Buffer.concat(chunks).toString()));
|
|
14180
|
+
req.on("error", reject);
|
|
14181
|
+
});
|
|
14182
|
+
}
|
|
14183
|
+
function parseQuery(url) {
|
|
14184
|
+
const idx = url.indexOf("?");
|
|
14185
|
+
if (idx === -1) return {};
|
|
14186
|
+
const params = {};
|
|
14187
|
+
const qs = url.slice(idx + 1);
|
|
14188
|
+
for (const pair of qs.split("&")) {
|
|
14189
|
+
const [k, v] = pair.split("=");
|
|
14190
|
+
if (k) params[decodeURIComponent(k)] = decodeURIComponent(v ?? "");
|
|
14191
|
+
}
|
|
14192
|
+
return params;
|
|
14193
|
+
}
|
|
14194
|
+
const server = http.createServer(async (req, res) => {
|
|
14195
|
+
const method = req.method?.toUpperCase() ?? "GET";
|
|
14196
|
+
const rawUrl = req.url ?? "/";
|
|
14197
|
+
const queryStart = rawUrl.indexOf("?");
|
|
14198
|
+
const path = queryStart >= 0 ? rawUrl.slice(0, queryStart) : rawUrl;
|
|
14199
|
+
const query = parseQuery(rawUrl);
|
|
14200
|
+
if (method === "OPTIONS") {
|
|
14201
|
+
res.writeHead(204, {
|
|
14202
|
+
"Access-Control-Allow-Origin": "*",
|
|
14203
|
+
"Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
|
|
14204
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
14205
|
+
"Access-Control-Max-Age": "86400"
|
|
14206
|
+
});
|
|
14207
|
+
res.end();
|
|
14208
|
+
return;
|
|
14209
|
+
}
|
|
14210
|
+
try {
|
|
14211
|
+
if (path === "/health" && method === "GET") {
|
|
14212
|
+
return json(res, 200, {
|
|
14213
|
+
status: "ok",
|
|
14214
|
+
service: "mm-local-dev",
|
|
14215
|
+
mode: "in-memory",
|
|
14216
|
+
started_at: startedAt,
|
|
14217
|
+
definitions: store.definitions.size,
|
|
14218
|
+
instances: store.instances.size
|
|
14219
|
+
});
|
|
14220
|
+
}
|
|
14221
|
+
if (path === "/api/v1/auth/login" && (method === "POST" || method === "GET")) {
|
|
14222
|
+
return json(res, 200, {
|
|
14223
|
+
token: "dev-token-local",
|
|
14224
|
+
user: {
|
|
14225
|
+
id: "dev-user-001",
|
|
14226
|
+
email: "dev@localhost",
|
|
14227
|
+
role: "admin",
|
|
14228
|
+
name: "Local Developer"
|
|
14229
|
+
}
|
|
14230
|
+
});
|
|
14231
|
+
}
|
|
14232
|
+
if (path === "/api/v1/workflow/definitions" && method === "GET") {
|
|
14233
|
+
const result = store.listDefinitions({
|
|
14234
|
+
category: query.category,
|
|
14235
|
+
limit: query.limit ? parseInt(query.limit, 10) : void 0,
|
|
14236
|
+
offset: query.offset ? parseInt(query.offset, 10) : void 0
|
|
14237
|
+
});
|
|
14238
|
+
if (query.slug) {
|
|
14239
|
+
const def = store.getDefinition(query.slug);
|
|
14240
|
+
return json(res, 200, { items: def ? [def] : [], total: def ? 1 : 0 });
|
|
14241
|
+
}
|
|
14242
|
+
return json(res, 200, result);
|
|
14243
|
+
}
|
|
14244
|
+
const defMatch = path.match(/^\/api\/v1\/workflow\/definitions\/([^/]+)$/);
|
|
14245
|
+
if (defMatch && method === "GET") {
|
|
14246
|
+
const def = store.getDefinition(defMatch[1]);
|
|
14247
|
+
if (!def) return json(res, 404, { error: "Not found" });
|
|
14248
|
+
return json(res, 200, def);
|
|
14249
|
+
}
|
|
14250
|
+
if (path === "/api/v1/workflow/definitions" && method === "POST") {
|
|
14251
|
+
const body = JSON.parse(await readBody(req));
|
|
14252
|
+
const def = store.createDefinition(body);
|
|
14253
|
+
return json(res, 201, def);
|
|
14254
|
+
}
|
|
14255
|
+
if (defMatch && method === "PATCH") {
|
|
14256
|
+
const body = JSON.parse(await readBody(req));
|
|
14257
|
+
const updated = store.patchDefinition(defMatch[1], body);
|
|
14258
|
+
if (!updated) return json(res, 404, { error: "Not found" });
|
|
14259
|
+
return json(res, 200, updated);
|
|
14260
|
+
}
|
|
14261
|
+
if (defMatch && method === "DELETE") {
|
|
14262
|
+
const deleted = store.deleteDefinition(defMatch[1]);
|
|
14263
|
+
if (!deleted) return json(res, 404, { error: "Not found" });
|
|
14264
|
+
return json(res, 204, null);
|
|
14265
|
+
}
|
|
14266
|
+
if (path === "/api/v1/workflow/instances" && method === "GET") {
|
|
14267
|
+
const result = store.listInstances({
|
|
14268
|
+
definition_id: query.definition_id,
|
|
14269
|
+
limit: query.limit ? parseInt(query.limit, 10) : void 0,
|
|
14270
|
+
offset: query.offset ? parseInt(query.offset, 10) : void 0
|
|
14271
|
+
});
|
|
14272
|
+
return json(res, 200, result);
|
|
14273
|
+
}
|
|
14274
|
+
if (path === "/api/v1/workflow/instances" && method === "POST") {
|
|
14275
|
+
const body = JSON.parse(await readBody(req));
|
|
14276
|
+
const inst = store.createInstance(body);
|
|
14277
|
+
if (!inst) return json(res, 404, { error: "Definition not found" });
|
|
14278
|
+
return json(res, 201, inst);
|
|
14279
|
+
}
|
|
14280
|
+
const instMatch = path.match(/^\/api\/v1\/workflow\/instances\/([^/]+)$/);
|
|
14281
|
+
if (instMatch && method === "GET") {
|
|
14282
|
+
const inst = store.getInstance(instMatch[1]);
|
|
14283
|
+
if (!inst) return json(res, 404, { error: "Not found" });
|
|
14284
|
+
return json(res, 200, inst);
|
|
14285
|
+
}
|
|
14286
|
+
if (path === "/api/v1/workflow/execute-action" && method === "POST") {
|
|
14287
|
+
const body = JSON.parse(await readBody(req));
|
|
14288
|
+
const result = store.executeAction(body);
|
|
14289
|
+
return json(res, 200, result);
|
|
14290
|
+
}
|
|
14291
|
+
const dataMatch = path.match(/^\/api\/v1\/data\/([^/]+)$/);
|
|
14292
|
+
if (dataMatch && method === "GET") {
|
|
14293
|
+
const def = store.getDefinition(dataMatch[1]);
|
|
14294
|
+
if (!def) return json(res, 404, { error: "Not found" });
|
|
14295
|
+
const instances = store.listInstances({ definition_id: def.id });
|
|
14296
|
+
return json(res, 200, instances);
|
|
14297
|
+
}
|
|
14298
|
+
if (path.startsWith("/api/v1/")) {
|
|
14299
|
+
return json(res, 501, { error: "Not implemented in local dev mode", path, method });
|
|
14300
|
+
}
|
|
14301
|
+
return json(res, 404, { error: "Not found", path });
|
|
14302
|
+
} catch (err) {
|
|
14303
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
14304
|
+
console.error(`[mm-local] ${method} ${path} \u2014 Error: ${message}`);
|
|
14305
|
+
return json(res, 500, { error: message });
|
|
14306
|
+
}
|
|
14307
|
+
});
|
|
14308
|
+
return new Promise((resolve3, reject) => {
|
|
14309
|
+
server.on("error", (err) => {
|
|
14310
|
+
if (err.code === "EADDRINUSE") {
|
|
14311
|
+
reject(new Error(`Port ${port} is already in use. Is another server running?`));
|
|
14312
|
+
} else {
|
|
14313
|
+
reject(err);
|
|
14314
|
+
}
|
|
14315
|
+
});
|
|
14316
|
+
server.listen(port, () => {
|
|
14317
|
+
console.log(`[mm-local] Local API server running at http://localhost:${port}`);
|
|
14318
|
+
console.log(`[mm-local] Mode: in-memory (data lost on restart)`);
|
|
14319
|
+
console.log(`[mm-local] Auth: disabled (all requests accepted)`);
|
|
14320
|
+
resolve3({
|
|
14321
|
+
server,
|
|
14322
|
+
port,
|
|
14323
|
+
store,
|
|
14324
|
+
async close() {
|
|
14325
|
+
return new Promise((res) => {
|
|
14326
|
+
server.close(() => {
|
|
14327
|
+
console.log("[mm-local] Local API server stopped");
|
|
14328
|
+
res();
|
|
14329
|
+
});
|
|
14330
|
+
});
|
|
14331
|
+
}
|
|
14332
|
+
});
|
|
14333
|
+
});
|
|
14334
|
+
});
|
|
14335
|
+
}
|
|
14336
|
+
|
|
14337
|
+
// src/dev-server.ts
|
|
14338
|
+
var currentErrors = null;
|
|
14339
|
+
function escapeHtml(s) {
|
|
14340
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
14341
|
+
}
|
|
14342
|
+
function renderErrorOverlay(errors) {
|
|
14343
|
+
const cards = errors.map((err) => {
|
|
14344
|
+
const loc = err.line ? `${err.file}:${err.line}${err.column ? ":" + err.column : ""}` : err.file;
|
|
14345
|
+
const snippet = err.snippet ? `<pre style="background:#1a1a2e;color:#e0e0e0;padding:12px 16px;border-radius:6px;overflow-x:auto;margin-top:8px;font-size:13px;line-height:1.5">${escapeHtml(err.snippet)}</pre>` : "";
|
|
14346
|
+
return `<div style="background:#2d1b1b;border:1px solid #5c2020;border-radius:8px;padding:16px 20px;margin-bottom:12px">
|
|
14347
|
+
<div style="color:#ff6b6b;font-family:monospace;font-size:13px;margin-bottom:6px">${escapeHtml(loc)}</div>
|
|
14348
|
+
<div style="color:#ffa0a0;font-size:15px;font-weight:500">${escapeHtml(err.message)}</div>${snippet}</div>`;
|
|
14349
|
+
}).join("\n");
|
|
14350
|
+
return `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
14351
|
+
<title>Compile Error - MindMatrix Dev</title>
|
|
14352
|
+
<style>*{box-sizing:border-box;margin:0;padding:0}body{background:#1a1a2e;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;padding:40px 20px;min-height:100vh}</style></head>
|
|
14353
|
+
<body><div style="max-width:800px;margin:0 auto">
|
|
14354
|
+
<div style="display:flex;align-items:center;gap:12px;margin-bottom:24px">
|
|
14355
|
+
<div style="background:#5c2020;color:#ff6b6b;font-size:14px;font-weight:600;padding:4px 10px;border-radius:4px">COMPILE ERROR</div>
|
|
14356
|
+
<div style="color:#888;font-size:13px">${errors.length} error${errors.length !== 1 ? "s" : ""}</div></div>
|
|
14357
|
+
${cards}
|
|
14358
|
+
<div style="color:#666;font-size:12px;margin-top:24px;text-align:center">Fix the error and save -- the page will reload automatically.</div></div>
|
|
14359
|
+
<script>const ws=new WebSocket('ws://'+location.host+'/__mm_dev');ws.onmessage=e=>{const m=JSON.parse(e.data);if(m.type==='workflow:compiled'||m.type==='workflow:rebuild')location.reload()};ws.onclose=()=>setTimeout(()=>location.reload(),2000)</script>
|
|
14360
|
+
</body></html>`;
|
|
14361
|
+
}
|
|
14362
|
+
function errorOverlayMiddleware() {
|
|
14363
|
+
return (req, res, next) => {
|
|
14364
|
+
if (!currentErrors || !req.url) return next();
|
|
14365
|
+
const url = req.url;
|
|
14366
|
+
if (url.startsWith("/api") || url.startsWith("/health") || url.startsWith("/ws") || url.startsWith("/__mm_dev") || url.startsWith("/@") || url.startsWith("/node_modules")) return next();
|
|
14367
|
+
const accept = req.headers?.accept ?? "";
|
|
14368
|
+
if (!accept.includes("text/html")) return next();
|
|
14369
|
+
res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
|
|
14370
|
+
res.end(renderErrorOverlay(currentErrors));
|
|
14371
|
+
};
|
|
14372
|
+
}
|
|
14373
|
+
async function resolveDevToken(apiUrl, explicit) {
|
|
14374
|
+
if (explicit) return explicit;
|
|
14375
|
+
try {
|
|
14376
|
+
const { resolveToken: resolveToken2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
|
|
14377
|
+
const s = resolveToken2(apiUrl);
|
|
14378
|
+
if (s) return s;
|
|
14379
|
+
} catch {
|
|
14380
|
+
}
|
|
14381
|
+
try {
|
|
14382
|
+
const resp = await fetch(`${apiUrl}/auth/login`, {
|
|
14383
|
+
method: "POST",
|
|
14384
|
+
headers: { "Content-Type": "application/json" },
|
|
14385
|
+
body: JSON.stringify({ email: "admin@mindmatrix.com", password: "Admin123!" })
|
|
14386
|
+
});
|
|
14387
|
+
if (resp.ok) {
|
|
14388
|
+
const data = await resp.json();
|
|
14389
|
+
const token = data.token ?? data.access_token;
|
|
14390
|
+
if (token) {
|
|
14391
|
+
try {
|
|
14392
|
+
const { saveCredentials: saveCredentials2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
|
|
14393
|
+
saveCredentials2(apiUrl, token);
|
|
14394
|
+
} catch {
|
|
14395
|
+
}
|
|
14396
|
+
return token;
|
|
14397
|
+
}
|
|
14398
|
+
}
|
|
14399
|
+
} catch {
|
|
14400
|
+
}
|
|
14401
|
+
return process.env.MINDMATRIX_TOKEN;
|
|
14402
|
+
}
|
|
14403
|
+
async function checkBackendHealth(apiUrl) {
|
|
14404
|
+
try {
|
|
14405
|
+
const base = apiUrl.replace(/\/api\/v1\/?$/, "");
|
|
14406
|
+
const resp = await fetch(`${base}/health`, { signal: AbortSignal.timeout(5e3) });
|
|
14407
|
+
if (resp.ok) {
|
|
14408
|
+
const d = await resp.json();
|
|
14409
|
+
return { ok: true, version: d.version, db: d.database };
|
|
14410
|
+
}
|
|
14411
|
+
return { ok: resp.status < 500 };
|
|
14412
|
+
} catch {
|
|
14413
|
+
return { ok: false };
|
|
14414
|
+
}
|
|
14415
|
+
}
|
|
14416
|
+
async function initialBuildDeploy(src, outDir, mode, apiUrl, token) {
|
|
14417
|
+
console.log("[mm-dev] Compiling project...");
|
|
14418
|
+
const buildResult = await build({ src, outDir, mode, skipTypeCheck: true });
|
|
14419
|
+
if (buildResult.errors > 0) {
|
|
14420
|
+
currentErrors = buildResult.errorDetails.map((e) => ({ file: e.file, message: e.message, line: e.line }));
|
|
14421
|
+
console.error(`[mm-dev] Build failed with ${buildResult.errors} error(s) -- error overlay active`);
|
|
14422
|
+
return { buildResult, deployed: false, slug: void 0 };
|
|
14423
|
+
}
|
|
14424
|
+
currentErrors = null;
|
|
14425
|
+
if (buildResult.compiled === 0) return { buildResult, deployed: false, slug: void 0 };
|
|
14426
|
+
console.log(`[mm-dev] Compiled ${buildResult.compiled} definition(s)`);
|
|
14427
|
+
try {
|
|
14428
|
+
const { deploy: deploy2 } = await Promise.resolve().then(() => (init_deploy(), deploy_exports));
|
|
14429
|
+
const result = await deploy2({ apiUrl, token, dir: outDir });
|
|
14430
|
+
const total = result.created.length + result.updated.length + result.versioned.length;
|
|
14431
|
+
if (total > 0) console.log(`[mm-dev] Deployed: ${result.created.length} created, ${result.updated.length} updated`);
|
|
14432
|
+
return { buildResult, deployed: true, slug: buildResult.definitions?.[0]?.slug };
|
|
14433
|
+
} catch (e) {
|
|
14434
|
+
console.warn(`[mm-dev] Deploy failed: ${e instanceof Error ? e.message : e}`);
|
|
14435
|
+
return { buildResult, deployed: false, slug: void 0 };
|
|
14436
|
+
}
|
|
14437
|
+
}
|
|
14438
|
+
function printBanner(o) {
|
|
14439
|
+
const l = "-".repeat(58);
|
|
14440
|
+
console.log(`
|
|
14441
|
+
${l}
|
|
14442
|
+
MindMatrix Dev Server
|
|
14443
|
+
${l}
|
|
14444
|
+
`);
|
|
14445
|
+
console.log(` Preview: http://localhost:${o.port}`);
|
|
14446
|
+
console.log(` API Proxy: /api/* -> ${o.apiUrl}`);
|
|
14447
|
+
console.log(` WebSocket: ws://localhost:${o.port}/__mm_dev`);
|
|
14448
|
+
console.log(` Watching: ${o.src} (${o.include.join(", ")})
|
|
14449
|
+
`);
|
|
14450
|
+
console.log(` Backend: ${o.health.ok ? "connected" : "unreachable"}${o.health.version ? ` (v${o.health.version})` : ""}`);
|
|
14451
|
+
if (o.health.db) console.log(` Database: ${o.health.db}`);
|
|
14452
|
+
console.log(` Auth: ${o.token ? "authenticated" : "no token"}`);
|
|
14453
|
+
if (o.compiled > 0) {
|
|
14454
|
+
console.log(` Blueprint: ${o.slug ?? "unknown"} (${o.compiled} defs)`);
|
|
14455
|
+
console.log(` Deploy: ${o.deployed ? "synced" : "pending"}`);
|
|
14456
|
+
}
|
|
14457
|
+
if (o.errors > 0) console.log(` Errors: ${o.errors} compile error(s) -- overlay active`);
|
|
14458
|
+
console.log(`
|
|
14459
|
+
${l}
|
|
14460
|
+
`);
|
|
14461
|
+
}
|
|
14462
|
+
function broadcast(clients, data) {
|
|
14463
|
+
const msg = JSON.stringify(data);
|
|
14464
|
+
for (const c of clients) {
|
|
14465
|
+
try {
|
|
14466
|
+
if (c.readyState === 1) c.send(msg);
|
|
14467
|
+
} catch {
|
|
14468
|
+
clients.delete(c);
|
|
14469
|
+
}
|
|
14470
|
+
}
|
|
13370
14471
|
}
|
|
13371
|
-
|
|
13372
|
-
// src/dev-server.ts
|
|
13373
14472
|
async function createDevServer(options = {}) {
|
|
13374
14473
|
const {
|
|
13375
14474
|
port = 5199,
|
|
@@ -13378,44 +14477,87 @@ async function createDevServer(options = {}) {
|
|
|
13378
14477
|
include = ["**/*.workflow.tsx"],
|
|
13379
14478
|
outDir = "dist/workflows",
|
|
13380
14479
|
seed = false,
|
|
13381
|
-
apiUrl = "
|
|
13382
|
-
authToken
|
|
14480
|
+
apiUrl: rawApiUrl = "auto",
|
|
14481
|
+
authToken: explicitToken,
|
|
13383
14482
|
ws: enableWs = true,
|
|
13384
14483
|
open = false
|
|
13385
14484
|
} = options;
|
|
13386
14485
|
const clients = /* @__PURE__ */ new Set();
|
|
13387
|
-
|
|
13388
|
-
|
|
13389
|
-
|
|
13390
|
-
|
|
13391
|
-
|
|
13392
|
-
apiUrl
|
|
13393
|
-
|
|
13394
|
-
}
|
|
13395
|
-
|
|
13396
|
-
|
|
14486
|
+
let localServer = null;
|
|
14487
|
+
let apiUrl;
|
|
14488
|
+
let isLocalMode = false;
|
|
14489
|
+
if (rawApiUrl === "local") {
|
|
14490
|
+
localServer = await startLocalServer({ port: 4200 });
|
|
14491
|
+
apiUrl = "http://localhost:4200/api/v1";
|
|
14492
|
+
isLocalMode = true;
|
|
14493
|
+
} else if (rawApiUrl === "auto") {
|
|
14494
|
+
const defaultRemote = "https://dev.mindmatrix.club/api/v1";
|
|
14495
|
+
const remoteHealth = await checkBackendHealth(defaultRemote);
|
|
14496
|
+
if (remoteHealth.ok) {
|
|
14497
|
+
apiUrl = defaultRemote;
|
|
14498
|
+
} else {
|
|
14499
|
+
const localHealth = await checkBackendHealth("http://localhost:4200/api/v1");
|
|
14500
|
+
if (localHealth.ok) {
|
|
14501
|
+
apiUrl = "http://localhost:4200/api/v1";
|
|
14502
|
+
} else {
|
|
14503
|
+
console.log("[mm-dev] No backend detected \u2014 starting local in-memory API server...");
|
|
14504
|
+
localServer = await startLocalServer({ port: 4200 });
|
|
14505
|
+
apiUrl = "http://localhost:4200/api/v1";
|
|
14506
|
+
isLocalMode = true;
|
|
14507
|
+
}
|
|
14508
|
+
}
|
|
14509
|
+
} else {
|
|
14510
|
+
apiUrl = rawApiUrl;
|
|
14511
|
+
}
|
|
14512
|
+
const token = isLocalMode ? "dev-token-local" : await resolveDevToken(apiUrl, explicitToken);
|
|
14513
|
+
const health = isLocalMode ? { ok: true, version: "local", db: "in-memory" } : await checkBackendHealth(apiUrl);
|
|
14514
|
+
let initialSlug, initialCompiled = 0, initialDeployed = false, initialErrors = 0;
|
|
14515
|
+
if (token && health.ok) {
|
|
14516
|
+
const r = await initialBuildDeploy(src, outDir, mode, apiUrl, token);
|
|
14517
|
+
initialCompiled = r.buildResult.compiled;
|
|
14518
|
+
initialDeployed = r.deployed;
|
|
14519
|
+
initialSlug = r.slug;
|
|
14520
|
+
initialErrors = r.buildResult.errors;
|
|
14521
|
+
}
|
|
14522
|
+
if (seed && token && initialDeployed) {
|
|
14523
|
+
try {
|
|
14524
|
+
const { seedInstances: seedInstances2 } = await Promise.resolve().then(() => (init_seed(), seed_exports));
|
|
14525
|
+
console.log("[mm-dev] Seeding sample instances...");
|
|
14526
|
+
const sr = await seedInstances2({ apiUrl, token, dir: outDir });
|
|
14527
|
+
console.log(`[mm-dev] Seeded ${sr.created} instance(s) across ${sr.definitions} definition(s)`);
|
|
14528
|
+
} catch (e) {
|
|
14529
|
+
console.warn(`[mm-dev] Seed failed: ${e instanceof Error ? e.message : e}`);
|
|
14530
|
+
}
|
|
14531
|
+
}
|
|
14532
|
+
const pluginOpts = { mode, include, outDir, seedOnCompile: seed, apiUrl, authToken: token };
|
|
14533
|
+
const proxyTarget = apiUrl.replace(/\/api\/v1\/?$/, "") || apiUrl;
|
|
14534
|
+
let deployInFlight = false;
|
|
14535
|
+
const compileDeployPlugin = {
|
|
14536
|
+
name: "mindmatrix-dev-compile-deploy",
|
|
13397
14537
|
enforce: "post",
|
|
13398
|
-
handleHotUpdate(ctx) {
|
|
13399
|
-
const
|
|
13400
|
-
|
|
13401
|
-
|
|
13402
|
-
});
|
|
13403
|
-
|
|
13404
|
-
|
|
13405
|
-
|
|
13406
|
-
|
|
13407
|
-
|
|
13408
|
-
}
|
|
13409
|
-
|
|
14538
|
+
async handleHotUpdate(ctx) {
|
|
14539
|
+
const isWf = include.some((p) => new RegExp(p.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "<<G>>").replace(/\*/g, "[^/]*").replace(/<<G>>/g, ".*").replace(/\?/g, ".")).test(ctx.file));
|
|
14540
|
+
if (!isWf) return;
|
|
14541
|
+
const fn = ctx.file.split("/").pop() ?? ctx.file;
|
|
14542
|
+
console.log(`[mm-dev] Recompiling ${fn}...`);
|
|
14543
|
+
const br = await build({ src, outDir, mode, skipTypeCheck: true });
|
|
14544
|
+
if (br.errors > 0) {
|
|
14545
|
+
currentErrors = br.errorDetails.map((e) => ({ file: e.file, message: e.message, line: e.line }));
|
|
14546
|
+
broadcast(clients, { type: "workflow:error", errors: br.errors, timestamp: ctx.timestamp });
|
|
14547
|
+
return;
|
|
14548
|
+
}
|
|
14549
|
+
currentErrors = null;
|
|
14550
|
+
if (token && !deployInFlight) {
|
|
14551
|
+
deployInFlight = true;
|
|
13410
14552
|
try {
|
|
13411
|
-
|
|
13412
|
-
|
|
13413
|
-
}
|
|
14553
|
+
const { deploy: d } = await Promise.resolve().then(() => (init_deploy(), deploy_exports));
|
|
14554
|
+
await d({ apiUrl, token, dir: outDir });
|
|
13414
14555
|
} catch {
|
|
13415
|
-
|
|
14556
|
+
} finally {
|
|
14557
|
+
deployInFlight = false;
|
|
13416
14558
|
}
|
|
13417
14559
|
}
|
|
13418
|
-
|
|
14560
|
+
broadcast(clients, { type: "workflow:compiled", file: ctx.file, compiled: br.compiled, timestamp: ctx.timestamp });
|
|
13419
14561
|
}
|
|
13420
14562
|
};
|
|
13421
14563
|
const viteConfig = {
|
|
@@ -13423,311 +14565,93 @@ async function createDevServer(options = {}) {
|
|
|
13423
14565
|
server: {
|
|
13424
14566
|
port,
|
|
13425
14567
|
open,
|
|
13426
|
-
host: true
|
|
14568
|
+
host: true,
|
|
14569
|
+
proxy: {
|
|
14570
|
+
"/api": { target: proxyTarget, changeOrigin: true, secure: true, ws: true },
|
|
14571
|
+
"/health": { target: proxyTarget, changeOrigin: true, secure: true },
|
|
14572
|
+
"/ws": { target: proxyTarget.replace(/^http/, "ws"), changeOrigin: true, ws: true }
|
|
14573
|
+
}
|
|
13427
14574
|
},
|
|
13428
14575
|
plugins: [
|
|
13429
|
-
mindmatrixReact(
|
|
13430
|
-
|
|
14576
|
+
mindmatrixReact(pluginOpts),
|
|
14577
|
+
compileDeployPlugin,
|
|
14578
|
+
{ name: "mindmatrix-error-overlay", configureServer(server) {
|
|
14579
|
+
server.middlewares.use(errorOverlayMiddleware());
|
|
14580
|
+
} }
|
|
13431
14581
|
],
|
|
13432
|
-
logLevel: "
|
|
14582
|
+
logLevel: "warn"
|
|
13433
14583
|
};
|
|
13434
|
-
const { createServer } = await import("vite");
|
|
13435
|
-
const vite = await
|
|
14584
|
+
const { createServer: createServer2 } = await import("vite");
|
|
14585
|
+
const vite = await createServer2(viteConfig);
|
|
13436
14586
|
await vite.listen();
|
|
13437
14587
|
const resolvedPort = vite.config.server.port ?? port;
|
|
13438
14588
|
if (enableWs && vite.httpServer) {
|
|
13439
14589
|
try {
|
|
13440
|
-
const
|
|
13441
|
-
const
|
|
13442
|
-
if (
|
|
13443
|
-
const wss = new
|
|
13444
|
-
|
|
13445
|
-
|
|
13446
|
-
|
|
13447
|
-
|
|
13448
|
-
clients.add(socket);
|
|
13449
|
-
console.log(`[mm-dev] Editor connected (${clients.size} total)`);
|
|
13450
|
-
socket.on("close", () => {
|
|
13451
|
-
clients.delete(socket);
|
|
13452
|
-
console.log(`[mm-dev] Editor disconnected (${clients.size} total)`);
|
|
13453
|
-
});
|
|
13454
|
-
socket.send(JSON.stringify({
|
|
13455
|
-
type: "mm:connected",
|
|
13456
|
-
version: "0.1.0",
|
|
13457
|
-
capabilities: ["compile", "seed", "notify"]
|
|
13458
|
-
}));
|
|
14590
|
+
const wsM = await Function('return import("ws")')();
|
|
14591
|
+
const WSS = wsM.WebSocketServer ?? wsM.default?.WebSocketServer;
|
|
14592
|
+
if (WSS) {
|
|
14593
|
+
const wss = new WSS({ server: vite.httpServer, path: "/__mm_dev" });
|
|
14594
|
+
wss.on("connection", (s) => {
|
|
14595
|
+
clients.add(s);
|
|
14596
|
+
s.on("close", () => clients.delete(s));
|
|
14597
|
+
s.send(JSON.stringify({ type: "mm:connected", version: "0.1.0", capabilities: ["compile", "deploy", "proxy", "notify", "error-overlay"] }));
|
|
13459
14598
|
});
|
|
13460
14599
|
}
|
|
13461
14600
|
} catch {
|
|
13462
|
-
console.warn("[mm-dev] ws package not available \u2014 WebSocket notifications disabled");
|
|
13463
14601
|
}
|
|
13464
14602
|
}
|
|
13465
|
-
|
|
13466
|
-
[mm-dev] MindMatrix Dev Server running`);
|
|
13467
|
-
console.log(` Local: http://localhost:${resolvedPort}`);
|
|
13468
|
-
console.log(` WebSocket: ws://localhost:${resolvedPort}/__mm_dev`);
|
|
13469
|
-
console.log(` Watching: ${src} (${include.join(", ")})`);
|
|
13470
|
-
if (seed) {
|
|
13471
|
-
console.log(` Seeding: ${apiUrl}`);
|
|
13472
|
-
}
|
|
13473
|
-
console.log();
|
|
14603
|
+
printBanner({ port: resolvedPort, apiUrl: isLocalMode ? `${apiUrl} (local in-memory)` : apiUrl, src, include, health, token: !!token, compiled: initialCompiled, deployed: initialDeployed, slug: initialSlug, errors: initialErrors });
|
|
13474
14604
|
return {
|
|
13475
14605
|
vite,
|
|
13476
14606
|
port: resolvedPort,
|
|
13477
14607
|
clients,
|
|
13478
14608
|
async rebuild() {
|
|
13479
|
-
|
|
13480
|
-
|
|
13481
|
-
|
|
13482
|
-
|
|
13483
|
-
|
|
13484
|
-
|
|
13485
|
-
|
|
13486
|
-
|
|
13487
|
-
|
|
13488
|
-
|
|
13489
|
-
|
|
13490
|
-
} catch {
|
|
13491
|
-
clients.delete(client);
|
|
14609
|
+
const r = await build({ src, outDir, mode });
|
|
14610
|
+
if (r.errors > 0) {
|
|
14611
|
+
currentErrors = r.errorDetails.map((e) => ({ file: e.file, message: e.message, line: e.line }));
|
|
14612
|
+
} else {
|
|
14613
|
+
currentErrors = null;
|
|
14614
|
+
if (token) {
|
|
14615
|
+
try {
|
|
14616
|
+
const { deploy: d } = await Promise.resolve().then(() => (init_deploy(), deploy_exports));
|
|
14617
|
+
await d({ apiUrl, token, dir: outDir });
|
|
14618
|
+
} catch {
|
|
14619
|
+
}
|
|
13492
14620
|
}
|
|
13493
14621
|
}
|
|
13494
|
-
|
|
14622
|
+
broadcast(clients, { type: "workflow:rebuild", compiled: r.compiled, errors: r.errors, timestamp: Date.now() });
|
|
14623
|
+
return r;
|
|
13495
14624
|
},
|
|
13496
14625
|
async close() {
|
|
13497
|
-
for (const
|
|
14626
|
+
for (const c of clients) {
|
|
13498
14627
|
try {
|
|
13499
|
-
|
|
14628
|
+
c.close();
|
|
13500
14629
|
} catch {
|
|
13501
14630
|
}
|
|
13502
14631
|
}
|
|
13503
14632
|
clients.clear();
|
|
14633
|
+
currentErrors = null;
|
|
13504
14634
|
await vite.close();
|
|
13505
|
-
|
|
14635
|
+
if (localServer) {
|
|
14636
|
+
await localServer.close();
|
|
14637
|
+
}
|
|
13506
14638
|
}
|
|
13507
14639
|
};
|
|
13508
14640
|
}
|
|
13509
14641
|
|
|
13510
|
-
// src/
|
|
13511
|
-
|
|
13512
|
-
var import_fs4 = require("fs");
|
|
13513
|
-
var import_path3 = require("path");
|
|
13514
|
-
async function deploy(options) {
|
|
13515
|
-
const dir = options.dir ?? "dist/workflows";
|
|
13516
|
-
const targetLabel = options.targetName ? ` (target: ${options.targetName})` : "";
|
|
13517
|
-
console.log(`[mindmatrix-react] Deploying workflows from ${dir} to ${options.apiUrl}${targetLabel}...`);
|
|
13518
|
-
const files = await (0, import_glob2.glob)(`${dir}/**/*.workflow.json`);
|
|
13519
|
-
if (files.length === 0) {
|
|
13520
|
-
console.log(`[mindmatrix-react] No workflow files found in ${dir}`);
|
|
13521
|
-
return { created: [], updated: [], versioned: [], skipped: [], failed: [] };
|
|
13522
|
-
}
|
|
13523
|
-
const result = {
|
|
13524
|
-
created: [],
|
|
13525
|
-
updated: [],
|
|
13526
|
-
versioned: [],
|
|
13527
|
-
skipped: [],
|
|
13528
|
-
failed: []
|
|
13529
|
-
};
|
|
13530
|
-
const deployedIRs = [];
|
|
13531
|
-
for (const file2 of files) {
|
|
13532
|
-
try {
|
|
13533
|
-
const ir = JSON.parse((0, import_fs4.readFileSync)(file2, "utf-8"));
|
|
13534
|
-
const slug = ir.slug;
|
|
13535
|
-
if (options.dryRun) {
|
|
13536
|
-
console.log(` [dry-run] Would deploy ${slug} (${(0, import_path3.basename)(file2)})`);
|
|
13537
|
-
continue;
|
|
13538
|
-
}
|
|
13539
|
-
const existing = await fetchExistingDefinition(options.apiUrl, options.token, slug);
|
|
13540
|
-
if (!existing) {
|
|
13541
|
-
await createDefinition(options.apiUrl, options.token, ir);
|
|
13542
|
-
console.log(` \u2713 ${slug} (created)`);
|
|
13543
|
-
result.created.push(slug);
|
|
13544
|
-
} else if (existing.instanceCount === 0 || options.force) {
|
|
13545
|
-
await updateDefinition(options.apiUrl, options.token, existing.id, ir);
|
|
13546
|
-
console.log(` \u2713 ${slug} (updated)`);
|
|
13547
|
-
result.updated.push(slug);
|
|
13548
|
-
} else {
|
|
13549
|
-
const newVersion = bumpVersion(existing.version);
|
|
13550
|
-
ir.version = newVersion;
|
|
13551
|
-
await createDefinition(options.apiUrl, options.token, ir);
|
|
13552
|
-
console.log(` \u2713 ${slug} v${newVersion} (new version, ${existing.instanceCount} instances on v${existing.version})`);
|
|
13553
|
-
result.versioned.push(slug);
|
|
13554
|
-
}
|
|
13555
|
-
deployedIRs.push(ir);
|
|
13556
|
-
} catch (error) {
|
|
13557
|
-
const slug = (0, import_path3.basename)(file2).replace(".workflow.json", "");
|
|
13558
|
-
const msg = error.message;
|
|
13559
|
-
console.error(` \u2717 ${slug}: ${msg}`);
|
|
13560
|
-
result.failed.push({ slug, error: msg });
|
|
13561
|
-
}
|
|
13562
|
-
}
|
|
13563
|
-
if (!options.dryRun) {
|
|
13564
|
-
for (const ir of deployedIRs) {
|
|
13565
|
-
const serviceResult = await syncServices(options.apiUrl, options.token, ir);
|
|
13566
|
-
if (serviceResult) {
|
|
13567
|
-
if (!result.services) {
|
|
13568
|
-
result.services = { registered: [], updated: [], failed: [] };
|
|
13569
|
-
}
|
|
13570
|
-
result.services.registered.push(...serviceResult.registered);
|
|
13571
|
-
result.services.updated.push(...serviceResult.updated);
|
|
13572
|
-
result.services.failed.push(...serviceResult.failed);
|
|
13573
|
-
}
|
|
13574
|
-
}
|
|
13575
|
-
}
|
|
13576
|
-
if (options.reportFile) {
|
|
13577
|
-
(0, import_fs4.writeFileSync)(options.reportFile, JSON.stringify(result, null, 2), "utf-8");
|
|
13578
|
-
console.log(`
|
|
13579
|
-
Report written to ${options.reportFile}`);
|
|
13580
|
-
}
|
|
13581
|
-
const total = result.created.length + result.updated.length + result.versioned.length;
|
|
13582
|
-
console.log(
|
|
13583
|
-
`
|
|
13584
|
-
[mindmatrix-react] Deployed ${total} workflows (${result.created.length} created, ${result.updated.length} updated, ${result.versioned.length} versioned, ${result.failed.length} failed)`
|
|
13585
|
-
);
|
|
13586
|
-
if (result.services) {
|
|
13587
|
-
const svcTotal = result.services.registered.length + result.services.updated.length;
|
|
13588
|
-
if (svcTotal > 0 || result.services.failed.length > 0) {
|
|
13589
|
-
console.log(
|
|
13590
|
-
`[mindmatrix-react] Synced ${svcTotal} services (${result.services.registered.length} registered, ${result.services.updated.length} updated, ${result.services.failed.length} failed)`
|
|
13591
|
-
);
|
|
13592
|
-
}
|
|
13593
|
-
}
|
|
13594
|
-
return result;
|
|
13595
|
-
}
|
|
13596
|
-
async function fetchExistingDefinition(apiUrl, token, slug) {
|
|
13597
|
-
try {
|
|
13598
|
-
const res = await fetch(`${apiUrl}/workflow/definitions?slug=${encodeURIComponent(slug)}`, {
|
|
13599
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
13600
|
-
});
|
|
13601
|
-
if (!res.ok) return null;
|
|
13602
|
-
const data = await res.json();
|
|
13603
|
-
const definitions = Array.isArray(data) ? data : data.items ?? data.data;
|
|
13604
|
-
if (!definitions || definitions.length === 0) return null;
|
|
13605
|
-
const def = definitions[0];
|
|
13606
|
-
return {
|
|
13607
|
-
id: def.id,
|
|
13608
|
-
slug: def.slug,
|
|
13609
|
-
version: def.version || "0.1.0",
|
|
13610
|
-
instanceCount: def.instanceCount ?? def.instance_count ?? 0
|
|
13611
|
-
};
|
|
13612
|
-
} catch {
|
|
13613
|
-
return null;
|
|
13614
|
-
}
|
|
13615
|
-
}
|
|
13616
|
-
async function createDefinition(apiUrl, token, ir) {
|
|
13617
|
-
const res = await fetch(`${apiUrl}/workflow/definitions`, {
|
|
13618
|
-
method: "POST",
|
|
13619
|
-
headers: {
|
|
13620
|
-
"Content-Type": "application/json",
|
|
13621
|
-
Authorization: `Bearer ${token}`
|
|
13622
|
-
},
|
|
13623
|
-
body: JSON.stringify(ir)
|
|
13624
|
-
});
|
|
13625
|
-
if (!res.ok) {
|
|
13626
|
-
const errorText = await res.text();
|
|
13627
|
-
throw new Error(`Create failed: ${res.status} ${errorText}`);
|
|
13628
|
-
}
|
|
13629
|
-
}
|
|
13630
|
-
async function updateDefinition(apiUrl, token, id, ir) {
|
|
13631
|
-
const { slug: _slug, ...updatePayload } = ir;
|
|
13632
|
-
const res = await fetch(`${apiUrl}/workflow/definitions/${id}`, {
|
|
13633
|
-
method: "PATCH",
|
|
13634
|
-
headers: {
|
|
13635
|
-
"Content-Type": "application/json",
|
|
13636
|
-
Authorization: `Bearer ${token}`
|
|
13637
|
-
},
|
|
13638
|
-
body: JSON.stringify(updatePayload)
|
|
13639
|
-
});
|
|
13640
|
-
if (!res.ok) {
|
|
13641
|
-
const errorText = await res.text();
|
|
13642
|
-
throw new Error(`Update failed: ${res.status} ${errorText}`);
|
|
13643
|
-
}
|
|
13644
|
-
}
|
|
13645
|
-
function bumpVersion(version) {
|
|
13646
|
-
const parts = version.split(".");
|
|
13647
|
-
if (parts.length !== 3) return `${version}.1`;
|
|
13648
|
-
const patch = parseInt(parts[2], 10) || 0;
|
|
13649
|
-
return `${parts[0]}.${parts[1]}.${patch + 1}`;
|
|
13650
|
-
}
|
|
13651
|
-
async function syncServices(apiUrl, token, ir) {
|
|
13652
|
-
const metadata = ir.metadata;
|
|
13653
|
-
const orchestration = metadata?.orchestration;
|
|
13654
|
-
const services = orchestration?.services;
|
|
13655
|
-
if (!services || Object.keys(services).length === 0) return null;
|
|
13656
|
-
const result = { registered: [], updated: [], failed: [] };
|
|
13657
|
-
for (const [name, config] of Object.entries(services)) {
|
|
13658
|
-
try {
|
|
13659
|
-
const registration = {
|
|
13660
|
-
name,
|
|
13661
|
-
connection: {
|
|
13662
|
-
type: config.type || "webhook",
|
|
13663
|
-
url: config.url,
|
|
13664
|
-
queue: config.queue
|
|
13665
|
-
},
|
|
13666
|
-
actions: config.actions || [],
|
|
13667
|
-
labels: config.labels || {}
|
|
13668
|
-
};
|
|
13669
|
-
const existing = await fetchExistingService(apiUrl, token, name);
|
|
13670
|
-
if (existing) {
|
|
13671
|
-
await fetch(`${apiUrl}/services/${existing.id}`, {
|
|
13672
|
-
method: "PATCH",
|
|
13673
|
-
headers: {
|
|
13674
|
-
"Content-Type": "application/json",
|
|
13675
|
-
Authorization: `Bearer ${token}`
|
|
13676
|
-
},
|
|
13677
|
-
body: JSON.stringify(registration)
|
|
13678
|
-
});
|
|
13679
|
-
console.log(` \u2713 service: ${name} (updated)`);
|
|
13680
|
-
result.updated.push(name);
|
|
13681
|
-
} else {
|
|
13682
|
-
const res = await fetch(`${apiUrl}/services`, {
|
|
13683
|
-
method: "POST",
|
|
13684
|
-
headers: {
|
|
13685
|
-
"Content-Type": "application/json",
|
|
13686
|
-
Authorization: `Bearer ${token}`
|
|
13687
|
-
},
|
|
13688
|
-
body: JSON.stringify(registration)
|
|
13689
|
-
});
|
|
13690
|
-
if (!res.ok) {
|
|
13691
|
-
const errorText = await res.text();
|
|
13692
|
-
throw new Error(`Register failed: ${res.status} ${errorText}`);
|
|
13693
|
-
}
|
|
13694
|
-
console.log(` \u2713 service: ${name} (registered)`);
|
|
13695
|
-
result.registered.push(name);
|
|
13696
|
-
}
|
|
13697
|
-
} catch (error) {
|
|
13698
|
-
const msg = error.message;
|
|
13699
|
-
console.error(` \u2717 service: ${name}: ${msg}`);
|
|
13700
|
-
result.failed.push(name);
|
|
13701
|
-
}
|
|
13702
|
-
}
|
|
13703
|
-
return result;
|
|
13704
|
-
}
|
|
13705
|
-
async function fetchExistingService(apiUrl, token, name) {
|
|
13706
|
-
try {
|
|
13707
|
-
const res = await fetch(`${apiUrl}/services?name=${encodeURIComponent(name)}`, {
|
|
13708
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
13709
|
-
});
|
|
13710
|
-
if (!res.ok) return null;
|
|
13711
|
-
const data = await res.json();
|
|
13712
|
-
const servicesList = Array.isArray(data) ? data : data.services ?? data.items ?? data.data;
|
|
13713
|
-
if (!servicesList || servicesList.length === 0) return null;
|
|
13714
|
-
const found = servicesList.find((s) => s.name === name);
|
|
13715
|
-
return found ? { id: found.id, name: found.name } : null;
|
|
13716
|
-
} catch {
|
|
13717
|
-
return null;
|
|
13718
|
-
}
|
|
13719
|
-
}
|
|
14642
|
+
// src/index.ts
|
|
14643
|
+
init_deploy();
|
|
13720
14644
|
|
|
13721
14645
|
// src/envelope.ts
|
|
13722
14646
|
var import_crypto3 = require("crypto");
|
|
13723
|
-
var
|
|
13724
|
-
var
|
|
13725
|
-
var
|
|
14647
|
+
var import_fs7 = require("fs");
|
|
14648
|
+
var import_path5 = require("path");
|
|
14649
|
+
var import_glob4 = require("glob");
|
|
13726
14650
|
var import_core7 = require("@babel/core");
|
|
13727
14651
|
init_babel();
|
|
13728
14652
|
async function buildEnvelope(projectDir, options = {}) {
|
|
13729
|
-
const root = (0,
|
|
13730
|
-
const slug = options.slug ?? (0,
|
|
14653
|
+
const root = (0, import_path5.resolve)(projectDir);
|
|
14654
|
+
const slug = options.slug ?? (0, import_path5.basename)(root);
|
|
13731
14655
|
const version = options.version ?? "0.1.0";
|
|
13732
14656
|
const mode = options.mode ?? "infer";
|
|
13733
14657
|
const patterns = options.include ?? [
|
|
@@ -13737,11 +14661,11 @@ async function buildEnvelope(projectDir, options = {}) {
|
|
|
13737
14661
|
];
|
|
13738
14662
|
const discoveredFiles = [];
|
|
13739
14663
|
for (const pattern of patterns) {
|
|
13740
|
-
const matches = await (0,
|
|
14664
|
+
const matches = await (0, import_glob4.glob)(pattern, { absolute: true });
|
|
13741
14665
|
discoveredFiles.push(...matches);
|
|
13742
14666
|
}
|
|
13743
14667
|
const uniqueFiles = [...new Set(discoveredFiles)];
|
|
13744
|
-
const allSourceFiles = await (0,
|
|
14668
|
+
const allSourceFiles = await (0, import_glob4.glob)(`${root}/**/*.{ts,tsx,js,jsx,css,json}`, {
|
|
13745
14669
|
absolute: true,
|
|
13746
14670
|
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"]
|
|
13747
14671
|
});
|
|
@@ -13751,10 +14675,10 @@ async function buildEnvelope(projectDir, options = {}) {
|
|
|
13751
14675
|
const errors = [];
|
|
13752
14676
|
const sourceBindings = [];
|
|
13753
14677
|
for (const file2 of uniqueFiles) {
|
|
13754
|
-
const relativePath = (0,
|
|
14678
|
+
const relativePath = (0, import_path5.relative)(root, file2);
|
|
13755
14679
|
let fileContent;
|
|
13756
14680
|
try {
|
|
13757
|
-
fileContent = (0,
|
|
14681
|
+
fileContent = (0, import_fs7.readFileSync)(file2);
|
|
13758
14682
|
} catch {
|
|
13759
14683
|
errors.push({ file: relativePath, message: "Could not read file" });
|
|
13760
14684
|
continue;
|