diff --git a/.changeset/allow-astrometry-dependency.md b/.changeset/allow-astrometry-dependency.md new file mode 100644 index 0000000..14a08c8 --- /dev/null +++ b/.changeset/allow-astrometry-dependency.md @@ -0,0 +1,5 @@ +--- +'@observerly/orderly': minor +--- + +Add @observerly/astrometry 0.69.0 as orderly's one runtime dependency, observerly's own library with no runtime dependencies of its own, ahead of the sun schedules built on it; the dependency guard now admits exactly that name and fails the package checks on any other. diff --git a/README.md b/README.md index e5c517a..2530e64 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ Single file, no pushing. Type-safe middleware that brings a little bit of order orderly removes the two failure modes every raw queue consumer carries: messages that are never decided, and one bad message crashing a whole batch. Every message resolves to exactly one explicit -outcome, every outcome is recorded as an event, and the consumer never rejects. Zero runtime -dependencies. +outcome, every outcome is recorded as an event, and the consumer never rejects. Zero third-party +runtime dependencies. ## Installation @@ -358,8 +358,9 @@ subtract(deadline, { days: 1 }); ## Requirements orderly runs on [workerd](https://github.com/cloudflare/workerd) and is tested inside it. It ships -as ESM with TypeScript declarations, requires nothing at runtime, and follows Standard Schema -rather than depending on any validation library. +as ESM with TypeScript declarations and follows Standard Schema rather than depending on any +validation library. Its one runtime dependency is observerly's own `@observerly/astrometry`, which +itself depends on nothing at runtime; a guard fails the package checks should another ever appear. ## Contributing diff --git a/package.json b/package.json index 63a5d8a..fb29aff 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,9 @@ "test:watch": "vitest", "typecheck": "tsc --noEmit && tsc --noEmit -p tests/tsconfig.json && tsc --noEmit -p scripts/tsconfig.json" }, + "dependencies": { + "@observerly/astrometry": "0.69.0" + }, "devDependencies": { "@arethetypeswrong/cli": "0.18.5", "@changesets/changelog-github": "1.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05e0acc..4269b6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,10 @@ settings: importers: .: + dependencies: + '@observerly/astrometry': + specifier: 0.69.0 + version: 0.69.0 devDependencies: '@arethetypeswrong/cli': specifier: 0.18.5 @@ -555,6 +559,9 @@ packages: resolution: {integrity: sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==} engines: {node: '>=20.0.0'} + '@observerly/astrometry@0.69.0': + resolution: {integrity: sha512-bgPl2sKbq+T1ixdDTThsYPFx7TgVLRnR2Xaab0CC+vxIEc+tKNolbEGQx+o1mmo2nMn74gMTZVR21Is5co/rKA==} + '@oxc-project/types@0.144.0': resolution: {integrity: sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==} @@ -965,6 +972,9 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/node@24.13.3': resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==} @@ -2398,6 +2408,10 @@ snapshots: tinyglobby: 0.2.17 yaml: 2.9.0 + '@observerly/astrometry@0.69.0': + dependencies: + '@types/geojson': 7946.0.16 + '@oxc-project/types@0.144.0': {} '@oxfmt/binding-android-arm-eabi@0.63.0': @@ -2611,6 +2625,8 @@ snapshots: '@types/estree@1.0.9': {} + '@types/geojson@7946.0.16': {} + '@types/node@24.13.3': dependencies: undici-types: 7.18.2 diff --git a/scripts/guard-dependencies.ts b/scripts/guard-dependencies.ts index 746d39f..48d3a10 100644 --- a/scripts/guard-dependencies.ts +++ b/scripts/guard-dependencies.ts @@ -12,16 +12,51 @@ import process from 'node:process'; /*****************************************************************************************************************/ -// orderly ships with zero runtime dependencies, and that is a guarantee rather than a habit. This guard makes the -// guarantee structural: the moment a dependencies key appears in the manifest, the package checks fail, in CI and -// locally alike. devDependencies remain allowed, as they are toolchain-only and never ship. +// orderly ships with no third party runtime dependencies, and that is a guarantee rather than a habit. This guard +// makes the guarantee structural: a dependency outside the one name allowed below fails the package checks, in +// CI and locally alike. devDependencies remain allowed, as they are toolchain-only and never ship. + +/*****************************************************************************************************************/ + +// The one runtime dependency orderly allows itself: observerly's own astrometry, which the sun schedules are +// built on. It carries no runtime dependencies of its own; the types-only package its manifest names ships no +// code. Nothing else may appear here without changing this line and the README sentence that promises it. +const ALLOWED = new Set(['@observerly/astrometry']); + +/*****************************************************************************************************************/ + const manifest: unknown = JSON.parse( readFileSync(new URL('../package.json', import.meta.url), 'utf8'), ); -if (typeof manifest === 'object' && manifest !== null && 'dependencies' in manifest) { +/*****************************************************************************************************************/ + +// The names under dependencies, or none when the key is absent. Anything the key holds that is not a plain +// object of names is refused too, rather than read as nothing. +const dependenciesOf = (candidate: unknown): string[] => { + if (typeof candidate !== 'object' || candidate === null || !('dependencies' in candidate)) { + return []; + } + + const { dependencies } = candidate; + + if (typeof dependencies !== 'object' || dependencies === null || Array.isArray(dependencies)) { + console.error('package.json declares dependencies that are not a map of names to versions.'); + + process.exit(1); + } + + return Object.keys(dependencies); +}; + +/*****************************************************************************************************************/ + +const strangers = dependenciesOf(manifest).filter(name => !ALLOWED.has(name)); + +if (strangers.length > 0) { console.error( - 'package.json declares a dependencies key: @observerly/orderly ships with zero runtime dependencies.', + `package.json declares runtime dependencies @observerly/orderly does not allow: ${strangers.join(', ')}. ` + + `The only runtime dependency permitted is ${[...ALLOWED].join(', ')}.`, ); process.exit(1);