chore(deps): update dependency @gramio/scenes to v0.7.1 - #266
Open
renovate[bot] wants to merge 1 commit into
Open
chore(deps): update dependency @gramio/scenes to v0.7.1#266renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
0.6.0→0.7.1Release Notes
gramiojs/scenes (@gramio/scenes)
v0.7.1Compare Source
Adds regression tests for all four mode/dedup combinations.
onEnter still sees the derived fields; subsequent updates still recompute.
through a shared _dedupeAgainstBot helper.
chain AND the pre-run. Both setup pre-runs (builder + legacy) now go
between bot.extend(withUser) and scene.extend(withUser) ran in the bot
The onEnter setup pre-run ignored cross-bot dedup, so a derive shared
Object.assign onto the live ctx, so their effect persists).
now takes skipFns and filters out the fns already pre-run (they
then dispatch() re-ran the whole chain — firing them again. dispatch()
Legacy path pre-ran derive/decorate so onEnter could see the fields,
update. Two causes, both fixed:
fix(runtime): run onEnter-consumed derives exactly once on entry updateA scene-level derive that onEnter reads fired twice on the scene-entry
chore: release 0.7.1
Full Changelog: gramiojs/scenes@v0.7.0...v0.7.1
v0.7.0Compare Source
arrives in step 6.
next()/previous() throw on string ids for now; sceneSteps array walking
is a superset.
numeric default. Existing numeric records remain valid —
string | numbershape so named steps (.step("intro", c => ...)) can land alongside the
refactor(types): widen stepId to string | numberStep 1 of the scene-as-composer redesign. Forward-compatible storage
composers.
separate slot also avoids polluting composer internals for non-Scene
and not amenable to declare-module augmentation. Keeping our state on a
@gramio/composer's
~slot — the latter is an inline class field typeThese types live on Scene's own
~sceneslot rather than augmentingexitData carriers).
stepsCount, enter/exit hooks, isModule flag, type-only params/state/
per-step records and per-Scene scene-specific state (steps array,
feat(scenes): add scene-internals.ts with SceneStepEntry/SceneInternalsStep 3 of scene-as-composer. Standalone types module describing
No runtime consumers yet — StepComposer wires into Scene in step 6.
exported) and the dev dependency to ^0.9.0.
Also bumps gramio peerDependency to >=0.9.0 (when _composerMethods was
getStepInternals() / buildStepEntry().
@gramio/composer's own ~ slot. Read by Scene runtime via
Storage: hooks land on this["~step"] so they don't collide with
.updates() — type-only opt-in for state inference
.events([...]) — narrow event whitelist (default: message|callback_query)
.message(text|fn) — sugar over .enter(ctx => ctx.send(text))
.fallback(handler) — catch-all for unmatched events
.exit(handler) — runs when leaving the step
.enter(handler) — runs on firstTime entry; replaces firstTime check
plus step-only lifecycle hooks:
gramio _composerMethods table (.command/.callbackQuery/.hears/.on/etc.)
exposed to
.step(c => c…)builders. Built via createComposer with thefeat(scenes): add StepComposer with lifecycle methodsStep 4 of scene-as-composer. StepComposer is the per-step sub-composer
4-slot generic system intact.
the composer's own generic slots are independent and we kept Scene's
exitData/step/ask/extend continues to flow through correctly because
74/74 existing tests pass. The Modify<...> chain on params/state/
within the package.
external API breaks: scene.run()'s 5-arg form was only invoked from
utils.ts and index.ts updated to call the renamed entry points. No
available unchanged for callers that want the middleware-runner shape.
The inherited Composer.compose() / Composer.run(ctx, next?) remain
• Scene.run(ctx, storage, key, data, ...) → Scene.dispatchActive(...)
• Scene.compose(ctx, onNext, passthrough) → Scene.dispatch(...)
Internal renames to dodge inherited-method signature clash:
no augmentation of @gramio/composer needed.
params/state/exitData). The composer's own
~slot stays untouched —stepsCount, lifecycle hooks, isModule flag, type-only carriers for
Scene-specific data lives on a dedicated
~sceneslot (steps array,• gramio sugar: .command/.callbackQuery/.hears/.reaction/.inlineQuery/
• base composer: .use/.on/.derive/.decorate/.guard/.branch/.route/
has the full bot-level DSL out of the box:
seeded with the gramio _composerMethods table), so every Scene instance
Scene now extends SceneComposerBase (a createComposer-produced class
refactor(scene): make Scene extend EventComposer (scene-as-composer)Step 5 of scene-as-composer. The crown jewel of the refactor.
All 81/81 tests green.
escape hatch, named-step collision detection, sequential numeric ids.
.message, named navigation, mixed legacy+builder, scene-level .command
Tests: 7 new smoke tests in tests/builder-smoke.test.ts cover .enter,
getSceneEnter now dispatches via scene.dispatchActive so the
step's id (string for named-first-step scenes, 0 for legacy-numeric).
getSceneEnter / getSceneEnterSub seed initial stepId from the first
go(string) jumps to a named step.
next/previous walks ~scene.steps by index, supporting named ids.
Step navigation (utils.ts):
Subsequent updates: scene-level chain (this["~"].middlewares with
firstTime: run entry's .message (sugar) then .enter, set firstTime=false.
composer chain).
Find current step in ~scene.steps. If absent → legacy mode (whole
Runtime (dispatchActive):
chain. Mixing the two in one scene works.
Legacy gated
.use()middleware steps continue to live on the composerStorage: builder steps live on
~scene.steps(array of SceneStepEntry).remain unambiguously legacy. No source-level breaks for existing tests.
it's treated as a legacy event filter; otherwise as a step name. Arrays
first string arg is a known Telegram update name (from utils.ts:events),
Disambiguation from the legacy
.step(eventName, handler)form: if thescene.step("intro", c => c.enter(...).on(...)) // named step
scene.step(c => c.enter(ctx => ...).on("message", ...)) // numeric, autoincrement
API surface added:
the full gramio surface (.on/.command/.callbackQuery/.hears/...).
sub-composer with its own .enter/.exit/.fallback/.message lifecycle plus
feat(scene): builder step API — .step(c => c.enter(...).on(...))Step 6 of scene-as-composer. The user-facing payoff: each step is now a
through. 86/86 tests green.
rejection, and plugin .extend() path still working with derive flow-
numeric renumbering, named-step collision throw, module-registration
Tests: 5 new in tests/extend-scene.test.ts cover named-step merge,
event to match on entry.
(.use/.on/.command/...) deliberately skipped because there's no incoming
scene chain and the step's own chain on first entry — regular handlers
running only ctx-mutating middleware (derive/decorate) from both the
results never reached the builder step's .enter handler. Fixed by
was skipping the scene chain entirely, so .derive() / .decorate()
Bug fix uncovered while writing tests: dispatchActive's firstTime path
const checkout = new Scene("checkout")
// Compose into a named scene
);
const confirm = new Scene().step("confirm", c => c
// Module: cannot be entered directly
• intended use: define reusable step blocks (confirm, collect-contact,
• validateScenes() throws if a module is registered in scenes([...])
• new Scene() with no name → ~scene.isModule = true
Step modules (unnamed Scene):
unchanged.
"~scene" in other— non-Scene arguments flow through super.extend()The plugin / EventComposer paths skip the step-merge branch by checking
• lifecycle hooks (.onEnter / .onExit) — A wins; B's copied only when A
• ~scene.steps array — numeric ids renumbered, named ids checked for
• composer middlewares / derives / macros / errors (via super.extend)
scene.extend(otherScene) merges:
together unlock reusable, composable scene flows.
feat(scene): scene.extend(otherScene) + step modules (unnamed Scene)Steps 9 + 10 of scene-as-composer. Two complementary features that
extend(scene) copy semantics. 90/90 tests green.
Tests: 4 in tests/onexit.test.ts cover all three triggers + the
• reenter wraps the existing getSceneEnter call in a small async
• getSceneExit / getSceneExitSub now receive
contextand theImplementation:
extended Scene only when the target Scene has none (A wins).
Also forwarded by scene.extend(otherScene): onExit is copied from the
• ctx.scene.reenter() — fires onExit on the prior occupancy,
• ctx.scene.exitSub() — fires onExit on the sub-scene leaving,
• ctx.scene.exit() — fires onExit, then deletes storage
Triggers:
for cleanup, analytics, "thanks for completing" messages.
user leaves the scene, before the scene's storage is torn down. Useful
feat(scene): add scene.onExit(handler) lifecycle hookStep 8 of scene-as-composer. Symmetric to scene.onEnter — fires when the
90/90 tests green.
two ordering schemes yet. Cleaner unification will land in v0.7.x.
.step("message", ...) — the transition system can't bridge between the
a named-step builder breaks chains where .ask() is followed by a legacy
legacy numeric step (Step 11 of the redesign deferred): migrating it to
Also documents in scene.ts why .ask() is intentionally still wired as a
Otherwise → persist state only
No options + numeric stepId → stepId + 1 (legacy)
No options + builder mode (sceneSteps populated) → walk array index
Explicit options without step → persist state only
Explicit options.step → go to it
Resolution rules in order:
step after calling update().
which silently no-op'd for string ids — the user got stuck on the same
Before: only numeric arithmetic (
stepId + 1) was used as the default,}));
scene.step("email", c => c.message("Email?").on("message", ctx => {
next step in the
~scene.stepsarray, supporting named step ids:fix(update): advance to next builder step when in builder modectx.scene.update({...}) without options now correctly advances to the
123/123 tests pass.
2. firstTime path was running only derive/decorate, skipping .guard
terminal(passthrough)Bug fixes uncovered
• tests/scene-as-composer.test.ts (8) — full Composer DSL on Scene:
• tests/step-lifecycle.test.ts (9) — .enter once-on-firstTime,
• tests/step-navigation.test.ts (7) — step.next/.previous/.go for
• tests/update-semantics.test.ts (7) — ctx.scene.update() auto-advance
Test files
API surface end-to-end. Two real bugs surfaced and fixed along the way.
test: comprehensive coverage for scene-as-composer + bug fixesStep 12 (partial). Adds 30 new tests across 4 files covering the new
130/130 tests green across 12 files.
.derive() in onEnter, matching the documented contract.
.decorate() (static deps, available before middleware runs) instead of
the dispatch chain which fires AFTER onEnter. Tests in this file use
results are not visible to the onEnter handler — they're applied during
Also clarifies in scene.onEnter() JSDoc that scene-level .derive()
• ctx.scene.exit() fires onExit and tears down storage so subsequent
• step.go(name) preserves previousId across back-and-forth navigation
• scene.extend(plugin) AND scene.extend(otherScene) chained together
• sub-scenes with builder steps + returnData merge into parent state
• the same module reused across multiple scenes with independent params
• multiple step modules merged into one scene (intro + survey + farewell)
• multi-step onboarding: decorate (analytics) + named navigation + step
actually hit:
test: complex realistic flows + onEnter doc7 new tests in tests/complex-flows.test.ts covering scenarios users will
132/132 tests green.
fires exactly once across step.go() transitions.
explicitly verify (a) derive value is visible in onEnter and (b) onEnter
onEnter (the natural pattern). 2 new tests in scene-as-composer.test.ts
JSDoc on .onEnter updated; complex-flows test now uses .derive() in
• Storage migration: existing data without
enteredfield treats it as• Legacy mode: a small wrapper in dispatchActive runs derive/decorate
• Builder mode: dispatchActive's firstTime path runs the existing setup
• New
entered: booleanfield on ScenesStorageData distinguishesImplementation:
new Scene("checkout")
ergonomic patterns like the following just work:
Now: onEnter fires AFTER derive/decorate apply on the entry update, so
Users had to choose between deriving data and reacting to it on entry.
so scene-level .derive() / .decorate() results weren't on ctx yet.
feat(scene): scene.derive() values are now visible in scene.onEnterPreviously, scene.onEnter fired BEFORE the scene's middleware chain ran,
132/132 tests still green.
• "Common patterns when editing" section for future agents
• dispatch/dispatchActive naming rationale (collision with inherited
• New
enteredflag explained for "scene-first vs step-transition"• Step API disambiguation rules listed
• Three dispatch paths in dispatchActive documented step-by-step
• "Two parallel slots: ~ and ~scene" — explains why composer's slot
• Module map table for src/
CLAUDE.md rewritten to mirror the actual architecture:
• "Legacy step API" appendix documents back-compat disambiguation
• Storage shape includes the new
entered?: booleanfield• Sub-scenes example modernized to builder API
• New "Type-safe state, params, and exit data" section covering
• New "ctx.scene.update — auto-advance" with named-step rules
• New "Scene lifecycle — onEnter / onExit" section, noting derive
• New "Reusable step modules — scene.extend(otherScene)" section
• New "Core concepts": Scene IS EventComposer, step builder, named ids
• Quick-start uses .step("name", c => c.enter().on()) pattern
docs: rewrite README + CLAUDE.md for scene-as-composer redesignREADME rewritten around the new builder API as primary:
visibility, ctx.scene typing, etc.
that
bun testcan't — generic chains, override surface, derive.test-d.tsfiles) under strict TS. Used to catch typing regressionschore: add tsconfig.test.json + test:types scriptType-checks
src/+ alltests/(including the newtests/types/Tests: 132/132 runtime + 0 type errors across
src/+ alltests/.separately.
loosely typed (typed as
anyshape via plugin derive). Trackedbot.extend(scenes([...]))-sidectx.scene.enter(scene)is stillhost scene declares — write
.params<T>()on the module too OR cast.Module scenes (
new Scene()with no name) can't know what params theWhat's NOT fixed (limitations)
both forms.
event names from arbitrary step names, so ctx gets typed properly in
named-builder overload —
T extends UpdateNamecleanly distinguishesLegacy
step("message", handler)overload moved BEFORE theworks with one type arg (was requiring two).
c.updates<T>()generic order swapped soc.updates<{name:string}>()replace.
collapse to
{count: never}. Switched toModify<>for a clean(intersection) which made
state: Record<string, never> & {count}state<T>()chain bug: was usingDerives & { global: ... }Other fixes
don't pollute state.
State. Handlers that don't callupdate()contribute{}so theythe builder's return via
ExtractStepStateand intersects it intoAccStategeneric.Scene.step(name, builder)readsAccStateoffmethods thread
ExtractUpdateState<ReturnType<H>>into a phantomwhose
.on/.command/.callbackQuery/.hears/.enter/.exit/.fallbackStepComposerStateTracked<...>is a re-typed view of the step composernew Scene("x")
form just didn't pipe it through. Now does:
the handler's awaited return and widened Scene's
State. The builderThe legacy
step(event, handler)already extractedUpdateData<T>fromState auto-inferred from ctx.scene.update({...}) calls
at call sites (needed by overload resolution).
so Scene's structural shape distinguishes
Scene<{id}>fromScene<never>Required making
SceneInternalsgeneric over<Params, State, ExitData>enter(typedScene, {wrong:1})✗ shape mismatchenter(typedScene)✗ params requiredenter(typedScene, params)✓enter(plainScene)✓unknownand accepted any second-arg shape). Now:SCENE GENERIC (not the runtime
~scene.paramscarrier, which was typedRewritten as a two-overload interface that reads
Paramsfrom theSceneEnterHandler enforces params
stripped from the type by the base class's wider return.
this, any chained
.onEnter / .step / .askafter.derive(...)wasScene<Params, Errors, State, Modify<Derives, { global: D }>>. WithoutOverride
.deriveand.decorateto delegate to super and re-type as.derive() preserves Scene<...>
with the parent scene's params/state typed.
and event handlers (
c.on / c.command / ...) all seectx.scenestep level — step builder
c.enter / c.exit / c.fallback / c.messageStepComposerFor<Derives, AccState>does the same threading at the.onEnter / .onExit
all type ctx withctx.scene` now.Scene-level `.on / .command / .callbackQuery / .hears / .use /
inherited method's ctx typing) picks up
ctx.sceneautomatically.scene field. With this,
EventContextOf<this, E>(used by everyphantom (
~.Out) to includeDerives["global"]— which carries theScene gets a
declare "~"that widens the inherited composer's TOutctx.scene is now visible in every handler
commit closes all of them.
made
ctx.scene.update(...)fail to type-check in most places. Thisfeat(scene): fix ctx.scene typing everywhere + auto-state inferenceThe scene-as-composer redesign shipped with several typing gaps that
of them fails at tsc time and we know which DX guarantee broke.
These tests are the contract: when a typing regression sneaks in, one
expect-type) — no extra deps.
tsconfig.test.json). Uses bun:test's
expectTypeOf(vendoredErased at runtime; checked via
bun run test:types(tsc --noEmit -ptyping, scenesDerives.
bot-integration.test-d.ts — bot.extend(scenes([...])) call-site
extend(plugin), stacked merges.
extend.test-d.ts — scene.extend(scene) state/params/errors merging,
from ctx.scene.update() calls, c.events(), c.updates() escape hatch.
narrowing (.on/.command/.callbackQuery/.hears), auto-state inference
step-builder.test-d.ts — step ctx (.send, ctx.scene), per-event
step(event, handler) ctx typing.
& shape enforcement, InActiveSceneHandlerReturn shape, legacy
through chained calls, .ask state inference, SceneEnterHandler arity
scene-chain.test-d.ts — Scene generic chain, .params/.state/.exitData
test: type-level test suite for Scene/StepComposer typing contractsFour
.test-d.tsfiles covering the API surface end-to-end:NodeNext moduleResolution).
index.test.ts: import path includes
.jsextension (required underparams shape backward into the module).
params shape its handlers will see (host scenes can't push their
.params<{who:string}>()because the module needs to know whatcomplex-flows.test.ts: module-scene "tap" now declares
match the storage type widening from the redesign.
core.test.ts:
capturedPreviousIdwidened tostring | numbertothe actual call site.
Switched to
toBeCallableWithwhich exercises the same contract atan overloaded interface; that form can't enumerate overloads).
.parameters.toEqualTypeOf<[scene, params]>(SceneEnterHandler is nowtest: update existing tests for new typing contracts* core.test.ts: issue-#6 enter() test no longer uses
typing (no automatic threading yet).
need to manually mirror child.exitData on parent.state for
the same-update re-dispatch into the parent's resumed step, and the
Also surfaces sub-scene quirks that previously weren't documented:
in syntax.
the architectural overview so first-time readers aren't drowning
storage shape, legacy disambiguation) preserved but moved BELOW
Reference sections (step builder, ctx.scene, plugin options,
Sub-scene flow diagram showing the enterSub ⇄ exitSub bounce
"What goes where" decision table — concern → method → reason
plugin → scene → step → handler → persist)
Update-flow ASCII diagram (dispatch path from Telegram update →
5-minute example with explicit "look ma, no boilerplate" callout
Mental model in 30 seconds (Scene + Steps + lifecycle + ctx.scene)
make decisions:
docs: restructure README around mental model + flow + decision guideOld README was a feature list. New one leads with what users need to
Also drops a dead ComposerLike import in step-composer.ts.
to cut a new release.
so scenes can ship as a patch without waiting on gramio or composer
are both exported from the already-published @gramio/composer@0.4.1,
scenes actually needs at type level (DeriveHandler, EventContextOf)
fix(deps): pull DeriveHandler from @gramio/composer directlyAvoids the unreleased re-export from gramio core. The two helpers
github_release_prereleasefor the GH release.to the
betadist-tag without touchinglatest. Mirrored aspublish.yml: new
npm_taginput (default "latest") so we can shipfix — all collected from the recent commits on scene-as-composer).
ctx.scene typing everywhere, auto-state inference, SceneEnterHandler
chore: bump to 0.7.0-beta.0 + add npm_tag / prerelease workflow inputs* package.json: 0.6.0 → 0.7.0-beta.0 (scene-as-composer redesign,
Bump to 0.7.0-beta.1.
see
ctx.scene— the~.Outwidening already threads it in.Drop the stale comment in scene.ts claiming scene-level handlers can't
not just
c.on("message", ...).scene.derive("message", ...)field reaches every step handler kind,StepEventCtxhelper that matches gramio'sEventContextOf— so aoverloads (
on/command/callbackQuery/hears/enter/exit/fallback) via aWhile in there, unify per-event derive merging across all seven tracked
extends RegExp ? RegExpMatchArray : never`.
`Trigger extends CallbackData ? ReturnType<Trigger["unpack"]> : Trigger
of
CallbackData.unpack()payloads. Mirror gramio's base method:fix(types): infer queryData from CallbackData/RegExp + merge per-event derives in step builderStep builder's
callbackQuerytypedqueryData: any, dropping the typetest(types): verify .extend(plugin/composer) derives reach inside step builder handlers
chore: release 0.7.0
Merge pull request #7 from gramiojs/scene-as-composerScene-as-composer: builder step API, reusable modules, full Composer DSL on Scene
writes a proper .npmrc with _authToken=${NODE_AUTH_TOKEN}.
404 from the registry. Switch to npm publish via setup-node which
(oven-sh/bun#24124) — the unauthenticated PUT gets a misleading
ci: use npm publish to work around bun publish auth bugbun publish 1.3.x ignores NPM_CONFIG_TOKEN in GitHub Actions
What's Changed
New Contributors
Full Changelog: gramiojs/scenes@v0.6.0...v0.7.0
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.