Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

21 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dsh-plugin-shared logo

dsh-plugin-shared

The shared library behind the dsh plugin family: a tool-event record table, card state helpers, a unified settings-card UI kit, a project key and a set of locale helpers — imported by plugins, never enabled by users.

npm version npm downloads (total) ci MIT node dsh powered by dsh

tech stack

GitHub Email Twitter/X 简体中文

A dsh library package: the runtime modules and lint/test baselines shared by one author's plugin family. It is only imported by plugin packages; users never enable it.

Contents

What it does

  • It is a library plus one client row, not a standalone bundle. The host half is imported by the plugins; package.json declares dsh.client and exports ./client, which each consumer enables from its own cordis.patch.yml with an id-less entry. It carries no dsh.bundle.patch, so installing the package alone adds no profile layer and no settings card or route. The id-less entry keeps the row out of the plugin page's per-component switches: switching it off would leave every settings card without its component supplier.
  • It holds the parts the plugins repeated verbatim:
    • The tool-event record table (./lib/tool-events): one scan from SessionEvent[] to call and result tables, PTC sub-calls included, plus editPathOf for the edit targets the record can see and applyPatchTargetsOf / editPathsOf for every write target in a multi-file apply_patch input, parsed by the official dsh-fs-patch.
    • Client card base components (./lib/card-apply, ./lib/card-diff): an apply idempotency guard across HMR, and a form-value diff that treats null, undefined and an absent key as the same value.
    • Runtime utilities (./lib/project-key, ./lib/locale, ./lib/lesson-bus, ./lib/text, ./lib/record, ./lib/errors, ./lib/jsonl, ./lib/job-outcome): the small logic every plugin would otherwise rewrite.
    • Build-time and check baselines (./lib/canonicalize-region-paths, ./config/*): artifact path normalization plus the oxlint, vitest and tsconfig starting points for every consumer.
  • It handles the generic part; domain decisions stay out. Which tools count as edits, path filtering, budgets and when a check should fail stay in the plugins. The shared layer makes no judgement about the domain handed to it.

What belongs here

  • Verbatim duplication is the admission rule. The same body in two or more places. A family that looks similar but differs in failure semantics, caching or override behavior stays where it is.
  • Domain judgement stays out. A predicate that depends on a plugin's own tool list, policy or user-facing wording is not boilerplate, even when it appears twice.
  • The published interface is a commitment. Adding a module means adding a subpath, a barrel re-export (except the build-time helper), a builder entry, the build include and a test; the consistency checks in Development fail if one is missed.

Install

npm install @jayyuen66/dsh-plugin-shared
# or
pnpm add @jayyuen66/dsh-plugin-shared

Declare it as an ordinary dependency of your plugin package and import it by bare-package subpath:

import { isRecord, fieldOf } from "@jayyuen66/dsh-plugin-shared/lib/record";

End users do not install or enable it separately: any consumer package pulls it in. When it is missing, the failure is ERR_MODULE_NOT_FOUND on a @jayyuen66/dsh-plugin-shared/... specifier, never a silent downgrade. There is no default export; import named exports from the barrel or a subpath.

Requirement Value
Node.js >=24.9.0 — the publish-side floor the plugin family shares, so a package declaring the same engines.node never rejects this library
dsh ^0.2.2-rc.1, declared as an optional peer (importing this package does not need a host)
Module format ESM only. require() works only where Node supports require(esm)
Tree shaking "sideEffects": false
  • The host window rolls. This package is verified against the exact development line pinned in devDependencies; the ranges in engines.dsh and in the peers are an install gate and a compile-time discipline, not the verification scope. Moving the window means moving those pins, re-running the host parity check and republishing.

Dependency requirements

dependencies contains exactly one exception, @deepseek-ai/dsh-fs-patch; every other @deepseek-ai/* entry lives only in peerDependencies and devDependencies. test/publish-manifest.test.ts enforces this, and the reason is a boot-time failure rather than style:

  • @deepseek-ai/dsh-fs-patch is the one allowed runtime dependency. It is a stateless pure parser: it holds no service instance or module-level identity, so a duplicate copy cannot split a Symbol. Shipping it makes apply_patch syntax resolvable on every host in the supported window. Every other host package stays in peer/dev.
  • A host package under dependencies disables the consumer's host. Profiles install with nodeLinker: hoisted and autoInstallPeers: false, so a listed host package is materialized as a real directory next to the copy the host process already loaded. @deepseek-ai/dsh-tools keys its tool-runtime scheduler with a plain Symbol (not Symbol.for), so symbol identity is per module instance: the host reads the scheduler from the duplicate and gets undefined, and every native tool call throws. Build, typecheck and tests pass up to that point; the invariant has to be stated in the manifest.
  • @deepseek-ai/dsh-output-retention is the one required peer. It is a value import (truncateEnd), provided by the host at runtime, and marked "optional": false in peerDependenciesMeta rather than left implicit — a peer left out of that table is a hard requirement for npm 7+.
  • The remaining peers are optional. Except for the bare @deepseek-ai/dsh peer, which only declares the host line, most carry type declarations only. Two subpath-scoped value peers are the exceptions: react is value-imported by ./lib/card-ui, and @deepseek-ai/dsh-typert-protocol by ./lib/remote-marker; both stay optional, so only a consumer of that subpath needs the package present. devDependencies holds the exact development line, including @deepseek-ai/dsh-llm and @deepseek-ai/dsh-util-values, whose types the local PTC declarations name, and @deepseek-ai/dsh-tool-executor-ptc, whose types only the parity check reads. Profiles never install peers, so no duplicate appears.
  • Version requirements separate the install gate from the build baseline. Every @deepseek-ai/dsh-* peer carries the same window as engines.dsh — ^0.2.2-rc.1, a single host line. A caret's prerelease comparison only covers its own patch tuple, so ^0.2.2-rc.1 admits 0.2.2-rc.1, later 0.2.2 prereleases and 0.2.2/0.2.x, but not the older 0.2.1 line or 0.2.2-alpha.*; the host's own comparison passes {includePrerelease:true}, which is what lets same-tuple prereleases participate. The cost is measured and belongs here: npm resolves each peer on its own, while the host family pins its own line inside every member (0.2.2-rc.1 asks cordis ~4.1.0 and schemastery ~3.19.0), so a consumer that pins an older host line can still stop at ERESOLVE, while the same tree resolves on the supported line. pnpm — the installer this workspace supports, and the one behind the host's dsh plugin add — resolves both shapes and only prints a peer warning. The loading gate is unaffected either way, because the host compares engines.dsh with includePrerelease: true. devDependencies pin the exact line this repository compiles and tests against (0.2.2-rc.2) and must stay uniform: one dsh-session version per tree keeps the nominal types recognizable. @deepseek-ai/cordis stays exact in devDependencies (4.1.0); its peer range ^4.0.5||^4.0.6-alpha.1 is retained unchanged and admits 4.1.0. The cordis union carries no spaces around ||; the install-matrix probe reads one specifier per line now. test/publish-manifest.test.ts checks each of these separately.
  • Known upstream limitation: @deepseek-ai/dsh-llm's declarations import @deepseek-ai/dsh-attachment, which that package does not declare. It is reached through dsh-session, so a consumer running with skipLibCheck: false sees TS2307 from that package, not from this one. The published ./lib/tool-events declaration also names @deepseek-ai/dsh-util-values, which is reached through dsh-session / dsh-tools; a consumer running with skipLibCheck: false must have that package resolvable as well.

Shared version floor

A consumer profile can lock this package with a pnpm overrides entry. Lock it below the version that introduced a ./lib/... subpath and the host boots with N entries did not activate plus does not provide an export named ...: the package installs, never loads, and no per-repository CI run sees it. Check this package's exports and CHANGELOG before locking.

What ships

  • main and exports point at dist/ only; there is no second, source-shaped manifest. Node throws ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING for .ts inside node_modules, which is exactly where a published package is loaded from, so an artifact is mandatory.
  • files is icon.svg, dist, config/*.json and README.zh-CN.md. npm always adds package.json, LICENSE and README.md; the Chinese copy is listed explicitly so it ships too.
  • publishConfig carries access and registry and nothing else. Entry fields there are a trap: pnpm merges them onto the top level when publishing and npm does not, so one publisher would ship a package whose entry points name files that are not in the tarball.
  • build deletes dist/ first, then emits the runtime artifacts, the config artifacts, the type declarations and the generated barrel declaration. Both builders minify; test/dist-size.test.ts keeps the runtime and config artifacts inside their budgets.
  • prepare runs build. It does not run when a consumer installs from the registry; it exists so packing, publishing and workspace links always ship a fresh dist/.
  • Adding a runtime module means touching the barrel in lib/index.ts, exports, the facets array in build-host.mjs, include in tsconfig.build.json, and a test/<module>.test.ts. A subpath-only runtime module (like remote-marker or card-ui) is registered in entries and marked barrel-exempt instead; a build-time module (like decorator-lowering) goes into buildFacets. The consistency checks in Development fail if one is missed.

Verify it is live

  1. A subpath resolves. From the consuming package, node -e 'import("@jayyuen66/dsh-plugin-shared/lib/record").then((m) => console.log(typeof m.isRecord))' prints function. ERR_MODULE_NOT_FOUND means the dependency is not installed; does not provide an export named ... means the installed copy is older than the subpath you import.
  2. The barrel resolves. The same check against the bare package name returns the runtime modules. canonicalize-region-paths (build-time), remote-marker (value-imports typert), and card-ui (client-only) are deliberately absent from the barrel; import them by subpath.
  3. A consumer host boots. Start the profile that declares the plugin. A boot log containing N entries did not activate and does not provide an export named ... means the profile's overrides version is below the version that contains the subpath the consumer imports — raise the version, do not patch the plugin.
  4. Local source edits reach the consumer. Consumers load dist/, not lib/. Run pnpm run build after editing; test/build-host.test.ts compares the runtime artifacts on disk against an in-memory build and fails when they are stale. "The code changed but nothing happened" is almost always a stale artifact, not a caching bug.

What it does not provide

  • No bundle declaration. It declares dsh.client for the client row but no dsh.bundle.patch; a copy in node_modules is not a profile layer by itself. Consumers enable the row from their own patch.
  • No settings card of its own. The client row exports card components; the package registers no card, owns no client state and serves no route.
  • No HTTP route and no request-trust checks. The host's /api gateway owns authentication, the Host / Origin checks and body limits for the plugins that do register a route.
  • No CLI subcommand and no model tool.
  • No domain judgement. It does not know which tool counts as an edit, which path is secret, how large a budget is, or when a check should fail. Those belong to the calling plugin.

Modules

The subpath column is the in-package specifier; value imports prefix it with the bare package name. exports also carries ./package.json, which the host reads for display metadata.

Subpath Provides
. Barrel over the runtime modules below, served from dist/plugin.js: the same names plus name and a no-op apply, so the host loader can also take this package as a row. canonicalize-region-paths (build-time), remote-marker (value-imports typert), and card-ui (client-only) deliberately stay out; the root barrel must not pull an unused package into every consumer.
./lib/tool-events scanToolEvents returns the calls / results tables, PTC sub-calls included; toolEventRowsOf handles a single event and already carries normalized arguments and badArguments. editPathOf returns a kind-discriminated EditTarget with only write / read-view; the path is undefined when it cannot be read. applyPatchTargetsOf parses the ordered operations of an apply_patch input with the official dsh-fs-patch; editPathsOf expands every write target, including both sides of a move. Record fields are readonly. Each result row also carries the structured failure identity error from the event's data.error (name and code must be non-empty strings; a partial shape reads as absent, and reason is kept only when it is a string). Re-exports the official SessionEvent; PtcDispatchStartEventData / PtcDispatchEventData are declared in this package and augment SessionEventMap locally, because the host moved that augmentation between packages inside the supported window.
./lib/card-apply claimApply + CardApplyCtx: an apply idempotency guard built on an own flag on globalThis (a symbol, or true written by an outside holder) with ctx.effect cleanup. The flag is process-wide, so a second context on the same page is rejected by design.
./lib/card-diff diffTouched: which touched fields really differ from the snapshot, compared by form value. undefined, null and an absent key are one value; arrays and objects compare element by element; cycles terminate.
./lib/card-ui Client-only settings-card kit: CardForm, Section, FieldGrid, StatusBar, Field, FieldLabel, InfoTip, ToggleField, NumberField, TextField, ListField, SelectField, SaveBar, splitHint and the CARD_UI_CSS string. Built with createElement only; react is an optional peer and the subpath never enters the root barrel. The declared peer floor is ^19.3.0; the kit uses only APIs that already existed in React 18 (useId, useState, useEffect, refs).
./client Browser client row: the __ModuleLoader__ bundle the host module graph serves once. Consumers externalize this specifier and enable the row from their own patch with an id-less entry.
./client-node Node/ESM face of the same client surface, used by tests and type-aware Node consumers.
./lib/project-key deriveProjectKey + ProjectKeyOptions: cwd → last-segment-<first 8 hex of sha256(norm)>, with default for a non-string, an empty string or a normalized root. realpathSync resolves symlinks; a failure falls back to the resolved path. Windows \ and / produce the same key.
./lib/locale Locale (an alias of the official BuiltInLocaleId), DEFAULT_LOCALE, resolveLocale, resolveLocalePreference, messagesFor, MessagesCatalog, plus LOCALE_SETTINGS_NAMESPACE / LOCALE_PREFERENCE_FIELD. Host-side dictionaries only; the client uses the official locale package.
./lib/lesson-bus settleLessonCall: routes synchronous throws and asynchronous rejections into one onFailure exit. Function-valued thenables count as awaitable; an onFailure that itself throws or rejects is handled in the same place, so it never splits into "thrown at the caller" or "unhandled rejection".
./lib/text truncateEnd delegates to the official truncateWithoutSplittingSurrogatePair; truncateStart is the back-cut form the official exports do not provide. Both are surrogate-pair safe; a budget below one code unit (including <= 0 and NaN) returns an empty string.
./lib/record isRecord, fieldOf, hasMethods. fieldOf reads through the prototype chain by design (cordis service methods are defined there); a read that hits a malicious getter or Proxy trap returns undefined instead of throwing. hasMethods requires every named method to be callable and does not accept Object.prototype members as a service implementation.
./lib/errors errorText follows the official @deepseek-ai/dsh-llm errorChain semantics (cause chain, AggregateError members, empty message → name, cross-realm message, malicious getters render as a placeholder) in a zero-dependency local implementation. isSettingsConflict recognizes the host's settings-conflict pattern from code / name, so a cross-realm error is still recognized.
./lib/jsonl shrinkJsonlTail: pure decision logic for JSONL tail shrinking. The trigger policy and error handling stay with the caller; the byte budget is the caller's responsibility to keep finite.
./lib/job-outcome jobOutcomeOf + SettledProcess + JobOutcome map a finished subprocess onto the official job registry's outcome; killProc + KillableProcess release a child handle and ignore a kill() that throws or rejects (for example, on an already-reaped process).
./lib/remote-marker markRemote(prototype, methodName): the programmatic equivalent of the official @Remote method decorator. It applies the same decorator function to a hand-built TC39 context, so a source file can avoid decorator syntax and needs no lowering pipeline. It value-imports @deepseek-ai/dsh-typert-protocol (an optional peer) and stays out of the root barrel.
./lib/decorator-lowering Build/test helper for standard decorators: needsDecoratorLowering, lowerStandardDecorators, rolldownDecorators, viteDecorators. It runs the caller-injected tsc entry on one file with --noResolve, preserves module specifiers, returns code plus a source map, and (on the Vite side) prunes injected plumbing from coverage mappings. Build-time only, never in the runtime barrel.
./lib/canonicalize-region-paths Build-time string helper that rewrites rolldown's //#region <path> markers into a process.cwd()-independent form. Exported as a subpath so build scripts can import it without putting it in the runtime barrel.

card-ui (value-imports react) and remote-marker (value-imports @deepseek-ai/dsh-typert-protocol) are the runtime modules that are deliberately subpath-only: putting them in the barrel would make every host-side importer resolve a package it does not use.

The client row is enabled by the consuming plugin, not by installing this package alone. The consumer writes - name: "@jayyuen66/dsh-plugin-shared" without an id; the host still loads it, while the plugin page does not expose it as a separately disableable component. An id would add that switch, and switching it off would break every settings card that requires the row's components.

The config exports

These are lint/test baselines for plugin authors, not runtime code. They pull in tooling your package must provide itself:

Subpath Provides You must install
./config/oxlint definePluginConfig, makeTestFileOverrides, OFF_JUSTIFICATIONS and the shared rule baseline oxlint, eslint-plugin-sonarjs, eslint-plugin-regexp, eslint-plugin-zod (requires zod 4), @e18e/eslint-plugin
./config/vitest.base definePackageConfig: four 100% coverage thresholds, a 20-second test timeout, the shared coverage exclusions, the test include globs, and the @jayyuen66/dsh-plugin-shared/client → @jayyuen66/dsh-plugin-shared/client-node alias (the UMD row reads window at load time, so a Node test cannot execute it) vitest and @vitest/coverage-v8 (the baseline sets coverage.provider: "v8")
./config/tsconfig.base.json, ./config/tsconfig.client.base.json Plain JSON for extends Nothing

Those toolchain packages are not declared as peerDependencies: npm 7+ tries to satisfy optional peers too, and oxlint's own peerOptional vite-plus locks vitest to a version disjoint from >=5.0.2, which made a plain npm install of this package fail with ERESOLVE. The requirement is stated here instead of in the manifest.

  • All four JS plugin entry points (sonarjs / regexp / zod / e18e) are resolved lazily inside definePluginConfig(). Importing ./config/oxlint without any of them succeeds; calling definePluginConfig() throws only when that plugin is neither installed nor supplied through jsPlugins, and the message names the specifiers it tried plus how to provide your own entry.
  • e18e/prefer-static-regex is enabled only on production paths (lib/**, src/**, host.ts, cli-main.ts); regex literals in tests and build scripts are not treated as defects. The measured counts and rationale live in OFF_JUSTIFICATIONS.
  • Every rule turned off in the baseline needs a kind + measured + note entry in OFF_JUSTIFICATIONS, or configuration loading throws. A package-level exception goes in that package's own offReasons.
  • definePackageConfig refuses an empty coverageInclude and refuses a test.coverage block: either one would silently drop the four thresholds or the coverage statistics. An exception belongs in the package that needs it, next to its own coverage override.

Known boundaries

One entry per class; each is fixed by a test at its current behavior.

  • fieldOf reads the prototype chain on purpose. Object.hasOwn would make every cordis service method invisible. Code that reads a user settings value must use the own-property form instead; resolveLocalePreference is the example and is covered by tests in both directions.
  • claimApply is a process-wide flag, not a per-context lock. A second context applying the same card on one page is rejected. A consumer that needs multiple contexts must manage its contexts itself rather than "fixing" this protection.
  • shrinkJsonlTail does not normalize the budget. NaN and +Infinity disable shrinking entirely; 0, negative values and -Infinity still shrink to at most two lines. The caller owns the trigger policy (including "0 means never trim") and the error handling.
  • jobOutcomeOf copies the host bash tool's wording. The tests check the literal exit code: N / signal: NAME strings, not equality with the upstream function, which is not importable from the published package. If upstream changes its wording, this package keeps passing and its display text stays its own.
  • editPathOf knows one read-only spelling. Only str_replace_editor with command: "view" is read-view; every other tool that carries file_path is write without further inspection.
  • The PTC payload interfaces are a local copy. The host moved that SessionEventMap augmentation between host packages inside the supported window, so lib/tool-events.ts declares the two interfaces and augments the map itself. test/tool-events-host-parity.ts turns drift against the development baseline into a pnpm typecheck failure; a runtime assertion cannot, because the types are erased.

Data and privacy

The package is imported into a host process; the table describes run time.

Aspect Content
File access deriveProjectKey calls realpathSync on the path you hand it, and only that path. A failure (ENOENT / EACCES) falls back to path.resolve and never throws. No other file access exists in lib/**.
Writes None. Nothing in this package writes a file, a cache or a log.
Environment No environment variable is read.
Network No outbound request, no HTTP server, no subprocess.
Dynamic code No eval, no new Function, no dynamic import().
Process memory The only mutable cross-instance state is the claimApply slot on globalThis, restored when the effect disposes or when claiming fails.
Logging console.error appears only when an onFailure callback in lesson-bus itself throws; the test setup checks every [shared/*] line against a template list.

Versioning and compatibility

  • Semantic versioning applies to the public interface: subpaths, exported value symbols, exported types, and error/reason enums. 0.x ranges admit patches only, so touching any of those is already breaking under the ranges consumers wrote.
  • deriveProjectKey's hash algorithm, the 8-hex slice and the trailing-segment whitespace cleaning are frozen. Changing one digit leaves existing lessons and memories under a key that no longer matches.
  • Windows path normalization changed the key shape. The same directory now yields last-segment-<hash> whether it is spelled with \ or /, and a drive root or UNC share root falls into default. Keys written before normalization on Windows no longer match; POSIX keys are byte-for-byte unchanged and fixed by literal assertions.
  • Locale is an alias of the official BuiltInLocaleId, so it tracks the host's LOCALE_IDS instead of a hand-written union.
  • errorText returns display text, not a parseable format. It follows the official errorChain semantics: a cause chain renders as outer: inner, an AggregateError appends [e1; e2], an empty message falls back to Error.name, and a cross-realm Error yields its own message instead of String()'s "Error: ...". Callers that regex it are outside the supported usage.
  • editPathOf returns a kind-discriminated EditTarget with only write / read-view. "skip" was never produced; keeping it in the type only pushed consumers into unreachable branches. When no path can be read it still returns the matching arm with path: undefined.
  • ./lib/http and ./lib/trust are gone. The plugin family's settings-card data plane runs on the official Typert Remote, whose /api gateway owns authentication, the Host / DNS-rebinding / Origin checks and the body cap. A plugin that still wants a raw webServer route has to write these checks itself — the published package no longer offers a second, divergent answer to "is this request allowed".
  • The declarations are compiled against one host line and installed across a peer window. devDependencies lock the exact @deepseek-ai/* line used for build and test; the dsh peer family and engines.dsh share the supported range, so a host inside that window is accepted without a second build. Moving the window means updating the development pins, re-running the parity check and republishing. Widening the peer range past the verified window is not supported.

FAQ

Question Answer
Should I dsh plugin add it? No. It declares dsh.client for the row consumers enable, but carries no dsh.bundle.patch, so adding it as a plugin installs no profile layer and enables nothing by itself. Declare it as a dependency of the plugin package, and insert the id-less row in your own cordis.patch.yml.
A consumer boots with does not provide an export named ... and N entries did not activate. The profile is serving an older copy of this package than the consumer imports. Raise the profile's overrides entry to a version that contains the subpath the consumer imports; check this package's exports and CHANGELOG. No per-repository CI run can see this state.
I changed lib/*.ts and the consumer still runs the old behavior. Consumers load dist/. Run pnpm run build; test/build-host.test.ts fails if the runtime artifact is stale. A workspace link does not bypass the artifact.
Why is @deepseek-ai/dsh-output-retention a required peer while the rest are optional? It is the only required host package imported for value (truncateEnd). @deepseek-ai/dsh-typert-protocol is also value-imported, but only by ./lib/remote-marker, so it stays optional; the remaining dsh peers are type-only. The bare @deepseek-ai/dsh peer only declares the host line, and react is a value peer for ./lib/card-ui.
Why isn't oxlint a peerDependency? npm 7+ would try to satisfy it, and oxlint's optional vite-plus peer locks vitest outside the range this baseline needs, producing ERESOLVE for a plain install. The requirement is documented instead.
Want a helper added here? First confirm the body repeats verbatim in two or more packages. Domain judgement stays in the plugins.

Development

pnpm install
pnpm run check   # typecheck → lint → build → test → format check
pnpm test        # vitest with coverage
pnpm run build   # runtime artifacts, config artifacts, declarations, client row
  • The suite splits by the class of defect a stub cannot see:
    • Module behavior: one test/<module>.test.ts per runtime module.
    • Surface parity: test/index.test.ts (barrel ↔ lib/ value exports), test/publish-manifest.test.ts (exports ↔ lib/ in both directions) and test/build-host.test.ts (build-host.mjs facets ↔ lib/). Each catches an omission the other two cannot see. test/tool-events-host-parity.ts is the declaration-side counterpart: it makes the locally declared PTC payloads and the development baseline mutually assignable, and it has no runtime case because a type assertion is erased before vitest can judge it; pnpm typecheck runs it.
    • Artifact checks: test/build-host.test.ts compares the runtime artifacts on disk with an in-memory build byte for byte; test/dist-size.test.ts holds the runtime and config artifacts to their byte budgets; the same test requires the official value import to stay a bare specifier and rejects runtime specifiers for dsh-jobs / dsh-shell.
    • Config checks: test/config-oxlint.test.ts compares the vitest rule snapshot with oxlint --rules and requires every off to be justified; test/vitest-base.test.ts requires the merge behavior that keeps the four thresholds and the coverage statistics.
    • Log records: test/setup-logs.ts takes over console.*; every [shared/*] line must match test/log-templates.ts or the run fails. test/log-templates.test.ts checks both directions, so adding or deleting a log without updating the record list fails.
    • Published shape: test/publish-manifest.test.ts requires every export target to be inside files, entry points to be artifacts rather than source, publishConfig to carry no entry fields, every peer to be stated in peerDependenciesMeta, every dsh-* development pin to be exact and uniform, every dsh peer range to equal engines.dsh, and the source, docs and artifacts to carry no dates, absolute paths, home-directory references or collaboration-only wording.
  • Coverage thresholds are 100% across lines, statements, functions and branches, so a new branch needs a case that reaches it. test/vitest-base.test.ts refuses an empty coverageInclude because that would make the four thresholds pass over nothing.
  • CI runs the checks on Linux, Windows and macOS on Node 24.9, with a weekly scheduled run to catch upstream changes. The install matrix packs with both npm and pnpm, installs into a clean directory without peer-resolution flags, imports every subpath and type-checks a small consumer import under both skipLibCheck settings.

License

MIT. Source at github.com/JayYuen666/dsh-plugin-shared. LICENSE ships with the package.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages