@fleox-agent-crm/agentcrm 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/LICENSE +143 -0
  2. package/README.md +223 -2
  3. package/changelog.json +57 -0
  4. package/dist/admin/admin-files.json +26 -0
  5. package/dist/admin/assets/chartsImpl-CE2akJat.js +91 -0
  6. package/dist/admin/assets/firebase-BHSUggRo.js +2 -0
  7. package/dist/admin/assets/gitJson-BulGh2KV.js +3 -0
  8. package/dist/admin/assets/index-DidVVK7L.js +1181 -0
  9. package/dist/admin/assets/index-hMXwHSvN.css +1 -0
  10. package/dist/admin/assets/index.esm-BFc7vwnl.js +400 -0
  11. package/dist/admin/assets/index.esm-CtEQZRb1.js +1651 -0
  12. package/dist/admin/assets/index.esm-Dum0J07z.js +16 -0
  13. package/dist/admin/assets/index.esm2017-EOzKbpUS.js +390 -0
  14. package/dist/admin/assets/lucide-react.min-DEDlHCPj.js +6 -0
  15. package/dist/admin/assets/sdk-CnrLnlRo.js +1717 -0
  16. package/dist/admin/config.js +3 -0
  17. package/dist/admin/favicon.svg +1 -0
  18. package/dist/admin/index.html +139 -0
  19. package/dist/admin/runtime/agentcrm-runtime.js +104 -0
  20. package/dist/admin/runtime/lucide-react.js +791 -0
  21. package/dist/admin/runtime/react-dom-client.js +17 -0
  22. package/dist/admin/runtime/react-dom.js +20 -0
  23. package/dist/admin/runtime/react-i18next.js +19 -0
  24. package/dist/admin/runtime/react-jsx-runtime.js +17 -0
  25. package/dist/admin/runtime/react.js +22 -0
  26. package/dist/agent/agentcrm.mjs +3932 -0
  27. package/dist/agent/chunks/_commonjsHelpers-ClciMIcB.mjs +14 -0
  28. package/dist/agent/chunks/activities-Cj_SIOay.mjs +227 -0
  29. package/dist/agent/chunks/api-B6OqNVWp.mjs +97 -0
  30. package/dist/agent/chunks/appearance-BaShelzt.mjs +174 -0
  31. package/dist/agent/chunks/automations-BWrvLqP6.mjs +698 -0
  32. package/dist/agent/chunks/backup-ClSmxmFZ.mjs +103 -0
  33. package/dist/agent/chunks/batch-CVETiJj5.mjs +142 -0
  34. package/dist/agent/chunks/compact-CsFPaAAq.mjs +69 -0
  35. package/dist/agent/chunks/context-CxFfqS4D.mjs +65 -0
  36. package/dist/agent/chunks/core-Dy7vY9H6.mjs +255 -0
  37. package/dist/agent/chunks/dedupe-BepmsaHv.mjs +123 -0
  38. package/dist/agent/chunks/encoding-C9MzdP95.mjs +36 -0
  39. package/dist/agent/chunks/exportCsv-B5hRvkNm.mjs +95 -0
  40. package/dist/agent/chunks/extCheck--7lmVvl7.mjs +193 -0
  41. package/dist/agent/chunks/extPackage-BGl5Oqs8.mjs +115 -0
  42. package/dist/agent/chunks/extScaffold-DqxI7R1e.mjs +320 -0
  43. package/dist/agent/chunks/extensionActions-D2HtpQz4.mjs +48 -0
  44. package/dist/agent/chunks/extensions-BP1k60md.mjs +151 -0
  45. package/dist/agent/chunks/featureRegistry-C9nbVShp.mjs +149 -0
  46. package/dist/agent/chunks/fields-B0bitgmr.mjs +476 -0
  47. package/dist/agent/chunks/files-CjsPJSR6.mjs +158 -0
  48. package/dist/agent/chunks/files-q8Xu5bft.mjs +367 -0
  49. package/dist/agent/chunks/firebase-DxGVQV9E.mjs +454 -0
  50. package/dist/agent/chunks/firebaseRules-iori-s7U.mjs +221 -0
  51. package/dist/agent/chunks/forms-BKU5F9WF.mjs +847 -0
  52. package/dist/agent/chunks/gitJson-fjr8mcHe.mjs +528 -0
  53. package/dist/agent/chunks/hosting-Bme7LxPc.mjs +144 -0
  54. package/dist/agent/chunks/http-Bk9ICb7I.mjs +127 -0
  55. package/dist/agent/chunks/ids-CvnmrMJX.mjs +59 -0
  56. package/dist/agent/chunks/ids-u-noYcy5.mjs +14 -0
  57. package/dist/agent/chunks/importCsv-DmE_sBch.mjs +334 -0
  58. package/dist/agent/chunks/index-B-4W0-P4.mjs +535 -0
  59. package/dist/agent/chunks/index-BM8qeGOb.mjs +2489 -0
  60. package/dist/agent/chunks/index-Bnmav-Oh.mjs +311 -0
  61. package/dist/agent/chunks/index-CRkw3_iF.mjs +147 -0
  62. package/dist/agent/chunks/index-CWrrF1wl.mjs +111 -0
  63. package/dist/agent/chunks/index-DUSdTBkz.mjs +2988 -0
  64. package/dist/agent/chunks/index-RCmo-uld.mjs +26 -0
  65. package/dist/agent/chunks/index-w03ReRs4.mjs +9780 -0
  66. package/dist/agent/chunks/install-CpHoUFtL.mjs +188 -0
  67. package/dist/agent/chunks/jsx-runtime-Ca2uwGDr.mjs +76 -0
  68. package/dist/agent/chunks/loader-C9nm3iwx.mjs +184 -0
  69. package/dist/agent/chunks/lucide-react.min-Bt3gtepY.mjs +64 -0
  70. package/dist/agent/chunks/memory-WNR3VHUJ.mjs +56 -0
  71. package/dist/agent/chunks/migrate-CsClnnN8.mjs +182 -0
  72. package/dist/agent/chunks/modules-CqdVQVJb.mjs +177 -0
  73. package/dist/agent/chunks/nodeImport-BPCt5uMu.mjs +38 -0
  74. package/dist/agent/chunks/package-DWbn3t2R.mjs +194 -0
  75. package/dist/agent/chunks/papaparse-DjBYnQM2.mjs +1285 -0
  76. package/dist/agent/chunks/pipelines-DCdRBNBV.mjs +234 -0
  77. package/dist/agent/chunks/plan-C0TBrU92.mjs +254 -0
  78. package/dist/agent/chunks/pluginRegistry-DyHtlkcr.mjs +83 -0
  79. package/dist/agent/chunks/query-C5nJB_3C.mjs +192 -0
  80. package/dist/agent/chunks/records-DoqiT9xR.mjs +2191 -0
  81. package/dist/agent/chunks/registry-Ceexawxl.mjs +182 -0
  82. package/dist/agent/chunks/registry-DijAKZ24.mjs +431 -0
  83. package/dist/agent/chunks/relations-B9TynBmA.mjs +191 -0
  84. package/dist/agent/chunks/reports-BB59N6ke.mjs +189 -0
  85. package/dist/agent/chunks/runtime-DHo7ZEEO.mjs +14852 -0
  86. package/dist/agent/chunks/runtimeConfig-CCOFg1RM.mjs +250 -0
  87. package/dist/agent/chunks/s3-Dz50-NuA.mjs +903 -0
  88. package/dist/agent/chunks/schema-7-4-qtSS.mjs +203 -0
  89. package/dist/agent/chunks/schema-DlHt2PxI.mjs +367 -0
  90. package/dist/agent/chunks/sdk-whvfg786.mjs +43644 -0
  91. package/dist/agent/chunks/seed-CugPGYZL.mjs +523 -0
  92. package/dist/agent/chunks/server-ISwlDRCN.mjs +242 -0
  93. package/dist/agent/chunks/settings-BtvR_jdm.mjs +76 -0
  94. package/dist/agent/chunks/standardObjects-CheqfDqa.mjs +243 -0
  95. package/dist/agent/chunks/stats-CfeJssSS.mjs +51 -0
  96. package/dist/agent/chunks/store-5gqRSKXg.mjs +122 -0
  97. package/dist/agent/chunks/themes-DPZFv3yc.mjs +43 -0
  98. package/dist/agent/chunks/tools-Bew8lxKx.mjs +844 -0
  99. package/dist/agent/chunks/types-BOy9PUl7.mjs +44 -0
  100. package/dist/agent/chunks/users-DI1BvS0f.mjs +107 -0
  101. package/dist/agent/chunks/utils-DmI8FtXP.mjs +83 -0
  102. package/dist/agent/chunks/views-fO_BM5U_.mjs +109 -0
  103. package/dist/version.json +7 -0
  104. package/package.json +97 -4
  105. package/plugin/.claude-plugin/plugin.json +17 -0
  106. package/plugin/skills/agentcrm-admin/SKILL.md +191 -0
  107. package/plugin/skills/agentcrm-admin/references/appearance.md +26 -0
  108. package/plugin/skills/agentcrm-admin/references/import.md +16 -0
  109. package/plugin/skills/agentcrm-admin/references/records.md +30 -0
  110. package/plugin/skills/agentcrm-admin/references/schema.md +59 -0
  111. package/plugin/skills/agentcrm-extensions/SKILL.md +93 -0
  112. package/plugin/skills/agentcrm-extensions/references/module-recipes.md +213 -0
  113. package/plugin/skills/agentcrm-extensions/references/runtime-api.md +41 -0
  114. package/plugin/skills/agentcrm-extensions/references/testing.md +76 -0
  115. package/plugin/skills/agentcrm-extensions/references/theme-recipes.md +65 -0
  116. package/plugin/skills/agentcrm-install/SKILL.md +116 -0
  117. package/plugin/skills/agentcrm-install/references/firebase.md +18 -0
  118. package/plugin/skills/agentcrm-install/references/github.md +20 -0
  119. package/plugin/skills/agentcrm-install/references/mcp.md +17 -0
  120. package/plugin/skills/agentcrm-install/references/plans.md +45 -0
  121. package/plugin/skills/agentcrm-install/references/s3.md +24 -0
  122. package/plugin/skills/agentcrm-manage/SKILL.md +108 -0
  123. package/plugin/skills/agentcrm-manage/references/cron.md +11 -0
  124. package/plugin/skills/agentcrm-manage/references/doctor.md +18 -0
  125. package/plugin/skills/agentcrm-sales/SKILL.md +92 -0
  126. package/plugin/skills/agentcrm-sales/references/enrichment.md +13 -0
  127. package/plugin/skills/agentcrm-sales/references/playbooks.md +29 -0
  128. package/plugin/skills/agentcrm-sales/references/weekly-report.md +27 -0
  129. package/types/agentcrm-runtime.d.ts +894 -0
