UNPKG

@debonet/es6pacts

Version:

Releasable/cancellable and Reporting Promises for Javascript ES6

125 lines (91 loc) 8.9 kB
# es6pacts v3 — Composition Spec ## TL;DR * Pact remains a one-line composition: `fclassReleasable( Task )`. All release semantics come from es6pledges v3; all reporting semantics come from es6tasks v3 (observer model). * The executor pass-through in the v3 mixin is what delivers Task's `fReport` as the Pact executor's third parameter: `( fResolve, fReject, fReport ) => xPolicy`. * README must be rewritten against the v3 API (the current example is a SyntaxError as written and uses the removed second constructor argument); the new example shows multiple observers and chain windowing. * Version → `3.0.0`, depending on `@debonet/es6pledges ^3.0.0` and `@debonet/es6tasks ^3.0.0` (all three packages ship as 3.0.0; es6tasks skips 2.x for family alignment). * Test suite expands beyond the single resolve-path test; nothing is deferred — the reporting model is decided. ## Purpose Pact composes releasability onto reporting by applying the es6pledges mixin to Task. ## Normative References Shared v3 semantics are owned upstream and are not restated here: * `../es6pledges/specs/v3-core.md` — constructor, executor, policy, pipeline, dispatch, wrappers, release contract, verbs, `releaseOn`. * `../es6pledges/specs/v3-chains-combinators.md` — chain and combinator release. * `../es6tasks/specs/v3-reporting.md` — observer model, microtask delivery, post-settle suppression, windowing, combinator report format. * `../es6tasks/specs/v3-bugfix-appendix.md` — Task crash-defect history. (Workspace-relative paths; in published form these are the `specs/` directories of `@debonet/es6pledges` and `@debonet/es6tasks`.) ## Composition Contract * `src/es6pacts.js` stays exactly: require `fclassReleasable` from `@debonet/es6pledges/fclassReleasable`, require `Task` from `@debonet/es6tasks`, export `fclassReleasable( Task )`. * Executor pass-through: the mixin forwards all executor parameters beyond `fResolve` / `fReject` from the wrapped class (requirement stated in the core spec). Task passes its report function third, so a Pact executor is `( fResolve, fReject, fReport ) => xPolicy` and the release handler may close over `fReport`. * `Pact.PactResolve` / `Pact.PactReject` are available as statics via the mixin. * `then( fOk, fErr )` is standard 2-arg in Task v3 (the `aOpts` third argument is deleted). The mixin's generic extra-argument pass-through in `then` remains harmless and future-proof, but no extra argument is consumed. * Task's own settle-wrapping (capability bookkeeping around `fOk` / `fErr`) is preserved because the mixin captures the capabilities Task passes into the executor. * Interlocking guarantees, both enforced upstream and re-verified here at composition level: * Post-settle report suppression means a released (hence settled) Pact stops reporting — no pacts-specific code needed. * A REFUSED release leaves the Pact pending, so reports keep flowing during and after refusal. ## README Rewrite Requirements Rewrite `README.md` for the v3 API. Specific corrections to the original document: * The example is a SyntaxError as written: stray `;` after `.progress(( x ) => ... )` before `.then( ... )`. Fixed example chains without the semicolon. * The example computes `let r = ...` and never uses it. Remove or use it. * The example uses the removed second constructor argument. v3 form: the executor returns the cleanup, e.g. `return () => clearInterval( interval );`. * The PRIMARY example is the shared cross-library example below, exactly as specified. Usage MUST hold and release the SOURCE pact (cancel-at-source idiom) — releasing a chain tail skips its own handler under scoped release and would make the shown output wrong. * A SECONDARY example keeps the full windowing walkthrough: a `.progress` attached before a `.then` (sees that stage only) and a `.progress` attached after it (sees the forwarded parent stream plus the handler-returned pact's stream), with the release arriving mid-chain and the output block showing suppression. * State plainly that progress reports stop when the pact settles — now a normative library guarantee (post-settle suppression in the reporting spec), not executor discipline. * Document, by summary plus links to the upstream specs: single-argument constructor, executor-returned policy, type dispatch with `PactResolve` / `PactReject`, refusable `resolve` / `reject` / `fulfill`, the release return contract, `releaseOn`, observer-model `.progress` (independent observers, no replay, returns `this`). * Version references in the README (dependency badges, "Full Semantics" cross-references, install snippets): all three packages are 3.0.0; do not cite an es6tasks 2.x anywhere. * Style: `*` lists, BLUF, no hyperbole. ### Primary example (shared across the three libraries) The three READMEs (es6pledges, es6tasks, es6pacts) share ONE running primary example — the interval delay function — so the composition is visible by inspection. THIS repo owns the canonical superset form; the parent libraries' forms are reductions of it (es6pledges drops the reporting lines; es6tasks drops the cleanup line). Use this code verbatim (names and structure are pinned; minor drift here breaks line-compatibility in three READMEs at once): ```javascript function fpactDelay( dtm, dtmTick = 100 ){ return new Pact(( fResolve, fReject, fReport ) => { let dtmElapsed = 0; const interval = setInterval(() => { dtmElapsed += dtmTick; fReport( dtmElapsed ); if ( dtmElapsed >= dtm ){ clearInterval( interval ); fResolve( dtm ); } }, dtmTick ); return () => clearInterval( interval ); }); } ``` Usage (source-held; the release lands on the source and flows downstream): ```javascript const pactDelay = fpactDelay( 10000 ); pactDelay .progress(( dtmElapsed ) => console.log( "progress:", dtmElapsed )) .then(( x ) => console.log( "result:", x )); setTimeout(() => { pactDelay.resolve( "faster" ); }, 250 ); ``` Shown output: ``` progress: 100 progress: 200 result: faster ``` (The release at 250ms sits mid-gap between the 200ms and 300ms ticks, so the shown output is stable; the cleanup clears the interval and post-settle suppression guarantees no report after settlement.) Diff contract (state it in the README next to the example): remove the `fReport` executor parameter and the `fReport( dtmElapsed );` line and this is `fpledgeDelay` (`@debonet/es6pledges` — releasable, no reporting); remove instead the `return () => clearInterval( interval );` line and this is `ftaskDelay` (`@debonet/es6tasks` — reporting, not releasable). All other lines are identical across the three libraries. Pact is the union, exactly as `fclassReleasable( Task )` is the union of the two libraries. ## Versioning * `package.json` version → `3.0.0` (breaking, tracks the pledges v3 API and tasks v3 reporting). * `dependencies`: `@debonet/es6pledges``^3.0.0`; `@debonet/es6tasks``^3.0.0` (es6tasks ships as 3.0.0 — version-family alignment; semver permits skipping 2.x from 1.0.11). * Note: the repo's current `2.0.1` is behind the published npm `2.0.3`; v3 supersedes both — publish from this repo at `3.0.0`. ## Build and Test Interpreted-language project; no build step. * Test runner: jest `^29` (existing devDependency), command `npm test`. * Tests: `/Users/jsd/@aios/tmp/es6pacts/test/es6pacts.test.js`, expanded from the single resolve-path test to cover composition-level behavior: * Executor receives `fReport` as the third parameter; reports reach `.progress` observers with observer-model semantics (same raw value to multiple observers, attachment order). * `release( xReason )` on a Pact runs the executor-returned cleanup and settles by dispatch; reports issued after settlement are dropped (suppression at composition level). * Refusal: cleanup that rejects leaves the Pact pending WITH reports still flowing to observers. * `resolve()` / `reject()` verbs on a Pact chain with `.progress` attached. * Chain windowing on Pacts: observer attached before `.then` sees only that stage; observer attached after sees the forwarded stream plus a handler-returned Pact's stream (the reporting spec's windowing example, exercised through the mixin). * `releaseOn` with an AbortController. * A Pact combinator (`Pact.all`) emits `{ task : n, report : x }` member reports and both report suppression and group release work on the combined pact. * v2 continuity: `test/v2compat.test.js` per `specs/v3-v2compat.md` (additive; existing suite untouched). * The test file's existing polyfill shim for running without jest is dropped; jest is the declared runner. ## Dependencies * `@debonet/es6pledges ^3.0.0`, `@debonet/es6tasks ^3.0.0`. ## Error Handling Entirely inherited: refusals per the pledges core spec, task rejections and observer-exception isolation per the tasks reporting spec. This module adds no error surface.