Skip to content

Commit 82fcfba

Browse files
fgatti675claude
andcommitted
fix(release): pin our own packages exactly in canary publishes
Root cause of `navigationController.resolveIdsFrom is not a function` on canary builds, and the companion react-i18next instance warning. Canary publishes ran `lerna version` without --exact, so the dependencies between our own packages were written as caret ranges: "@firecms/core": "^3.4.0-canary.abc1234" That range also matches the stable 3.4.0, and npm installs the HIGHEST match. So a canary install silently pulled *stable* transitive packages, which in turn require "@firecms/core": "^3.4.0" — which the canary core does not satisfy — and npm nested a second copy of core inside them. Measured on the published 3.3.0-canary.5906216. Installing core, ui, firebase and data_export all at that one version gave five copies of @firecms/core: 3.3.0-canary.5906216 @firecms/core 3.3.0 @firecms/collection_editor/node_modules/@firecms/core 3.3.0 @firecms/data_import/node_modules/@firecms/core 3.3.0 @firecms/data_enhancement/node_modules/@firecms/core 3.3.0 @firecms/data_import_export/node_modules/@firecms/core Two copies of core is two React context objects: the app's <FireCMS> fills one, the plugin reads the other and gets an empty controller, so every method on it is undefined. Pinning exactly keeps a canary install internally consistent. This supersedes the peer-dependency conversion in 42ba35b, now reverted. That change also stopped the duplicate, but it altered the install contract of 13 published packages to fix what is a release-tooling bug. Package manifests are byte-identical to 3.3.0 again: upgrading stays "change the version numbers". Also sets the canary base version to 3.4.0. Only lerna.json is bumped, which is what the workflow reads for BASE_VERSION; bumping the package versions would point their sibling ranges at an unpublished 3.4.0 and break CI's `pnpm install --frozen-lockfile`. Build 27/27, tests 528 across 13 projects, frozen-lockfile install clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 42ba35b commit 82fcfba

18 files changed

Lines changed: 239 additions & 220 deletions

File tree

‎.github/workflows/publish-canary.yml‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,19 @@ jobs:
4444
echo "CANARY_VERSION=$CANARY_VERSION" >> $GITHUB_ENV
4545
echo "Publishing canary version: $CANARY_VERSION"
4646
47-
# Update all package versions without creating git tags or commits
48-
npx lerna version "$CANARY_VERSION" --no-git-tag-version --no-push --yes
47+
# Update all package versions without creating git tags or commits.
48+
#
49+
# --exact is required. Without it lerna writes caret ranges between our own
50+
# packages, e.g. "@firecms/core": "^3.4.0-canary.abc1234". That range also
51+
# matches the stable 3.4.0, and npm installs the HIGHEST match — so a canary
52+
# install silently pulled stable transitive packages, which in turn required
53+
# "@firecms/core": "^3.4.0" that the canary core does not satisfy, and npm
54+
# nested a second copy of core. Two copies of core means two React contexts, so
55+
# plugins read an empty navigation controller and every method on it is
56+
# undefined ("resolveIdsFrom is not a function").
57+
#
58+
# Pinning exactly keeps a canary install internally consistent.
59+
npx lerna version "$CANARY_VERSION" --no-git-tag-version --no-push --yes --exact
4960
5061
# Commit version changes locally so lerna publish doesn't complain
5162
# (this commit is never pushed back)

‎CHANGELOG.md‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
## [Unreleased]
22

