@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.
- package/LICENSE +143 -0
- package/README.md +223 -2
- package/changelog.json +57 -0
- package/dist/admin/admin-files.json +26 -0
- package/dist/admin/assets/chartsImpl-CE2akJat.js +91 -0
- package/dist/admin/assets/firebase-BHSUggRo.js +2 -0
- package/dist/admin/assets/gitJson-BulGh2KV.js +3 -0
- package/dist/admin/assets/index-DidVVK7L.js +1181 -0
- package/dist/admin/assets/index-hMXwHSvN.css +1 -0
- package/dist/admin/assets/index.esm-BFc7vwnl.js +400 -0
- package/dist/admin/assets/index.esm-CtEQZRb1.js +1651 -0
- package/dist/admin/assets/index.esm-Dum0J07z.js +16 -0
- package/dist/admin/assets/index.esm2017-EOzKbpUS.js +390 -0
- package/dist/admin/assets/lucide-react.min-DEDlHCPj.js +6 -0
- package/dist/admin/assets/sdk-CnrLnlRo.js +1717 -0
- package/dist/admin/config.js +3 -0
- package/dist/admin/favicon.svg +1 -0
- package/dist/admin/index.html +139 -0
- package/dist/admin/runtime/agentcrm-runtime.js +104 -0
- package/dist/admin/runtime/lucide-react.js +791 -0
- package/dist/admin/runtime/react-dom-client.js +17 -0
- package/dist/admin/runtime/react-dom.js +20 -0
- package/dist/admin/runtime/react-i18next.js +19 -0
- package/dist/admin/runtime/react-jsx-runtime.js +17 -0
- package/dist/admin/runtime/react.js +22 -0
- package/dist/agent/agentcrm.mjs +3932 -0
- package/dist/agent/chunks/_commonjsHelpers-ClciMIcB.mjs +14 -0
- package/dist/agent/chunks/activities-Cj_SIOay.mjs +227 -0
- package/dist/agent/chunks/api-B6OqNVWp.mjs +97 -0
- package/dist/agent/chunks/appearance-BaShelzt.mjs +174 -0
- package/dist/agent/chunks/automations-BWrvLqP6.mjs +698 -0
- package/dist/agent/chunks/backup-ClSmxmFZ.mjs +103 -0
- package/dist/agent/chunks/batch-CVETiJj5.mjs +142 -0
- package/dist/agent/chunks/compact-CsFPaAAq.mjs +69 -0
- package/dist/agent/chunks/context-CxFfqS4D.mjs +65 -0
- package/dist/agent/chunks/core-Dy7vY9H6.mjs +255 -0
- package/dist/agent/chunks/dedupe-BepmsaHv.mjs +123 -0
- package/dist/agent/chunks/encoding-C9MzdP95.mjs +36 -0
- package/dist/agent/chunks/exportCsv-B5hRvkNm.mjs +95 -0
- package/dist/agent/chunks/extCheck--7lmVvl7.mjs +193 -0
- package/dist/agent/chunks/extPackage-BGl5Oqs8.mjs +115 -0
- package/dist/agent/chunks/extScaffold-DqxI7R1e.mjs +320 -0
- package/dist/agent/chunks/extensionActions-D2HtpQz4.mjs +48 -0
- package/dist/agent/chunks/extensions-BP1k60md.mjs +151 -0
- package/dist/agent/chunks/featureRegistry-C9nbVShp.mjs +149 -0
- package/dist/agent/chunks/fields-B0bitgmr.mjs +476 -0
- package/dist/agent/chunks/files-CjsPJSR6.mjs +158 -0
- package/dist/agent/chunks/files-q8Xu5bft.mjs +367 -0
- package/dist/agent/chunks/firebase-DxGVQV9E.mjs +454 -0
- package/dist/agent/chunks/firebaseRules-iori-s7U.mjs +221 -0
- package/dist/agent/chunks/forms-BKU5F9WF.mjs +847 -0
- package/dist/agent/chunks/gitJson-fjr8mcHe.mjs +528 -0
- package/dist/agent/chunks/hosting-Bme7LxPc.mjs +144 -0
- package/dist/agent/chunks/http-Bk9ICb7I.mjs +127 -0
- package/dist/agent/chunks/ids-CvnmrMJX.mjs +59 -0
- package/dist/agent/chunks/ids-u-noYcy5.mjs +14 -0
- package/dist/agent/chunks/importCsv-DmE_sBch.mjs +334 -0
- package/dist/agent/chunks/index-B-4W0-P4.mjs +535 -0
- package/dist/agent/chunks/index-BM8qeGOb.mjs +2489 -0
- package/dist/agent/chunks/index-Bnmav-Oh.mjs +311 -0
- package/dist/agent/chunks/index-CRkw3_iF.mjs +147 -0
- package/dist/agent/chunks/index-CWrrF1wl.mjs +111 -0
- package/dist/agent/chunks/index-DUSdTBkz.mjs +2988 -0
- package/dist/agent/chunks/index-RCmo-uld.mjs +26 -0
- package/dist/agent/chunks/index-w03ReRs4.mjs +9780 -0
- package/dist/agent/chunks/install-CpHoUFtL.mjs +188 -0
- package/dist/agent/chunks/jsx-runtime-Ca2uwGDr.mjs +76 -0
- package/dist/agent/chunks/loader-C9nm3iwx.mjs +184 -0
- package/dist/agent/chunks/lucide-react.min-Bt3gtepY.mjs +64 -0
- package/dist/agent/chunks/memory-WNR3VHUJ.mjs +56 -0
- package/dist/agent/chunks/migrate-CsClnnN8.mjs +182 -0
- package/dist/agent/chunks/modules-CqdVQVJb.mjs +177 -0
- package/dist/agent/chunks/nodeImport-BPCt5uMu.mjs +38 -0
- package/dist/agent/chunks/package-DWbn3t2R.mjs +194 -0
- package/dist/agent/chunks/papaparse-DjBYnQM2.mjs +1285 -0
- package/dist/agent/chunks/pipelines-DCdRBNBV.mjs +234 -0
- package/dist/agent/chunks/plan-C0TBrU92.mjs +254 -0
- package/dist/agent/chunks/pluginRegistry-DyHtlkcr.mjs +83 -0
- package/dist/agent/chunks/query-C5nJB_3C.mjs +192 -0
- package/dist/agent/chunks/records-DoqiT9xR.mjs +2191 -0
- package/dist/agent/chunks/registry-Ceexawxl.mjs +182 -0
- package/dist/agent/chunks/registry-DijAKZ24.mjs +431 -0
- package/dist/agent/chunks/relations-B9TynBmA.mjs +191 -0
- package/dist/agent/chunks/reports-BB59N6ke.mjs +189 -0
- package/dist/agent/chunks/runtime-DHo7ZEEO.mjs +14852 -0
- package/dist/agent/chunks/runtimeConfig-CCOFg1RM.mjs +250 -0
- package/dist/agent/chunks/s3-Dz50-NuA.mjs +903 -0
- package/dist/agent/chunks/schema-7-4-qtSS.mjs +203 -0
- package/dist/agent/chunks/schema-DlHt2PxI.mjs +367 -0
- package/dist/agent/chunks/sdk-whvfg786.mjs +43644 -0
- package/dist/agent/chunks/seed-CugPGYZL.mjs +523 -0
- package/dist/agent/chunks/server-ISwlDRCN.mjs +242 -0
- package/dist/agent/chunks/settings-BtvR_jdm.mjs +76 -0
- package/dist/agent/chunks/standardObjects-CheqfDqa.mjs +243 -0
- package/dist/agent/chunks/stats-CfeJssSS.mjs +51 -0
- package/dist/agent/chunks/store-5gqRSKXg.mjs +122 -0
- package/dist/agent/chunks/themes-DPZFv3yc.mjs +43 -0
- package/dist/agent/chunks/tools-Bew8lxKx.mjs +844 -0
- package/dist/agent/chunks/types-BOy9PUl7.mjs +44 -0
- package/dist/agent/chunks/users-DI1BvS0f.mjs +107 -0
- package/dist/agent/chunks/utils-DmI8FtXP.mjs +83 -0
- package/dist/agent/chunks/views-fO_BM5U_.mjs +109 -0
- package/dist/version.json +7 -0
- package/package.json +97 -4
- package/plugin/.claude-plugin/plugin.json +17 -0
- package/plugin/skills/agentcrm-admin/SKILL.md +191 -0
- package/plugin/skills/agentcrm-admin/references/appearance.md +26 -0
- package/plugin/skills/agentcrm-admin/references/import.md +16 -0
- package/plugin/skills/agentcrm-admin/references/records.md +30 -0
- package/plugin/skills/agentcrm-admin/references/schema.md +59 -0
- package/plugin/skills/agentcrm-extensions/SKILL.md +93 -0
- package/plugin/skills/agentcrm-extensions/references/module-recipes.md +213 -0
- package/plugin/skills/agentcrm-extensions/references/runtime-api.md +41 -0
- package/plugin/skills/agentcrm-extensions/references/testing.md +76 -0
- package/plugin/skills/agentcrm-extensions/references/theme-recipes.md +65 -0
- package/plugin/skills/agentcrm-install/SKILL.md +116 -0
- package/plugin/skills/agentcrm-install/references/firebase.md +18 -0
- package/plugin/skills/agentcrm-install/references/github.md +20 -0
- package/plugin/skills/agentcrm-install/references/mcp.md +17 -0
- package/plugin/skills/agentcrm-install/references/plans.md +45 -0
- package/plugin/skills/agentcrm-install/references/s3.md +24 -0
- package/plugin/skills/agentcrm-manage/SKILL.md +108 -0
- package/plugin/skills/agentcrm-manage/references/cron.md +11 -0
- package/plugin/skills/agentcrm-manage/references/doctor.md +18 -0
- package/plugin/skills/agentcrm-sales/SKILL.md +92 -0
- package/plugin/skills/agentcrm-sales/references/enrichment.md +13 -0
- package/plugin/skills/agentcrm-sales/references/playbooks.md +29 -0
- package/plugin/skills/agentcrm-sales/references/weekly-report.md +27 -0
- package/types/agentcrm-runtime.d.ts +894 -0
|
@@ -0,0 +1,56 @@
|
|
|
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 { c as createStoreCore, s as snapshotFromDocs } from "./core-Dy7vY9H6.mjs";
|
|
8
|
+
import { w as withFileStore } from "./index-RCmo-uld.mjs";
|
|
9
|
+
import { a as assertFileId } from "./ids-u-noYcy5.mjs";
|
|
10
|
+
import "./types-BOy9PUl7.mjs";
|
|
11
|
+
function createMemoryFileStore(initial) {
|
|
12
|
+
const blobs = initial ?? /* @__PURE__ */ new Map();
|
|
13
|
+
return {
|
|
14
|
+
kind: "memory",
|
|
15
|
+
blobs,
|
|
16
|
+
async put(id, bytes) {
|
|
17
|
+
assertFileId(id);
|
|
18
|
+
blobs.set(id, new Uint8Array(bytes));
|
|
19
|
+
},
|
|
20
|
+
async putMany(items) {
|
|
21
|
+
for (const it of items) {
|
|
22
|
+
assertFileId(it.id);
|
|
23
|
+
blobs.set(it.id, new Uint8Array(it.bytes));
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
async get(id) {
|
|
27
|
+
const b = blobs.get(id);
|
|
28
|
+
return b ? new Uint8Array(b) : null;
|
|
29
|
+
},
|
|
30
|
+
async delete(id) {
|
|
31
|
+
blobs.delete(id);
|
|
32
|
+
},
|
|
33
|
+
async list() {
|
|
34
|
+
return [...blobs.keys()].sort();
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function createMemoryStore(opts = {}) {
|
|
39
|
+
const store = createStoreCore({
|
|
40
|
+
kind: "memory",
|
|
41
|
+
async load() {
|
|
42
|
+
return snapshotFromDocs(opts.initial ?? {});
|
|
43
|
+
},
|
|
44
|
+
async persist() {
|
|
45
|
+
const err = opts.failNextWrite?.();
|
|
46
|
+
if (err) throw err;
|
|
47
|
+
},
|
|
48
|
+
async identity() {
|
|
49
|
+
return opts.identity === void 0 ? { uid: "local", email: "admin@localhost", displayName: "Admin", role: "admin" } : opts.identity;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return withFileStore(store, createMemoryFileStore(opts.blobs));
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
createMemoryStore
|
|
56
|
+
};
|
|
@@ -0,0 +1,182 @@
|
|
|
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 { a as standardObjects, S as STANDARD_IMAGE_FIELDS } from "./standardObjects-CheqfDqa.mjs";
|
|
8
|
+
import { assertCan } from "./context-CxFfqS4D.mjs";
|
|
9
|
+
import { settingsDoc, objectOf } from "./store-5gqRSKXg.mjs";
|
|
10
|
+
const FR = {
|
|
11
|
+
objects: {
|
|
12
|
+
companies: { singular: "Entreprise", plural: "Entreprises" },
|
|
13
|
+
people: { singular: "Contact", plural: "Contacts" },
|
|
14
|
+
deals: { singular: "Affaire", plural: "Affaires" }
|
|
15
|
+
},
|
|
16
|
+
fields: {
|
|
17
|
+
"companies.name": "Nom",
|
|
18
|
+
"companies.domain": "Domaine",
|
|
19
|
+
"companies.industry": "Secteur",
|
|
20
|
+
"companies.employees": "Effectif",
|
|
21
|
+
"companies.annualRevenue": "Chiffre d'affaires annuel",
|
|
22
|
+
"companies.address": "Adresse",
|
|
23
|
+
"companies.linkedin": "LinkedIn",
|
|
24
|
+
"companies.phone": "Téléphone",
|
|
25
|
+
"companies.tags": "Étiquettes",
|
|
26
|
+
"companies.logo": "Logo",
|
|
27
|
+
"people.firstName": "Prénom",
|
|
28
|
+
"people.lastName": "Nom de famille",
|
|
29
|
+
"people.name": "Nom",
|
|
30
|
+
"people.email": "E-mail",
|
|
31
|
+
"people.phone": "Téléphone",
|
|
32
|
+
"people.jobTitle": "Fonction",
|
|
33
|
+
"people.company": "Entreprise",
|
|
34
|
+
"people.city": "Ville",
|
|
35
|
+
"people.linkedin": "LinkedIn",
|
|
36
|
+
"people.lifecycleStage": "Cycle de vie",
|
|
37
|
+
"people.leadSource": "Source du lead",
|
|
38
|
+
"people.tags": "Étiquettes",
|
|
39
|
+
"people.avatar": "Photo",
|
|
40
|
+
"deals.name": "Nom",
|
|
41
|
+
"deals.amount": "Montant",
|
|
42
|
+
"deals.stage": "Étape",
|
|
43
|
+
"deals.pipeline": "Pipeline",
|
|
44
|
+
"deals.closeDate": "Date de clôture",
|
|
45
|
+
"deals.probability": "Probabilité",
|
|
46
|
+
"deals.company": "Entreprise",
|
|
47
|
+
"deals.contacts": "Contacts",
|
|
48
|
+
"deals.lostReason": "Motif de perte"
|
|
49
|
+
},
|
|
50
|
+
options: {
|
|
51
|
+
"companies.industry.software": "Logiciel",
|
|
52
|
+
"companies.industry.finance": "Finance",
|
|
53
|
+
"companies.industry.healthcare": "Santé",
|
|
54
|
+
"companies.industry.retail": "Commerce",
|
|
55
|
+
"companies.industry.manufacturing": "Industrie",
|
|
56
|
+
"companies.industry.education": "Éducation",
|
|
57
|
+
"companies.industry.media": "Médias",
|
|
58
|
+
"companies.industry.consulting": "Conseil",
|
|
59
|
+
"companies.industry.real-estate": "Immobilier",
|
|
60
|
+
"companies.industry.other": "Autre",
|
|
61
|
+
"people.lifecycleStage.subscriber": "Abonné",
|
|
62
|
+
"people.lifecycleStage.lead": "Lead",
|
|
63
|
+
"people.lifecycleStage.mql": "MQL",
|
|
64
|
+
"people.lifecycleStage.sql": "SQL",
|
|
65
|
+
"people.lifecycleStage.opportunity": "Opportunité",
|
|
66
|
+
"people.lifecycleStage.customer": "Client",
|
|
67
|
+
"people.lifecycleStage.evangelist": "Ambassadeur",
|
|
68
|
+
"people.leadSource.website": "Site web",
|
|
69
|
+
"people.leadSource.referral": "Recommandation",
|
|
70
|
+
"people.leadSource.event": "Événement",
|
|
71
|
+
"people.leadSource.outbound": "Prospection",
|
|
72
|
+
"people.leadSource.partner": "Partenaire",
|
|
73
|
+
"people.leadSource.ads": "Publicité",
|
|
74
|
+
"people.leadSource.social": "Réseaux sociaux",
|
|
75
|
+
"people.leadSource.other": "Autre"
|
|
76
|
+
},
|
|
77
|
+
stages: {
|
|
78
|
+
new: "Nouveau",
|
|
79
|
+
qualified: "Qualifié",
|
|
80
|
+
proposal: "Proposition",
|
|
81
|
+
negotiation: "Négociation",
|
|
82
|
+
won: "Gagné",
|
|
83
|
+
lost: "Perdu"
|
|
84
|
+
},
|
|
85
|
+
pipelines: { sales: "Ventes" },
|
|
86
|
+
views: {
|
|
87
|
+
"companies-all": "Toutes les entreprises",
|
|
88
|
+
"people-all": "Tous les contacts",
|
|
89
|
+
"deals-all": "Toutes les affaires",
|
|
90
|
+
"deals-pipeline": "Pipeline"
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const BY_LANG = { fr: FR };
|
|
94
|
+
function labelsFor(locale) {
|
|
95
|
+
if (!locale) return null;
|
|
96
|
+
return BY_LANG[locale.toLowerCase().split(/[-_]/)[0]] ?? null;
|
|
97
|
+
}
|
|
98
|
+
function localizeObjects(objects, locale) {
|
|
99
|
+
const L = labelsFor(locale);
|
|
100
|
+
if (!L) return objects;
|
|
101
|
+
const stage = (id, label) => L.stages[id] ?? label;
|
|
102
|
+
return objects.map((o) => {
|
|
103
|
+
const names = L.objects[o.id];
|
|
104
|
+
return {
|
|
105
|
+
...o,
|
|
106
|
+
...names ? { labelSingular: names.singular, labelPlural: names.plural } : {},
|
|
107
|
+
fields: o.fields.map((f) => ({
|
|
108
|
+
...f,
|
|
109
|
+
label: L.fields[`${o.id}.${f.id}`] ?? f.label,
|
|
110
|
+
...f.options ? {
|
|
111
|
+
options: f.options.map((opt) => ({
|
|
112
|
+
...opt,
|
|
113
|
+
label: o.id === "deals" && f.id === "stage" ? stage(opt.id, opt.label) : o.id === "deals" && f.id === "pipeline" ? L.pipelines[opt.id] ?? opt.label : L.options[`${o.id}.${f.id}.${opt.id}`] ?? opt.label
|
|
114
|
+
}))
|
|
115
|
+
} : {}
|
|
116
|
+
}))
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function localizePipeline(pipeline, locale) {
|
|
121
|
+
const L = labelsFor(locale);
|
|
122
|
+
if (!L) return pipeline;
|
|
123
|
+
return {
|
|
124
|
+
...pipeline,
|
|
125
|
+
name: L.pipelines[pipeline.id] ?? pipeline.name,
|
|
126
|
+
stages: pipeline.stages.map((s) => ({ ...s, label: L.stages[s.id] ?? s.label }))
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function localizeView(view, locale) {
|
|
130
|
+
const L = labelsFor(locale);
|
|
131
|
+
if (!L) return view;
|
|
132
|
+
return { ...view, name: L.views[view.id] ?? view.name };
|
|
133
|
+
}
|
|
134
|
+
const SCHEMA_MIGRATIONS = ["0.3.0-image-fields"];
|
|
135
|
+
function schemaMigrationMutations(ctx) {
|
|
136
|
+
const report = { applied: [] };
|
|
137
|
+
const mutations = [];
|
|
138
|
+
const now = ctx.now();
|
|
139
|
+
const locale = settingsDoc(ctx.store)?.locale;
|
|
140
|
+
const standard = new Map(localizeObjects(standardObjects(now), locale).map((o) => [o.id, o]));
|
|
141
|
+
const migration = SCHEMA_MIGRATIONS[0];
|
|
142
|
+
for (const [objectId, fieldId] of Object.entries(STANDARD_IMAGE_FIELDS)) {
|
|
143
|
+
const current = objectOf(ctx.store, objectId);
|
|
144
|
+
if (!current || current.migrations?.includes(migration)) continue;
|
|
145
|
+
const def = standard.get(objectId)?.fields.find((f) => f.id === fieldId);
|
|
146
|
+
if (!def) continue;
|
|
147
|
+
const added = [];
|
|
148
|
+
const fields = [...current.fields];
|
|
149
|
+
if (!fields.some((f) => f.id === fieldId)) {
|
|
150
|
+
fields.push({ ...def });
|
|
151
|
+
added.push(fieldId);
|
|
152
|
+
}
|
|
153
|
+
const patch = { migrations: [...current.migrations ?? [], migration], updatedAt: now };
|
|
154
|
+
if (added.length) patch.fields = fields;
|
|
155
|
+
const field = fields.find((f) => f.id === fieldId);
|
|
156
|
+
if (!current.imageField && field?.type === "image") patch.imageField = fieldId;
|
|
157
|
+
mutations.push({ op: "patch", collection: "objects", id: objectId, fields: patch });
|
|
158
|
+
report.applied.push({ object: objectId, migration, added });
|
|
159
|
+
}
|
|
160
|
+
return { mutations, report };
|
|
161
|
+
}
|
|
162
|
+
async function migrateSchema(ctx) {
|
|
163
|
+
assertCan(ctx, "schema.write");
|
|
164
|
+
const { mutations, report } = schemaMigrationMutations(ctx);
|
|
165
|
+
if (mutations.length) await ctx.store.write(mutations, { message: "Upgrade the schema (image fields)" });
|
|
166
|
+
return report;
|
|
167
|
+
}
|
|
168
|
+
const migrate = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
169
|
+
__proto__: null,
|
|
170
|
+
SCHEMA_MIGRATIONS,
|
|
171
|
+
migrateSchema,
|
|
172
|
+
schemaMigrationMutations
|
|
173
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
174
|
+
export {
|
|
175
|
+
SCHEMA_MIGRATIONS as S,
|
|
176
|
+
localizePipeline as a,
|
|
177
|
+
localizeView as b,
|
|
178
|
+
migrate as c,
|
|
179
|
+
localizeObjects as l,
|
|
180
|
+
migrateSchema as m,
|
|
181
|
+
schemaMigrationMutations as s
|
|
182
|
+
};
|
|
@@ -0,0 +1,177 @@
|
|
|
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 { c as REQUIRED_MODULES, d as disabledFromDoc, B as BUILTIN_MODULES, M as MODULES_CONFIG_ID, i as isCompatibleApiVersion, e as builtinModule } from "./api-B6OqNVWp.mjs";
|
|
8
|
+
import { assertCan, OpsError } from "./context-CxFfqS4D.mjs";
|
|
9
|
+
import { assertNoSecrets } from "./settings-BtvR_jdm.mjs";
|
|
10
|
+
import { extensionDocsOf, extensionDocOf } from "./store-5gqRSKXg.mjs";
|
|
11
|
+
import "./pluginRegistry-DyHtlkcr.mjs";
|
|
12
|
+
import "./runtimeConfig-CCOFg1RM.mjs";
|
|
13
|
+
function configDoc(store) {
|
|
14
|
+
return store.get("config", MODULES_CONFIG_ID);
|
|
15
|
+
}
|
|
16
|
+
function moduleExtensions(store) {
|
|
17
|
+
return extensionDocsOf(store).filter((e) => e.kind === "module");
|
|
18
|
+
}
|
|
19
|
+
function moduleStates(store) {
|
|
20
|
+
const disabled = disabledFromDoc(configDoc(store));
|
|
21
|
+
const list = BUILTIN_MODULES.map((m) => ({
|
|
22
|
+
id: m.id,
|
|
23
|
+
source: "builtin",
|
|
24
|
+
name: m.label,
|
|
25
|
+
description: m.summary,
|
|
26
|
+
nameKey: m.name,
|
|
27
|
+
descriptionKey: m.description,
|
|
28
|
+
icon: m.icon,
|
|
29
|
+
category: m.category,
|
|
30
|
+
required: !!m.required,
|
|
31
|
+
enabled: !disabled.has(m.id),
|
|
32
|
+
dependsOn: m.dependsOn ?? [],
|
|
33
|
+
dependents: [],
|
|
34
|
+
missingDependencies: [],
|
|
35
|
+
objects: m.objects ?? [],
|
|
36
|
+
...m.settingsSection ? { settingsSection: m.settingsSection } : {}
|
|
37
|
+
}));
|
|
38
|
+
for (const e of moduleExtensions(store)) {
|
|
39
|
+
const incompatible = !isCompatibleApiVersion(e.manifest.apiVersion);
|
|
40
|
+
list.push({
|
|
41
|
+
id: e.id,
|
|
42
|
+
source: "extension",
|
|
43
|
+
name: e.manifest.name,
|
|
44
|
+
description: e.manifest.description,
|
|
45
|
+
icon: e.manifest.icon ?? "Puzzle",
|
|
46
|
+
category: e.manifest.category ?? "customisation",
|
|
47
|
+
required: false,
|
|
48
|
+
enabled: !!e.enabled && !incompatible,
|
|
49
|
+
dependsOn: e.manifest.dependsOn ?? [],
|
|
50
|
+
dependents: [],
|
|
51
|
+
missingDependencies: [],
|
|
52
|
+
objects: [],
|
|
53
|
+
version: e.manifest.version,
|
|
54
|
+
...incompatible ? { incompatible: true } : {}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
const byId = new Map(list.map((m) => [m.id, m]));
|
|
58
|
+
for (const m of list) {
|
|
59
|
+
for (const d of m.dependsOn) {
|
|
60
|
+
byId.get(d)?.dependents.push(m.id);
|
|
61
|
+
if (!byId.get(d)?.enabled) m.missingDependencies.push(d);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return list;
|
|
65
|
+
}
|
|
66
|
+
function isModuleEnabled(store, id) {
|
|
67
|
+
const m = moduleStates(store).find((x) => x.id === id);
|
|
68
|
+
return m ? m.enabled : true;
|
|
69
|
+
}
|
|
70
|
+
function disabledObjectIds(store) {
|
|
71
|
+
const out = /* @__PURE__ */ new Set();
|
|
72
|
+
for (const m of moduleStates(store)) if (!m.enabled) for (const o of m.objects) out.add(o);
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
function listModules(ctx) {
|
|
76
|
+
assertCan(ctx, "settings.read");
|
|
77
|
+
return moduleStates(ctx.store);
|
|
78
|
+
}
|
|
79
|
+
function getModule(ctx, id) {
|
|
80
|
+
const m = listModules(ctx).find((x) => x.id === id);
|
|
81
|
+
if (!m) throw new OpsError("notFound", `Unknown module "${id}". Modules: ${listModules(ctx).map((x) => x.id).join(", ")}.`);
|
|
82
|
+
return m;
|
|
83
|
+
}
|
|
84
|
+
async function setModuleEnabled(ctx, id, enabled, opts = {}) {
|
|
85
|
+
assertCan(ctx, "modules.write");
|
|
86
|
+
const states = moduleStates(ctx.store);
|
|
87
|
+
const byId = new Map(states.map((m) => [m.id, m]));
|
|
88
|
+
const target = byId.get(id);
|
|
89
|
+
if (!target) throw new OpsError("notFound", `Unknown module "${id}". Modules: ${states.map((m) => m.id).join(", ")}.`);
|
|
90
|
+
if (!enabled && target.required) throw new OpsError("invalid", `"${target.name}" is required and can't be disabled. Required modules: ${REQUIRED_MODULES.join(", ")}.`);
|
|
91
|
+
if (enabled && target.incompatible) throw new OpsError("invalid", `"${target.name}" targets an unsupported extension API version — install an updated version.`);
|
|
92
|
+
const toChange = /* @__PURE__ */ new Map();
|
|
93
|
+
const visit = (mid) => {
|
|
94
|
+
const m = byId.get(mid);
|
|
95
|
+
if (!m || toChange.has(mid)) return;
|
|
96
|
+
toChange.set(mid, enabled);
|
|
97
|
+
const next = enabled ? m.dependsOn : m.dependents;
|
|
98
|
+
for (const n of next) {
|
|
99
|
+
const other = byId.get(n);
|
|
100
|
+
if (enabled && !other) throw new OpsError("conflict", `"${target.name}" depends on "${n}", which is not installed.`, { missing: [n] });
|
|
101
|
+
if (!other || other.enabled === enabled) continue;
|
|
102
|
+
if (!opts.cascade) {
|
|
103
|
+
const blockers = next.filter((x) => byId.get(x) && byId.get(x).enabled !== enabled);
|
|
104
|
+
throw new OpsError(
|
|
105
|
+
"conflict",
|
|
106
|
+
enabled ? `"${m.name}" needs ${blockers.map((b) => `"${byId.get(b).name}"`).join(", ")} — enable ${blockers.length > 1 ? "them" : "it"} first (or pass cascade).` : `${blockers.map((b) => `"${byId.get(b).name}"`).join(", ")} depend${blockers.length > 1 ? "" : "s"} on "${m.name}" — disable ${blockers.length > 1 ? "them" : "it"} first (or pass cascade).`,
|
|
107
|
+
{ blockers }
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
if (!enabled && other.required) throw new OpsError("conflict", `Required module "${other.name}" depends on "${m.name}".`);
|
|
111
|
+
visit(n);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
visit(id);
|
|
115
|
+
const changed = [...toChange].filter(([mid, on]) => byId.get(mid).enabled !== on).map(([mid, on]) => ({ id: mid, enabled: on }));
|
|
116
|
+
if (!changed.length) return { id, enabled, changed: [] };
|
|
117
|
+
const mutations = [];
|
|
118
|
+
const builtinChanges = changed.filter((c) => byId.get(c.id).source === "builtin");
|
|
119
|
+
if (builtinChanges.length) {
|
|
120
|
+
const current = configDoc(ctx.store);
|
|
121
|
+
const disabled = disabledFromDoc(current);
|
|
122
|
+
for (const c of builtinChanges) {
|
|
123
|
+
if (c.enabled) disabled.delete(c.id);
|
|
124
|
+
else disabled.add(c.id);
|
|
125
|
+
}
|
|
126
|
+
const list = BUILTIN_MODULES.map((m) => m.id).filter((m) => disabled.has(m));
|
|
127
|
+
if (current) mutations.push({ op: "patch", collection: "config", id: MODULES_CONFIG_ID, fields: { disabled: list, updatedAt: ctx.now() } });
|
|
128
|
+
else mutations.push({ op: "set", collection: "config", doc: { id: MODULES_CONFIG_ID, disabled: list, config: {}, updatedAt: ctx.now() } });
|
|
129
|
+
}
|
|
130
|
+
for (const c of changed.filter((x) => byId.get(x.id).source === "extension")) {
|
|
131
|
+
mutations.push({ op: "patch", collection: "extensions", id: c.id, fields: { enabled: c.enabled, updatedAt: ctx.now() } });
|
|
132
|
+
}
|
|
133
|
+
await ctx.store.write(mutations, { message: `${enabled ? "Enable" : "Disable"} module ${id}` });
|
|
134
|
+
ctx.log?.(`modules: ${changed.map((c) => `${c.id}=${c.enabled ? "on" : "off"}`).join(", ")}`);
|
|
135
|
+
return { id, enabled, changed };
|
|
136
|
+
}
|
|
137
|
+
function assertKnownModule(store, id) {
|
|
138
|
+
if (!builtinModule(id) && !extensionDocOf(store, id)) throw new OpsError("notFound", `Unknown module "${id}".`);
|
|
139
|
+
}
|
|
140
|
+
function getModuleConfig(ctx, id) {
|
|
141
|
+
assertCan(ctx, "settings.read");
|
|
142
|
+
return configDoc(ctx.store)?.config?.[id];
|
|
143
|
+
}
|
|
144
|
+
const MAX_MODULE_CONFIG = 256 * 1024;
|
|
145
|
+
async function setModuleConfig(ctx, id, value) {
|
|
146
|
+
assertCan(ctx, "modules.write");
|
|
147
|
+
assertKnownModule(ctx.store, id);
|
|
148
|
+
if (value !== null && value !== void 0) {
|
|
149
|
+
let json;
|
|
150
|
+
try {
|
|
151
|
+
json = JSON.stringify(value);
|
|
152
|
+
} catch {
|
|
153
|
+
throw new OpsError("validation", "Module configuration must be JSON-serialisable.");
|
|
154
|
+
}
|
|
155
|
+
if (json === void 0) throw new OpsError("validation", "Module configuration must be JSON-serialisable.");
|
|
156
|
+
if (json.length > MAX_MODULE_CONFIG) throw new OpsError("validation", `Module configuration is larger than ${MAX_MODULE_CONFIG / 1024} KB.`);
|
|
157
|
+
assertNoSecrets(value);
|
|
158
|
+
}
|
|
159
|
+
const current = configDoc(ctx.store);
|
|
160
|
+
const next = value === null || value === void 0 ? void 0 : JSON.parse(JSON.stringify(value));
|
|
161
|
+
if (current) {
|
|
162
|
+
await ctx.store.write([{ op: "patch", collection: "config", id: MODULES_CONFIG_ID, fields: { [`config.${id}`]: next, updatedAt: ctx.now() } }], { message: `Configure module ${id}` });
|
|
163
|
+
} else {
|
|
164
|
+
await ctx.store.write([{ op: "set", collection: "config", doc: { id: MODULES_CONFIG_ID, disabled: [], config: next === void 0 ? {} : { [id]: next }, updatedAt: ctx.now() } }], { message: `Configure module ${id}` });
|
|
165
|
+
}
|
|
166
|
+
return getModuleConfig(ctx, id);
|
|
167
|
+
}
|
|
168
|
+
export {
|
|
169
|
+
disabledObjectIds,
|
|
170
|
+
getModule,
|
|
171
|
+
getModuleConfig,
|
|
172
|
+
isModuleEnabled,
|
|
173
|
+
listModules,
|
|
174
|
+
moduleStates,
|
|
175
|
+
setModuleConfig,
|
|
176
|
+
setModuleEnabled
|
|
177
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
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 { mkdtemp, writeFile, rm } from "node:fs/promises";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { resolve, join } from "node:path";
|
|
10
|
+
import { pathToFileURL } from "node:url";
|
|
11
|
+
import { g as RUNTIME_MODULES } from "./api-B6OqNVWp.mjs";
|
|
12
|
+
import { rewriteRuntimeImports } from "./package-DWbn3t2R.mjs";
|
|
13
|
+
import "./index-w03ReRs4.mjs";
|
|
14
|
+
import "./_commonjsHelpers-ClciMIcB.mjs";
|
|
15
|
+
import "stream";
|
|
16
|
+
import "events";
|
|
17
|
+
import "buffer";
|
|
18
|
+
import "util";
|
|
19
|
+
function createNodeImportBundle(opts) {
|
|
20
|
+
const runtimeDir = resolve(opts.runtimeDir);
|
|
21
|
+
return async (code, info) => {
|
|
22
|
+
const dir = await mkdtemp(join(opts.tmpDir ?? tmpdir(), "agentcrm-ext-"));
|
|
23
|
+
const file = join(dir, `${info.id}-${info.version}.mjs`);
|
|
24
|
+
try {
|
|
25
|
+
const rewritten = rewriteRuntimeImports(code, (spec) => pathToFileURL(join(runtimeDir, RUNTIME_MODULES[spec])).href);
|
|
26
|
+
await writeFile(file, rewritten, "utf8");
|
|
27
|
+
return await import(
|
|
28
|
+
/* @vite-ignore */
|
|
29
|
+
pathToFileURL(file).href
|
|
30
|
+
);
|
|
31
|
+
} finally {
|
|
32
|
+
await rm(dir, { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
createNodeImportBundle
|
|
38
|
+
};
|
|
@@ -0,0 +1,194 @@
|
|
|
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 { a as EXTENSION_LIMITS, R as RUNTIME_SPECIFIERS, i as isCompatibleApiVersion, b as EXTENSION_MIN_API_VERSION, E as EXTENSION_API_VERSION, B as BUILTIN_MODULES, f as MODULE_CATEGORIES } from "./api-B6OqNVWp.mjs";
|
|
9
|
+
import "./_commonjsHelpers-ClciMIcB.mjs";
|
|
10
|
+
import "stream";
|
|
11
|
+
import "events";
|
|
12
|
+
import "buffer";
|
|
13
|
+
import "util";
|
|
14
|
+
class ExtensionPackageError extends Error {
|
|
15
|
+
constructor(code, message, details) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.code = code;
|
|
18
|
+
this.details = details;
|
|
19
|
+
this.name = "ExtensionPackageError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const EXTENSION_ID_RE = /^[a-z][a-z0-9-]{1,48}[a-z0-9]$/;
|
|
23
|
+
const SEMVER_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
24
|
+
const RESERVED_EXTENSION_IDS = [...BUILTIN_MODULES.map((m) => m.id), "agentcrm", "core", "runtime", "admin", "app", "custom", "default"];
|
|
25
|
+
const MANIFEST_KEYS = ["id", "kind", "name", "version", "apiVersion", "description", "author", "homepage", "entry", "style", "icon", "category", "dependsOn"];
|
|
26
|
+
function byteLength(s) {
|
|
27
|
+
return new TextEncoder().encode(s).length;
|
|
28
|
+
}
|
|
29
|
+
function validateManifest(raw) {
|
|
30
|
+
const errors = [];
|
|
31
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) throw new ExtensionPackageError("invalidManifest", "manifest.json must be a JSON object.");
|
|
32
|
+
const m = raw;
|
|
33
|
+
const str = (k, required, max = 200) => {
|
|
34
|
+
const v = m[k];
|
|
35
|
+
if (v === void 0 || v === null || v === "") {
|
|
36
|
+
if (required) errors.push(`"${k}" is required.`);
|
|
37
|
+
return void 0;
|
|
38
|
+
}
|
|
39
|
+
if (typeof v !== "string") {
|
|
40
|
+
errors.push(`"${k}" must be a string.`);
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
if (v.length > max) errors.push(`"${k}" is too long (max ${max} characters).`);
|
|
44
|
+
return v.trim();
|
|
45
|
+
};
|
|
46
|
+
for (const k of Object.keys(m)) if (!MANIFEST_KEYS.includes(k)) errors.push(`Unknown key "${k}".`);
|
|
47
|
+
const id = str("id", true, 50);
|
|
48
|
+
if (id && !EXTENSION_ID_RE.test(id)) errors.push(`"id" must be 3–50 characters: lowercase letters, digits and dashes, starting with a letter (got "${id}").`);
|
|
49
|
+
if (id && RESERVED_EXTENSION_IDS.includes(id)) errors.push(`"id" "${id}" is reserved (built-in module or reserved word).`);
|
|
50
|
+
const kind = str("kind", true, 10);
|
|
51
|
+
if (kind && kind !== "module" && kind !== "theme") errors.push(`"kind" must be "module" or "theme".`);
|
|
52
|
+
const name = str("name", true, 80);
|
|
53
|
+
const version = str("version", true, 40);
|
|
54
|
+
if (version && !SEMVER_RE.test(version)) errors.push(`"version" must be semver (1.2.3), got "${version}".`);
|
|
55
|
+
const apiVersion = str("apiVersion", true, 20);
|
|
56
|
+
const description = str("description", false, 500) ?? "";
|
|
57
|
+
const author = str("author", false, 120);
|
|
58
|
+
const homepage = str("homepage", false, 300);
|
|
59
|
+
if (homepage && !/^https?:\/\/\S+$/i.test(homepage)) errors.push(`"homepage" must be an http(s) URL.`);
|
|
60
|
+
const entry = str("entry", false, 40) ?? "bundle.js";
|
|
61
|
+
if (entry !== "bundle.js") errors.push(`"entry" must be "bundle.js".`);
|
|
62
|
+
const style = str("style", false, 40);
|
|
63
|
+
if (style !== void 0 && style !== "style.css") errors.push(`"style" must be "style.css" when set.`);
|
|
64
|
+
const icon = str("icon", false, 60);
|
|
65
|
+
if (icon && !/^[A-Z][A-Za-z0-9]*$/.test(icon)) errors.push(`"icon" must be a lucide icon name in PascalCase ("Target").`);
|
|
66
|
+
const category = str("category", false, 30);
|
|
67
|
+
if (category && !MODULE_CATEGORIES.includes(category)) errors.push(`"category" must be one of ${MODULE_CATEGORIES.join(", ")}.`);
|
|
68
|
+
let dependsOn;
|
|
69
|
+
if (m.dependsOn !== void 0) {
|
|
70
|
+
if (!Array.isArray(m.dependsOn) || !m.dependsOn.every((d) => typeof d === "string" && /^[a-z][a-z0-9-]*$/.test(d))) errors.push(`"dependsOn" must be an array of module ids.`);
|
|
71
|
+
else dependsOn = [...new Set(m.dependsOn)].filter((d) => d !== id);
|
|
72
|
+
}
|
|
73
|
+
if (kind === "theme" && (category || dependsOn?.length)) errors.push(`Themes can't declare "category" or "dependsOn".`);
|
|
74
|
+
if (errors.length) throw new ExtensionPackageError("invalidManifest", `Invalid manifest.json: ${errors.join(" ")}`, errors);
|
|
75
|
+
if (!isCompatibleApiVersion(apiVersion)) {
|
|
76
|
+
throw new ExtensionPackageError("incompatibleApi", `apiVersion ${apiVersion} is not supported by this admin (supported: ${EXTENSION_MIN_API_VERSION} – ${EXTENSION_API_VERSION}).`);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
id,
|
|
80
|
+
kind,
|
|
81
|
+
name,
|
|
82
|
+
version,
|
|
83
|
+
apiVersion,
|
|
84
|
+
description,
|
|
85
|
+
...author ? { author } : {},
|
|
86
|
+
...homepage ? { homepage } : {},
|
|
87
|
+
entry: "bundle.js",
|
|
88
|
+
...style ? { style: "style.css" } : {},
|
|
89
|
+
...icon ? { icon } : {},
|
|
90
|
+
...category ? { category } : {},
|
|
91
|
+
...dependsOn?.length ? { dependsOn } : {}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function bundleImports(code) {
|
|
95
|
+
const out = /* @__PURE__ */ new Set();
|
|
96
|
+
const re = /(?:\bimport\s*(?:[\w*{}\s,$]+?\s*from\s*)?|\bexport\s*(?:\*|\{[^}]*\})\s*from\s*|\bimport\s*\(\s*)["']([^"']+)["']/g;
|
|
97
|
+
for (const m of code.matchAll(re)) {
|
|
98
|
+
const spec = m[1];
|
|
99
|
+
if (!spec.startsWith(".") && !spec.startsWith("/") && !/^[a-z]+:/i.test(spec)) out.add(spec);
|
|
100
|
+
}
|
|
101
|
+
return [...out].sort();
|
|
102
|
+
}
|
|
103
|
+
function rewriteRuntimeImports(code, resolve) {
|
|
104
|
+
const specs = RUNTIME_SPECIFIERS.map((s) => s.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&")).join("|");
|
|
105
|
+
const re = new RegExp(`(\\bfrom\\s*|\\bimport\\s*\\(\\s*|\\bimport\\s+)(["'])(${specs})\\2`, "g");
|
|
106
|
+
return code.replace(re, (_m, prefix, quote, spec) => `${prefix}${quote}${resolve(spec)}${quote}`);
|
|
107
|
+
}
|
|
108
|
+
function validatePackage(pkg) {
|
|
109
|
+
const raw = pkg.files;
|
|
110
|
+
const manifest = validateManifest(pkg.manifest);
|
|
111
|
+
const bundle = raw["bundle.js"];
|
|
112
|
+
if (typeof bundle !== "string" || !bundle.trim()) throw new ExtensionPackageError("missingEntry", "The package has no bundle.js.");
|
|
113
|
+
if (byteLength(bundle) > EXTENSION_LIMITS.bundle) throw new ExtensionPackageError("tooLarge", `bundle.js is ${(byteLength(bundle) / 1024 / 1024).toFixed(2)} MB — the limit is ${EXTENSION_LIMITS.bundle / 1024 / 1024} MB.`);
|
|
114
|
+
const unknown = bundleImports(bundle).filter((s) => !RUNTIME_SPECIFIERS.includes(s));
|
|
115
|
+
if (unknown.length) {
|
|
116
|
+
throw new ExtensionPackageError("invalidBundle", `bundle.js imports modules the admin doesn't provide: ${unknown.join(", ")}. Bundle them, or import from: ${RUNTIME_SPECIFIERS.join(", ")}.`, unknown);
|
|
117
|
+
}
|
|
118
|
+
if (!/\bexport\s*(?:default\b|\{[^}]*\bas\s+default\b)/.test(bundle)) throw new ExtensionPackageError("invalidBundle", "bundle.js must have a default export (defineModule / defineTheme).");
|
|
119
|
+
const files = { "bundle.js": bundle };
|
|
120
|
+
const style = raw["style.css"];
|
|
121
|
+
if (manifest.style) {
|
|
122
|
+
if (typeof style !== "string") throw new ExtensionPackageError("missingEntry", 'manifest.json declares "style": "style.css" but the package has no style.css.');
|
|
123
|
+
}
|
|
124
|
+
if (typeof style === "string" && style.trim()) {
|
|
125
|
+
if (byteLength(style) > EXTENSION_LIMITS.style) throw new ExtensionPackageError("tooLarge", `style.css is larger than ${EXTENSION_LIMITS.style / 1024} KB.`);
|
|
126
|
+
files["style.css"] = style;
|
|
127
|
+
}
|
|
128
|
+
return { manifest, files };
|
|
129
|
+
}
|
|
130
|
+
async function readExtensionZip(data) {
|
|
131
|
+
const size = data instanceof Uint8Array ? data.byteLength : data instanceof ArrayBuffer ? data.byteLength : data.size;
|
|
132
|
+
if (size > EXTENSION_LIMITS.zip) throw new ExtensionPackageError("tooLarge", `The ZIP is larger than ${EXTENSION_LIMITS.zip / 1024 / 1024} MB.`);
|
|
133
|
+
let zip;
|
|
134
|
+
try {
|
|
135
|
+
zip = await JSZip.loadAsync(data);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
throw new ExtensionPackageError("invalidZip", `Not a valid ZIP file (${err.message}).`);
|
|
138
|
+
}
|
|
139
|
+
const names = Object.keys(zip.files).filter((n) => !zip.files[n].dir && !n.startsWith("__MACOSX/") && !n.split("/").pop().startsWith("."));
|
|
140
|
+
let prefix = "";
|
|
141
|
+
if (!names.includes("manifest.json")) {
|
|
142
|
+
const candidates = names.filter((n) => /^[^/]+\/manifest\.json$/.test(n));
|
|
143
|
+
if (candidates.length !== 1) throw new ExtensionPackageError("missingManifest", "The ZIP has no manifest.json at its root.");
|
|
144
|
+
prefix = candidates[0].slice(0, -"manifest.json".length);
|
|
145
|
+
}
|
|
146
|
+
const read = async (name) => {
|
|
147
|
+
const f = zip.file(prefix + name);
|
|
148
|
+
return f ? f.async("string") : void 0;
|
|
149
|
+
};
|
|
150
|
+
const manifestText = await read("manifest.json");
|
|
151
|
+
if (byteLength(manifestText) > EXTENSION_LIMITS.manifest) throw new ExtensionPackageError("tooLarge", "manifest.json is too large.");
|
|
152
|
+
let manifest;
|
|
153
|
+
try {
|
|
154
|
+
manifest = JSON.parse(manifestText);
|
|
155
|
+
} catch (err) {
|
|
156
|
+
throw new ExtensionPackageError("invalidManifest", `manifest.json is not valid JSON (${err.message}).`);
|
|
157
|
+
}
|
|
158
|
+
const files = { "bundle.js": await read("bundle.js"), "style.css": await read("style.css") };
|
|
159
|
+
return validatePackage({ manifest, files });
|
|
160
|
+
}
|
|
161
|
+
async function packExtension(pkg) {
|
|
162
|
+
const valid = validatePackage(pkg);
|
|
163
|
+
const zip = new JSZip();
|
|
164
|
+
zip.file("manifest.json", JSON.stringify(valid.manifest, null, 2) + "\n");
|
|
165
|
+
zip.file("bundle.js", valid.files["bundle.js"]);
|
|
166
|
+
if (valid.files["style.css"]) zip.file("style.css", valid.files["style.css"]);
|
|
167
|
+
return zip.generateAsync({ type: "uint8array", compression: "DEFLATE", compressionOptions: { level: 9 } });
|
|
168
|
+
}
|
|
169
|
+
function fingerprint(manifest, files) {
|
|
170
|
+
let h = 2166136261;
|
|
171
|
+
const feed = (s) => {
|
|
172
|
+
for (let i = 0; i < s.length; i++) {
|
|
173
|
+
h ^= s.charCodeAt(i);
|
|
174
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
feed(manifest.version);
|
|
178
|
+
feed(files["bundle.js"]);
|
|
179
|
+
feed(files["style.css"] ?? "");
|
|
180
|
+
return `${manifest.version}-${h.toString(16).padStart(8, "0")}`;
|
|
181
|
+
}
|
|
182
|
+
export {
|
|
183
|
+
EXTENSION_ID_RE,
|
|
184
|
+
ExtensionPackageError,
|
|
185
|
+
RESERVED_EXTENSION_IDS,
|
|
186
|
+
SEMVER_RE,
|
|
187
|
+
bundleImports,
|
|
188
|
+
fingerprint,
|
|
189
|
+
packExtension,
|
|
190
|
+
readExtensionZip,
|
|
191
|
+
rewriteRuntimeImports,
|
|
192
|
+
validateManifest,
|
|
193
|
+
validatePackage
|
|
194
|
+
};
|