@@ -0,0 +1,103 @@
1
+ import { createRequire as __acrmRequire } from "node:module";
2
+ import { fileURLToPath as __acrmPath } from "node:url";
3
+ import { dirname as __acrmDirname } from "node:path";
4
+ const require2 = __acrmRequire(import.meta.url);
5
+ const __filename = __acrmPath(import.meta.url);
6
+ const __dirname = __acrmDirname(__filename);
7
+ import { J as JSZip } from "./index-w03ReRs4.mjs";
8
+ import { F as FILE_ID_RE } from "./ids-u-noYcy5.mjs";
9
+ import { assertCan, OpsError } from "./context-CxFfqS4D.mjs";
10
+ import { exportJson, importJson } from "./exportCsv-B5hRvkNm.mjs";
11
+ import { filesOf } from "./store-5gqRSKXg.mjs";
12
+ import "./_commonjsHelpers-ClciMIcB.mjs";
13
+ import "stream";
14
+ import "events";
15
+ import "buffer";
16
+ import "util";
17
+ import "./pluginRegistry-DyHtlkcr.mjs";
18
+ import "./papaparse-DjBYnQM2.mjs";
19
+ import "./fields-B0bitgmr.mjs";
20
+ import "./query-C5nJB_3C.mjs";
21
+ import "./types-BOy9PUl7.mjs";
22
+ import "./records-DoqiT9xR.mjs";
23
+ import "./ids-CvnmrMJX.mjs";
24
+ import "./standardObjects-CheqfDqa.mjs";
25
+ import "./relations-B9TynBmA.mjs";
26
+ const BACKUP_DATA_FILE = "data.json";
27
+ const BACKUP_FILES_DIR = "files/";
28
+ async function exportBackupZip(ctx, opts = {}) {
29
+ assertCan(ctx, "export");
30
+ const zip = new JSZip();
31
+ const data = exportJson(ctx);
32
+ zip.file(BACKUP_DATA_FILE, JSON.stringify(data, null, 2));
33
+ const report = { files: 0, missing: [], bytes: 0 };
34
+ if (opts.includeFiles !== false && ctx.store.files) {
35
+ for (const f of filesOf(ctx.store)) {
36
+ for (const id of [f.id, ...f.thumbId ? [f.thumbId] : []]) {
37
+ const bytes = await ctx.store.files.get(id).catch(() => null);
38
+ if (!bytes) {
39
+ if (id === f.id) report.missing.push(f.id);
40
+ continue;
41
+ }
42
+ zip.file(`${BACKUP_FILES_DIR}${id}.bin`, bytes, { binary: true });
43
+ report.bytes += bytes.length;
44
+ if (id === f.id) report.files++;
45
+ }
46
+ }
47
+ }
48
+ const out = await zip.generateAsync({ type: "uint8array", compression: "DEFLATE", compressionOptions: { level: 6 } });
49
+ return { zip: out, report };
50
+ }
51
+ async function importBackupZip(ctx, input, opts = {}) {
52
+ assertCan(ctx, "settings.write");
53
+ let zip;
54
+ try {
55
+ zip = await JSZip.loadAsync(input);
56
+ } catch {
57
+ throw new OpsError("validation", "Not a ZIP archive.");
58
+ }
59
+ const entry = zip.file(BACKUP_DATA_FILE);
60
+ if (!entry) throw new OpsError("validation", `Not an AgentCRM backup (no ${BACKUP_DATA_FILE}).`);
61
+ let data;
62
+ try {
63
+ data = JSON.parse(await entry.async("string"));
64
+ } catch {
65
+ throw new OpsError("validation", `${BACKUP_DATA_FILE} is not valid JSON.`);
66
+ }
67
+ let files = 0;
68
+ const blobs = Object.keys(zip.files).filter((p) => p.startsWith(BACKUP_FILES_DIR) && p.endsWith(".bin"));
69
+ if (blobs.length) {
70
+ if (!ctx.store.files) throw new OpsError("invalid", `The "${ctx.store.kind}" backend can't store files: restore the data JSON only.`);
71
+ const metaIds = new Set((data.collections.files ?? []).map((f) => f.id));
72
+ for (const p of blobs) {
73
+ const id = p.slice(BACKUP_FILES_DIR.length, -".bin".length);
74
+ if (!FILE_ID_RE.test(id)) continue;
75
+ await ctx.store.files.put(id, await zip.file(p).async("uint8array"));
76
+ if (metaIds.has(id)) files++;
77
+ }
78
+ }
79
+ const res = await importJson(ctx, data, { mode: opts.mode });
80
+ return { ...res, files };
81
+ }
82
+ async function readBackupZipInfo(input) {
83
+ let zip;
84
+ try {
85
+ zip = await JSZip.loadAsync(input);
86
+ } catch {
87
+ throw new OpsError("validation", "Not a ZIP archive.");
88
+ }
89
+ const entry = zip.file(BACKUP_DATA_FILE);
90
+ if (!entry) throw new OpsError("validation", `Not an AgentCRM backup (no ${BACKUP_DATA_FILE}).`);
91
+ const data = JSON.parse(await entry.async("string"));
92
+ if (data?.format !== "agentcrm-export") throw new OpsError("validation", "Not an AgentCRM export.");
93
+ const metaIds = new Set((data.collections.files ?? []).map((f) => f.id));
94
+ const files = Object.keys(zip.files).filter((p) => p.startsWith(BACKUP_FILES_DIR) && metaIds.has(p.slice(BACKUP_FILES_DIR.length, -".bin".length))).length;
95
+ return { data, files };
96
+ }
97
+ export {
98
+ BACKUP_DATA_FILE,
99
+ BACKUP_FILES_DIR,
100
+ exportBackupZip,
101
+ importBackupZip,
102
+ readBackupZipInfo
103
+ };
@@ -0,0 +1,142 @@
1
+ import { createRequire as __acrmRequire } from "node:module";
2
+ import { fileURLToPath as __acrmPath } from "node:url";
3
+ import { dirname as __acrmDirname } from "node:path";
4
+ const require2 = __acrmRequire(import.meta.url);
5
+ const __filename = __acrmPath(import.meta.url);
6
+ const __dirname = __acrmDirname(__filename);
7
+ import { g as getActiveStorageConnector } from "./registry-DijAKZ24.mjs";
8
+ import "./index-BM8qeGOb.mjs";
9
+ import "./http-Bk9ICb7I.mjs";
10
+ import "./index-w03ReRs4.mjs";
11
+ import "./_commonjsHelpers-ClciMIcB.mjs";
12
+ import "stream";
13
+ import "events";
14
+ import "buffer";
15
+ import "util";
16
+ import "./encoding-C9MzdP95.mjs";
17
+ import "./s3-Dz50-NuA.mjs";
18
+ import "./runtimeConfig-CCOFg1RM.mjs";
19
+ let open = null;
20
+ function normalizePrefix(path) {
21
+ return path.replace(/^\/+|\/+$/g, "");
22
+ }
23
+ function isUnder(path, prefix) {
24
+ return path === prefix || path.startsWith(prefix + "/");
25
+ }
26
+ function currentBatch() {
27
+ return open;
28
+ }
29
+ function stagePut(batch, input) {
30
+ batch.files.set(input.path, { type: "put", ...input });
31
+ }
32
+ function stageDelete(batch, path) {
33
+ batch.files.set(path, { type: "delete", path });
34
+ }
35
+ function stageDeletePrefix(batch, path) {
36
+ const prefix = normalizePrefix(path);
37
+ for (const key of [...batch.files.keys()]) {
38
+ if (isUnder(key, prefix)) batch.files.delete(key);
39
+ }
40
+ batch.prefixes.add(prefix);
41
+ }
42
+ function readStaged(batch, path) {
43
+ const op = batch.files.get(path);
44
+ if (op?.type === "put") {
45
+ if (op.encoding !== "base64") return { kind: "content", content: op.content };
46
+ const bytes = Uint8Array.from(atob(op.content), (c) => c.charCodeAt(0));
47
+ return { kind: "content", content: new TextDecoder().decode(bytes) };
48
+ }
49
+ if (op?.type === "delete") return { kind: "deleted" };
50
+ for (const prefix of batch.prefixes) {
51
+ if (isUnder(path, prefix)) return { kind: "deleted" };
52
+ }
53
+ return null;
54
+ }
55
+ function collectOps(batch) {
56
+ const ops = [];
57
+ for (const path of batch.prefixes) ops.push({ type: "deletePrefix", path });
58
+ for (const op of batch.files.values()) ops.push(op);
59
+ return ops;
60
+ }
61
+ const IMPLICIT_IDLE_MS = 1500;
62
+ let implicit = null;
63
+ function scheduleImplicitFlush() {
64
+ if (!implicit) return;
65
+ if (implicit.timer) clearTimeout(implicit.timer);
66
+ implicit.timer = setTimeout(() => void flushImplicitBatch(), IMPLICIT_IDLE_MS);
67
+ }
68
+ function implicitBatchFor(connector) {
69
+ if (!implicit || implicit.connector !== connector) {
70
+ implicit = { connector, depth: 0, files: /* @__PURE__ */ new Map(), prefixes: /* @__PURE__ */ new Set(), timer: null, flushing: null };
71
+ }
72
+ scheduleImplicitFlush();
73
+ return implicit;
74
+ }
75
+ function pendingImplicitBatch() {
76
+ return implicit;
77
+ }
78
+ async function flushImplicitBatch() {
79
+ const batch = implicit;
80
+ if (!batch) return;
81
+ if (batch.timer) clearTimeout(batch.timer);
82
+ implicit = null;
83
+ const ops = collectOps(batch);
84
+ if (ops.length === 0) return;
85
+ try {
86
+ await batch.connector.commitBatch(ops);
87
+ } catch (err) {
88
+ console.error("[storage] deferred commit failed:", err);
89
+ }
90
+ }
91
+ if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
92
+ window.addEventListener("beforeunload", (e) => {
93
+ if (implicit && (implicit.files.size > 0 || implicit.prefixes.size > 0)) {
94
+ void flushImplicitBatch();
95
+ e.preventDefault();
96
+ }
97
+ });
98
+ }
99
+ async function withStorageBatch(fn) {
100
+ const connector = await getActiveStorageConnector();
101
+ if (!connector.commitBatch) return fn();
102
+ await flushImplicitBatch();
103
+ if (!open) open = { connector, depth: 0, files: /* @__PURE__ */ new Map(), prefixes: /* @__PURE__ */ new Set() };
104
+ const batch = open;
105
+ batch.depth++;
106
+ let failed = false;
107
+ let error;
108
+ let result;
109
+ try {
110
+ result = await fn();
111
+ } catch (err) {
112
+ failed = true;
113
+ error = err;
114
+ }
115
+ batch.depth--;
116
+ if (batch.depth === 0) {
117
+ open = null;
118
+ const ops = collectOps(batch);
119
+ if (ops.length > 0) {
120
+ try {
121
+ await batch.connector.commitBatch(ops);
122
+ } catch (commitErr) {
123
+ if (!failed) throw commitErr;
124
+ console.error("[storage] batch commit failed after an earlier error:", commitErr);
125
+ }
126
+ }
127
+ }
128
+ if (failed) throw error;
129
+ return result;
130
+ }
131
+ export {
132
+ IMPLICIT_IDLE_MS,
133
+ currentBatch,
134
+ flushImplicitBatch,
135
+ implicitBatchFor,
136
+ pendingImplicitBatch,
137
+ readStaged,
138
+ stageDelete,
139
+ stageDeletePrefix,
140
+ stagePut,
141
+ withStorageBatch
142
+ };
@@ -0,0 +1,69 @@
1
+ import { createRequire as __acrmRequire } from "node:module";
2
+ import { fileURLToPath as __acrmPath } from "node:url";
3
+ import { dirname as __acrmDirname } from "node:path";
4
+ const require2 = __acrmRequire(import.meta.url);
5
+ const __filename = __acrmPath(import.meta.url);
6
+ const __dirname = __acrmDirname(__filename);
7
+ import { assertCan } from "./context-CxFfqS4D.mjs";
8
+ import { b as fileCleanupMutations } from "./relations-B9TynBmA.mjs";
9
+ import { purgeFiles } from "./files-q8Xu5bft.mjs";
10
+ import { hardDeleteMutations } from "./records-DoqiT9xR.mjs";
11
+ import { recordsOf, activitiesOf, automationRunsOf } from "./store-5gqRSKXg.mjs";
12
+ import "./pluginRegistry-DyHtlkcr.mjs";
13
+ import "./fields-B0bitgmr.mjs";
14
+ import "./files-CjsPJSR6.mjs";
15
+ import "./ids-CvnmrMJX.mjs";
16
+ import "./index-RCmo-uld.mjs";
17
+ import "./http-Bk9ICb7I.mjs";
18
+ import "./index-BM8qeGOb.mjs";
19
+ import "./activities-Cj_SIOay.mjs";
20
+ import "./query-C5nJB_3C.mjs";
21
+ import "../agentcrm.mjs";
22
+ import "node:util";
23
+ import "node:fs";
24
+ import "node:path";
25
+ import "node:url";
26
+ import "node:crypto";
27
+ import "node:os";
28
+ import "node:child_process";
29
+ import "node:http";
30
+ import "./standardObjects-CheqfDqa.mjs";
31
+ async function compactData(ctx, opts = {}) {
32
+ assertCan(ctx, "compact");
33
+ const cutoff = ctx.now() - (opts.olderThanDays ?? 0) * 864e5;
34
+ const records = recordsOf(ctx.store, void 0, { includeDeleted: true }).filter((r) => r.deletedAt && r.deletedAt <= cutoff);
35
+ const ids = records.map((r) => r.id);
36
+ const mutations = ids.length ? hardDeleteMutations(ctx, ids) : [];
37
+ const alreadyDeleted = new Set(mutations.filter((m) => m.op === "delete" && m.collection === "activities").map((m) => m.id));
38
+ let purgedActivities = alreadyDeleted.size;
39
+ const purgedActivityIds = [];
40
+ for (const a of activitiesOf(ctx.store, { includeDeleted: true })) {
41
+ if (a.deletedAt && a.deletedAt <= cutoff && !alreadyDeleted.has(a.id)) {
42
+ mutations.push({ op: "delete", collection: "activities", id: a.id });
43
+ purgedActivityIds.push(a.id);
44
+ purgedActivities++;
45
+ }
46
+ }
47
+ mutations.push(...fileCleanupMutations(ctx, { activities: purgedActivityIds }));
48
+ const keep = opts.keepRuns ?? 500;
49
+ const runs = automationRunsOf(ctx.store).sort((a, b) => b.startedAt - a.startedAt);
50
+ const oldRuns = runs.slice(keep);
51
+ for (const r of oldRuns) mutations.push({ op: "delete", collection: "automationRuns", id: r.id });
52
+ const report = { purgedRecords: ids, purgedActivities, purgedRuns: oldRuns.length, purgedFiles: 0, storageCompacted: false, dryRun: !!opts.dryRun };
53
+ if (opts.dryRun) {
54
+ report.files = await purgeFiles(ctx, { olderThanDays: opts.olderThanDays, dryRun: true });
55
+ report.purgedFiles = report.files.purged.length;
56
+ return report;
57
+ }
58
+ if (mutations.length) await ctx.store.write(mutations, { message: `Purge ${ids.length} deleted records` });
59
+ report.files = await purgeFiles(ctx, { olderThanDays: opts.olderThanDays });
60
+ report.purgedFiles = report.files.purged.length;
61
+ if (ctx.store.compact) {
62
+ await ctx.store.compact();
63
+ report.storageCompacted = true;
64
+ }
65
+ return report;
66
+ }
67
+ export {
68
+ compactData
69
+ };
@@ -0,0 +1,65 @@
1
+ import { createRequire as __acrmRequire } from "node:module";
2
+ import { fileURLToPath as __acrmPath } from "node:url";
3
+ import { dirname as __acrmDirname } from "node:path";
4
+ const require2 = __acrmRequire(import.meta.url);
5
+ const __filename = __acrmPath(import.meta.url);
6
+ const __dirname = __acrmDirname(__filename);
7
+ import { plugins } from "./pluginRegistry-DyHtlkcr.mjs";
8
+ const READ = ["records.read", "activities.read", "views.read", "settings.read", "users.read", "reports.read"];
9
+ const MEMBER = [...READ, "records.write", "records.delete", "activities.write", "views.writeOwn", "import", "export", "automations.run"];
10
+ const ADMIN = [
11
+ ...MEMBER,
12
+ "views.writeShared",
13
+ "schema.write",
14
+ "pipelines.write",
15
+ "settings.write",
16
+ "users.write",
17
+ "automations.write",
18
+ "forms.write",
19
+ "compact",
20
+ "modules.write",
21
+ "appearance.write",
22
+ "extensions.write"
23
+ ];
24
+ const PERMISSIONS = {
25
+ admin: new Set(ADMIN),
26
+ member: new Set(MEMBER),
27
+ viewer: new Set(READ)
28
+ };
29
+ class OpsError extends Error {
30
+ constructor(code, message, details) {
31
+ super(message);
32
+ this.code = code;
33
+ this.details = details;
34
+ this.name = "OpsError";
35
+ }
36
+ }
37
+ function can(user, action) {
38
+ if (user === null) return true;
39
+ if (user.disabled) return false;
40
+ return PERMISSIONS[user.role]?.has(action) ?? false;
41
+ }
42
+ function assertCan(ctx, action) {
43
+ if (!can(ctx.user, action)) {
44
+ const who = ctx.user ? `${ctx.user.email || ctx.user.name} (${ctx.user.disabled ? "disabled" : ctx.user.role})` : "anonymous";
45
+ throw new OpsError("forbidden", `Not allowed: ${who} can't ${action.replace(".", " ")}.`);
46
+ }
47
+ }
48
+ function createOpsContext(store, user, opts = {}) {
49
+ return { store, user, now: opts.now ?? Date.now, log: opts.log, plugins: opts.plugins, depth: opts.depth };
50
+ }
51
+ async function emit(ctx, name, payload) {
52
+ await (ctx.plugins ?? plugins).doAction(name, ctx, payload);
53
+ }
54
+ function actorId(ctx) {
55
+ return ctx.user?.id;
56
+ }
57
+ export {
58
+ OpsError,
59
+ PERMISSIONS,
60
+ actorId,
61
+ assertCan,
62
+ can,
63
+ createOpsContext,
64
+ emit
65
+ };
@@ -0,0 +1,255 @@
1
+ import { createRequire as __acrmRequire } from "node:module";
2
+ import { fileURLToPath as __acrmPath } from "node:url";
3
+ import { dirname as __acrmDirname } from "node:path";
4
+ const require2 = __acrmRequire(import.meta.url);
5
+ const __filename = __acrmPath(import.meta.url);
6
+ const __dirname = __acrmDirname(__filename);
7
+ import { COLLECTIONS } from "./types-BOy9PUl7.mjs";
8
+ function isPlainObject(v) {
9
+ return !!v && typeof v === "object" && !Array.isArray(v) && Object.getPrototypeOf(v) === Object.prototype;
10
+ }
11
+ function splitPath(path) {
12
+ return path.split(".").filter(Boolean);
13
+ }
14
+ function setPath(target, path, value) {
15
+ if (path.length === 0) return target;
16
+ const [head, ...rest] = path;
17
+ const out = { ...target };
18
+ if (rest.length === 0) {
19
+ if (value === void 0) delete out[head];
20
+ else out[head] = value;
21
+ return out;
22
+ }
23
+ const child = isPlainObject(out[head]) ? out[head] : {};
24
+ out[head] = setPath(child, rest, value);
25
+ return out;
26
+ }
27
+ function getPath(target, path) {
28
+ let cur = target;
29
+ for (const p of path) {
30
+ if (!isPlainObject(cur)) return void 0;
31
+ cur = cur[p];
32
+ }
33
+ return cur;
34
+ }
35
+ function applyPatch(doc, fields) {
36
+ let out = { ...doc };
37
+ for (const [key, value] of Object.entries(fields)) {
38
+ const path = splitPath(key);
39
+ if (path[0] === "id") continue;
40
+ out = setPath(out, path, value);
41
+ }
42
+ return out;
43
+ }
44
+ function normalizeDoc(value) {
45
+ if (Array.isArray(value)) return Object.freeze(value.map(normalizeDoc));
46
+ if (isPlainObject(value)) {
47
+ const out = {};
48
+ for (const [k, v] of Object.entries(value)) if (v !== void 0) out[k] = normalizeDoc(v);
49
+ return Object.freeze(out);
50
+ }
51
+ return value;
52
+ }
53
+ function plainCopy(value) {
54
+ return value === void 0 ? value : JSON.parse(JSON.stringify(value));
55
+ }
56
+ function emptySnapshot() {
57
+ return Object.fromEntries(COLLECTIONS.map((c) => [c, /* @__PURE__ */ new Map()]));
58
+ }
59
+ function cloneSnapshot(s) {
60
+ return Object.fromEntries(COLLECTIONS.map((c) => [c, new Map(s[c] ?? [])]));
61
+ }
62
+ function snapshotFromDocs(docs) {
63
+ const s = emptySnapshot();
64
+ for (const c of COLLECTIONS) for (const d of docs[c] ?? []) s[c].set(d.id, normalizeDoc(d));
65
+ return s;
66
+ }
67
+ function applyMutations(base, mutations) {
68
+ const next = cloneSnapshot(base);
69
+ const changed = /* @__PURE__ */ new Set();
70
+ for (const m of mutations) {
71
+ if (m.op === "set") {
72
+ next[m.collection].set(m.doc.id, normalizeDoc(m.doc));
73
+ changed.add(m.collection);
74
+ } else if (m.op === "patch") {
75
+ const current = next[m.collection].get(m.id);
76
+ if (!current) continue;
77
+ next[m.collection].set(m.id, normalizeDoc(applyPatch(current, m.fields)));
78
+ changed.add(m.collection);
79
+ } else {
80
+ if (next[m.collection].delete(m.id)) changed.add(m.collection);
81
+ }
82
+ }
83
+ return { next, changed };
84
+ }
85
+ function diffCollections(a, b) {
86
+ const changed = /* @__PURE__ */ new Set();
87
+ for (const c of COLLECTIONS) {
88
+ if (a[c].size !== b[c].size) {
89
+ changed.add(c);
90
+ continue;
91
+ }
92
+ for (const [id, doc] of a[c]) {
93
+ const other = b[c].get(id);
94
+ if (other === doc) continue;
95
+ if (!other || JSON.stringify(other) !== JSON.stringify(doc)) {
96
+ changed.add(c);
97
+ break;
98
+ }
99
+ }
100
+ }
101
+ return changed;
102
+ }
103
+ function mutationId(m) {
104
+ return m.op === "set" ? m.doc.id : m.id;
105
+ }
106
+ function validate(mutations) {
107
+ for (const m of mutations) {
108
+ if (!COLLECTIONS.includes(m.collection)) throw new Error(`Unknown collection "${m.collection}".`);
109
+ const id = mutationId(m);
110
+ if (typeof id !== "string" || !id) throw new Error(`Mutation on ${m.collection} without an id.`);
111
+ }
112
+ }
113
+ function createStoreCore(backend) {
114
+ let snapshot = emptySnapshot();
115
+ let loading = null;
116
+ let loaded = false;
117
+ let queue = Promise.resolve();
118
+ let pending = 0;
119
+ const listeners = /* @__PURE__ */ new Set();
120
+ let timer = null;
121
+ let unwatch = null;
122
+ let closed = false;
123
+ function notify(changed) {
124
+ if (changed.size === 0) return;
125
+ for (const l of [...listeners]) {
126
+ try {
127
+ l(changed);
128
+ } catch (err) {
129
+ console.error("[datastore] listener failed:", err);
130
+ }
131
+ }
132
+ }
133
+ function adopt(fresh) {
134
+ if (pending > 0) return;
135
+ const changed = diffCollections(snapshot, fresh);
136
+ snapshot = fresh;
137
+ notify(changed);
138
+ }
139
+ function ready() {
140
+ if (!loading) {
141
+ loading = backend.load().then(
142
+ (s) => {
143
+ snapshot = s;
144
+ loaded = true;
145
+ notify(new Set(COLLECTIONS));
146
+ },
147
+ (err) => {
148
+ loading = null;
149
+ throw err;
150
+ }
151
+ );
152
+ }
153
+ return loading;
154
+ }
155
+ async function refresh() {
156
+ await ready();
157
+ if (!backend.poll || pending > 0) return;
158
+ const fresh = await backend.poll();
159
+ if (fresh) adopt(fresh);
160
+ }
161
+ function start() {
162
+ if (closed) return;
163
+ if (backend.watch && !unwatch) {
164
+ unwatch = backend.watch((fresh) => {
165
+ void ready().then(() => adopt(fresh));
166
+ });
167
+ }
168
+ if (backend.poll && !timer) {
169
+ timer = setInterval(() => {
170
+ refresh().catch((err) => console.warn("[datastore] poll failed:", err));
171
+ }, backend.pollIntervalMs ?? 3e4);
172
+ timer.unref?.();
173
+ }
174
+ }
175
+ function stop() {
176
+ if (timer) clearInterval(timer);
177
+ timer = null;
178
+ unwatch?.();
179
+ unwatch = null;
180
+ }
181
+ const store = {
182
+ kind: backend.kind,
183
+ ready,
184
+ refresh,
185
+ identity: () => backend.identity(),
186
+ list(collection) {
187
+ return [...snapshot[collection]?.values() ?? []];
188
+ },
189
+ get(collection, id) {
190
+ return snapshot[collection]?.get(id);
191
+ },
192
+ async write(mutations, opts) {
193
+ if (!loaded) await ready();
194
+ if (mutations.length === 0) return;
195
+ validate(mutations);
196
+ const before = snapshot;
197
+ const { next, changed } = applyMutations(before, mutations);
198
+ snapshot = next;
199
+ notify(changed);
200
+ pending++;
201
+ const run = queue.then(() => backend.persist(mutations, before, next, opts?.message ?? "Update data"));
202
+ queue = run.catch(() => void 0);
203
+ try {
204
+ await run;
205
+ } catch (err) {
206
+ const reverted = cloneSnapshot(snapshot);
207
+ for (const m of mutations) {
208
+ const id = mutationId(m);
209
+ const original = before[m.collection].get(id);
210
+ if (original) reverted[m.collection].set(id, original);
211
+ else reverted[m.collection].delete(id);
212
+ }
213
+ snapshot = reverted;
214
+ notify(changed);
215
+ throw err;
216
+ } finally {
217
+ pending--;
218
+ }
219
+ },
220
+ subscribe(listener) {
221
+ listeners.add(listener);
222
+ start();
223
+ return () => {
224
+ listeners.delete(listener);
225
+ if (listeners.size === 0) stop();
226
+ };
227
+ },
228
+ close() {
229
+ closed = true;
230
+ stop();
231
+ listeners.clear();
232
+ backend.close?.();
233
+ }
234
+ };
235
+ if (backend.compact) {
236
+ store.compact = async () => {
237
+ await ready();
238
+ await queue;
239
+ const result = await backend.compact(snapshot);
240
+ if (result) adopt(result);
241
+ };
242
+ }
243
+ return store;
244
+ }
245
+ export {
246
+ splitPath as a,
247
+ setPath as b,
248
+ createStoreCore as c,
249
+ applyMutations as d,
250
+ emptySnapshot as e,
251
+ getPath as g,
252
+ normalizeDoc as n,
253
+ plainCopy as p,
254
+ snapshotFromDocs as s
255
+ };