3-
- **`@firecms/core`, `@firecms/ui` and `@firecms/formex` are now peer dependencies of every plugin package**:
4-
- Fixes `navigationController.resolveIdsFrom is not a function`, and the companion `react-i18next:: You will need to pass in an i18next instance` warning.
5-
- These three packages hold React context (`@firecms/core` alone has 18 context objects). They were declared as regular `dependencies` of the plugin packages, which lets a package manager install a second, private copy inside a plugin whenever its version differs from the one in your app. Two copies means two distinct context objects: your `<FireCMS>` provider fills one, the plugin reads the other, gets the empty default, and every method on it is `undefined`.
6-
- This is easiest to hit when mixing versions — for example an app on a `canary` build with a plugin still resolving `^3.3.0`, since a prerelease does not satisfy a stable range. Measured directly: with the old declaration, `npm install @firecms/core@canary @firecms/data_export@3.3.0` produced **two** copies of `@firecms/core`; as a peer dependency it produces **one**.
7-
- Affects `collection_editor`, `collection_editor_firebase`, `data_enhancement`, `data_export`, `data_import`, `data_import_export`, `datatalk`, `entity_history`, `firebase`, `firebase_admin`, `cloud`, `media_manager` and `user_management`.
8-
- **No action needed for most projects.** Any app already installs `@firecms/core` directly, which satisfies the peer. npm and pnpm resolve peers automatically. If you use a package manager that does not, or you had relied on a plugin pulling core in for you, add it explicitly: `npm i @firecms/core@<same version as your other @firecms packages>`.
9-
- Keep all `@firecms/*` packages on the same version. A mismatch is now a visible peer warning at install time instead of a silently broken app.
10-
- `@firecms/schema_inference` holds no context and remains a regular dependency.
3+
- **Fixed duplicate `@firecms/core` in prerelease installs**:
4+
- Fixes `navigationController.resolveIdsFrom is not a function`, and the companion `react-i18next:: You will need to pass in an i18next instance` warning, when using a `canary` build.
5+
- Canary releases pinned the dependencies between our own packages with a caret, e.g. `"@firecms/core": "^3.4.0-canary.abc1234"`. That range also matches the stable `3.4.0`, and npm installs the highest match — so a canary install quietly pulled **stable** transitive packages. Those in turn require `"@firecms/core": "^3.4.0"`, which the canary core does not satisfy, so npm nested a second copy of `@firecms/core` inside them. Two copies means two React context objects: the app's `<FireCMS>` provider fills one, the plugin reads the other and gets an empty controller.
6+
- Canary publishes now pin our own packages exactly, so a canary install is internally consistent. Verified: installing `@firecms/core`, `@firecms/ui`, `@firecms/firebase` and `@firecms/data_export` at the same canary version previously produced five copies of `@firecms/core` (one canary, four stable); it now produces one.
7+
- Stable releases were never affected, and no package manifests changed — **nothing to do when upgrading beyond changing the version numbers**, as before.
8+
- As always, keep every `@firecms/*` package on the same version.
119
- **CLI (`firecms init`)**:
1210
- Fixed `firecms init my-app` scaffolding into a directory called `init` and ignoring the name given. The `init` subcommand was being passed through to the argument parser, so the positional directory name was read one position too early. `create-firecms-app` was unaffected.
1311
- Fixed the Firebase project id sometimes not being substituted into a new project, leaving the raw `[REPLACE_WITH_PROJECT_ID]` placeholder in the generated files. The substitution used callback-style `fs` calls inside an `await`, so the writes were fire-and-forget and the CLI could exit before they landed — non-deterministically, which is why it went unnoticed.

‎discord-bot/src/index.ts‎

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ MODES OF OPERATION:
8080
- Do not provide any explanation when ignoring, just output the exact string "IGNORE".
8181
`;
8282

83+
// Cheap relevance check used before the expensive answer call. The instruction above
84+
// embeds the entire documentation (~500 KB), so running it on every message in every
85+
// channel would burn tokens on ordinary chatter. This one carries no docs and returns
86+
// a single word, so unsolicited messages are filtered out at a fraction of the cost.
87+
const triageInstruction = `You triage messages for a Discord bot that supports FireCMS and Firebase.
88+
Reply with exactly one word and nothing else.
89+
Reply "ANSWER" only if the message is a clear question asking for help with FireCMS or Firebase — for example "how do I configure X?", "why does Y fail?", "does FireCMS support Z?".
90+
Reply "IGNORE" for everything else: statements, explanations, replies aimed at someone else, code snippets or configuration without a question, bug reports that ask nothing, greetings, casual conversation, and any subject other than FireCMS or Firebase.
91+
When uncertain, reply "IGNORE".`;
92+
8393
// Initialize Discord Client
8494
const client = new Client({
8595
intents: [
@@ -183,27 +193,49 @@ client.on("messageCreate", async (message: Message) => {
183193
}
184194
}
185195

186-
const isGeneralChannel = "name" in message.channel && typeof message.channel.name === "string" && message.channel.name.toLowerCase() === "general";
187-
const shouldRespond = isDM || (isInAllowedChannel && (isMentioned || isReplyToBot || isGeneralChannel));
196+
// Eavesdrop in every allowed channel rather than only in #general: a FireCMS
197+
// question deserves an answer wherever it is asked. Whether a message is worth
198+
// answering is decided by the triage call below, not by which channel it is in.
199+
const shouldRespond = isDM || isInAllowedChannel;
188200

189201
if (!shouldRespond) return;
190202

191-
const isEavesdropping = isGeneralChannel && !isMentioned && !isReplyToBot;
203+
const isEavesdropping = !isDM && !isMentioned && !isReplyToBot;
192204

193205
try {
194206
const cleanedCurrentContent = message.content.replace(new RegExp(`<@!?${client.user!.id}>`, "g"), "").trim();
195207
if (!cleanedCurrentContent) return;
196208

197209
console.log(`Processing message from ${message.author.tag} in ${isDM ? "DM" : "guild channel"} (eavesdropping=${isEavesdropping})`);
198210

211+
// Nobody addressed the bot, so decide whether this is worth answering before
212+
// paying for a call that carries the full documentation.
213+
if (isEavesdropping) {
214+
const triage = await ai.models.generateContent({
215+
model: GEMINI_MODEL,
216+
contents: [{ role: "user", parts: [{ text: cleanedCurrentContent }] }],
217+
config: {
218+
systemInstruction: triageInstruction,
219+
temperature: 0
220+
}
221+
});
222+
const verdict = (triage.text || "").trim().replace(/[.\s]/g, "").toUpperCase();
223+
if (verdict !== "ANSWER") {
224+
console.log(`Eavesdropping: Ignored message from ${message.author.tag} ("${cleanedCurrentContent}")`);
225+
return;
226+
}
227+
console.log(`Eavesdropping: Answering message from ${message.author.tag} ("${cleanedCurrentContent}")`);
228+
}
229+
199230
// Build contents for Gemini API
200231
const contents: Content[] = [];
201232

202233
if (isEavesdropping) {
203-
// In eavesdropping mode, only process the single current message without history to avoid confusion
234+
// Triage already judged this relevant, so ask for a real answer. Sent without
235+
// channel history: the bot was not part of the conversation that preceded it.
204236
contents.push({
205237
role: "user",
206-
parts: [{ text: `[Eavesdropping Mode - Answer only if this is a clear question about FireCMS or Firebase, otherwise respond with IGNORE]: ${cleanedCurrentContent}` }]
238+
parts: [{ text: `[Direct Mode - Always respond]: ${cleanedCurrentContent}` }]
207239
});
208240
} else {
209241
// Show typing indicator

‎lerna.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
3-
"version": "3.3.0",
3+
"version": "3.4.0",
44
"packages": [
55
"packages/firecms",
66
"packages/firecms_cloud",
@@ -31,4 +31,4 @@
3131
"examples/example_pro"
3232
],
3333
"npmClient": "pnpm"
34-
}
34+
}

‎packages/collection_editor/package.json‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77
"types": "./dist/index.d.ts",
88
"source": "src/index.ts",
99
"dependencies": {
10+
"@firecms/core": "^3.3.0",
1011
"@firecms/data_export": "^3.3.0",
1112
"@firecms/data_import": "^3.3.0",
1213
"@firecms/data_import_export": "^3.3.0",
14+
"@firecms/formex": "^3.3.0",
1315
"@firecms/schema_inference": "^3.3.0",
16+
"@firecms/ui": "^3.3.0",
1417
"json5": "^2.2.3",
1518
"prism-react-renderer": "^2.4.1"
1619
},
1720
"peerDependencies": {
18-
"@firecms/core": "^3.3.0",
19-
"@firecms/formex": "^3.3.0",
20-
"@firecms/ui": "^3.3.0",
2121
"react": ">=18.3.1 || >=19.0.0",
2222
"react-dom": ">=18.3.1 || >=19.0.0",
2323
"react-router": "^6.28.0",
@@ -50,9 +50,6 @@
5050
]
5151
},
5252
"devDependencies": {
53-
"@firecms/core": "^3.3.0",
54-
"@firecms/formex": "^3.3.0",
55-
"@firecms/ui": "^3.3.0",
5653
"@jest/globals": "^30.2.0",
5754
"@types/react": "^19.2.3",
5855
"@types/react-dom": "^19.2.3",

‎packages/collection_editor_firebase/package.json‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,15 @@
3030
"source": "src/index.ts",
3131
"dependencies": {
3232
"@firecms/collection_editor": "^3.3.0",
33+
"@firecms/core": "^3.3.0",
3334
"@firecms/firebase": "^3.3.0"
3435
},
3536
"peerDependencies": {
36-
"@firecms/core": "^3.3.0",
3737
"firebase": "^10.12.2 || ^11.0.0 || ^12.0.0",
3838
"react": ">=18.3.1 || >=19.0.0",
3939
"react-dom": ">=18.3.1 || >=19.0.0"
4040
},
4141
"devDependencies": {
42-
"@firecms/core": "^3.3.0",
4342
"@types/node": "^26.1.1",
4443
"@types/react": "^19.2.3",
4544
"@types/react-dom": "^19.2.3",

‎packages/data_enhancement/package.json‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
},
1515
"./package.json": "./package.json"
1616
},
17-
"dependencies": {},
18-
"peerDependencies": {
17+
"dependencies": {
1918
"@firecms/core": "^3.3.0",
20-
"@firecms/ui": "^3.3.0",
19+
"@firecms/ui": "^3.3.0"
20+
},
21+
"peerDependencies": {
2122
"firebase": "^10.12.2 || ^11.0.0 || ^12.0.0",
2223
"react": ">=18.3.1 || >=19.0.0",
2324
"react-dom": ">=18.3.1 || >=19.0.0",
@@ -43,8 +44,6 @@
4344
]
4445
},
4546
"devDependencies": {
46-
"@firecms/core": "^3.3.0",
47-
"@firecms/ui": "^3.3.0",
4847
"@testing-library/jest-dom": "^6.9.1",
4948
"@types/jest": "^29.5.14",
5049
"@types/react": "^19.2.3",

‎packages/data_export/package.json‎

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
"types": "./dist/index.d.ts",
99
"source": "src/index.ts",
1010
"dependencies": {
11-
"@firecms/schema_inference": "^3.3.0"
12-
},
13-
"peerDependencies": {
1411
"@firecms/core": "^3.3.0",
1512
"@firecms/formex": "^3.3.0",
16-
"@firecms/ui": "^3.3.0",
13+
"@firecms/schema_inference": "^3.3.0",
14+
"@firecms/ui": "^3.3.0"
15+
},
16+
"peerDependencies": {
1717
"react": ">=18.3.1 || >=19.0.0",
1818
"react-dom": ">=18.3.1 || >=19.0.0",
1919
"react-router": "^6.28.0",
@@ -47,9 +47,6 @@
4747
]
4848
},
4949
"devDependencies": {
50-
"@firecms/core": "^3.3.0",
51-
"@firecms/formex": "^3.3.0",
52-
"@firecms/ui": "^3.3.0",
5350
"@jest/globals": "^30.2.0",
5451
"@testing-library/jest-dom": "^6.9.1",
5552
"@types/jest": "^29.5.14",

‎packages/data_import/package.json‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
"types": "./dist/index.d.ts",
99
"source": "src/index.ts",
1010
"dependencies": {
11+
"@firecms/core": "^3.3.0",
12+
"@firecms/formex": "^3.3.0",
1113
"@firecms/schema_inference": "^3.3.0",
14+
"@firecms/ui": "^3.3.0",
1215
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz"
1316
},
1417
"peerDependencies": {
15-
"@firecms/core": "^3.3.0",
16-
"@firecms/formex": "^3.3.0",
17-
"@firecms/ui": "^3.3.0",
1818
"react": ">=18.3.1 || >=19.0.0",
1919
"react-dom": ">=18.3.1 || >=19.0.0",
2020
"react-router": "^6.28.0",
@@ -48,9 +48,6 @@
4848
]
4949
},
5050
"devDependencies": {
51-
"@firecms/core": "^3.3.0",
52-
"@firecms/formex": "^3.3.0",
53-
"@firecms/ui": "^3.3.0",
5451
"@jest/globals": "^30.2.0",
5552
"@testing-library/jest-dom": "^6.9.1",
5653
"@types/jest": "^29.5.14",

‎packages/data_import_export/package.json‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
"types": "./dist/index.d.ts",
99
"source": "src/index.ts",
1010
"dependencies": {
11+
"@firecms/core": "^3.3.0",
1112
"@firecms/data_export": "^3.3.0",
1213
"@firecms/data_import": "^3.3.0"
1314
},
1415
"peerDependencies": {
15-
"@firecms/core": "^3.3.0",
1616
"react": ">=18.3.1 || >=19.0.0",
1717
"react-dom": ">=18.3.1 || >=19.0.0",
1818
"react-router": "^6.28.0",
@@ -46,7 +46,6 @@
4646
]
4747
},
4848
"devDependencies": {
49-
"@firecms/core": "^3.3.0",
5049
"@jest/globals": "^30.2.0",
5150
"@testing-library/jest-dom": "^6.9.1",
5251
"@types/jest": "^29.5.14",

0 commit comments

Comments
 (0)