refactor: remove rxjs dependency

Replace rxjs with a lightweight internal Subject implementation
to reduce package size and complexity.
This commit is contained in:
hypercross 2026-05-31 16:38:27 +08:00
parent 87c01858e7
commit 46da8abbe1
6 changed files with 55 additions and 22 deletions

17
package-lock.json generated
View File

@ -9,13 +9,9 @@
"version": "0.1.0", "version": "0.1.0",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"rxjs": "^7.8.1",
"tsup": "^8.3.5", "tsup": "^8.3.5",
"typescript": "^5.6.0", "typescript": "^5.6.0",
"vitest": "^4.1.7" "vitest": "^4.1.7"
},
"peerDependencies": {
"rxjs": "^7.0.0"
} }
}, },
"node_modules/@emnapi/core": { "node_modules/@emnapi/core": {
@ -2266,16 +2262,6 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/rxjs": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/siginfo": { "node_modules/siginfo": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
@ -2426,7 +2412,8 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true, "dev": true,
"license": "0BSD" "license": "0BSD",
"optional": true
}, },
"node_modules/tsup": { "node_modules/tsup": {
"version": "8.5.1", "version": "8.5.1",

View File

@ -22,11 +22,7 @@
"test": "vitest run", "test": "vitest run",
"prepublishOnly": "npm run build" "prepublishOnly": "npm run build"
}, },
"peerDependencies": {
"rxjs": "^7.0.0"
},
"devDependencies": { "devDependencies": {
"rxjs": "^7.8.1",
"tsup": "^8.3.5", "tsup": "^8.3.5",
"typescript": "^5.6.0", "typescript": "^5.6.0",
"vitest": "^4.1.7" "vitest": "^4.1.7"
@ -34,7 +30,6 @@
"keywords": [ "keywords": [
"ecs", "ecs",
"entity-component-system", "entity-component-system",
"rxjs",
"observable", "observable",
"game" "game"
], ],

View File

@ -17,3 +17,4 @@ export type {
RelationshipUpdate, RelationshipUpdate,
} from "./observable/events"; } from "./observable/events";
export type { WorldSnapshot } from "./serialization"; export type { WorldSnapshot } from "./serialization";
export type { Observable, Subscription } from "./observable/subject";

View File

@ -1,4 +1,4 @@
import { Subject } from "rxjs"; import { Subject } from "./subject";
import type { Query } from "../query"; import type { Query } from "../query";
import type { Entity } from "../entity"; import type { Entity } from "../entity";
import type { import type {

50
src/observable/subject.ts Normal file
View File

@ -0,0 +1,50 @@
// ── Internal Observable / Subject ─────────────────────
/** Minimal subscription handle returned by `.subscribe()`. */
export interface Subscription {
unsubscribe(): void;
}
/** Minimal observable interface — only supports single-callback subscribe. */
export interface Observable<T> {
subscribe(observer: (value: T) => void): Subscription;
}
/** Lightweight multicast subject, replacing the RxJS dependency. */
export class Subject<T> implements Observable<T> {
private _subs = new Set<(value: T) => void>();
private _done = false;
/** Push a value to all current subscribers. No-op after complete. */
next(value: T): void {
if (this._done) return;
for (const fn of this._subs) {
fn(value);
}
}
/** Register a subscriber. Returns a handle to unsubscribe. */
subscribe(observer: (value: T) => void): Subscription {
const fn = observer;
this._subs.add(fn);
return {
unsubscribe: () => {
this._subs.delete(fn);
},
};
}
/** Complete this subject — clears all subscribers and silences future calls. */
complete(): void {
this._done = true;
this._subs.clear();
}
/** Return a read-only Observable facade (hides `next` / `complete`). */
asObservable(): Observable<T> {
return {
subscribe: (observer: (value: T) => void): Subscription => {
return this.subscribe(observer);
},
};
}
}

View File

@ -6,7 +6,7 @@ import { SparseSet } from "./storage/sparse-set";
import { ObservableLayer } from "./observable/observe"; import { ObservableLayer } from "./observable/observe";
import type { QueryUpdate, RelationshipUpdate } from "./observable/events"; import type { QueryUpdate, RelationshipUpdate } from "./observable/events";
import type { RelationshipDef } from "./relationship"; import type { RelationshipDef } from "./relationship";
import { Observable } from "rxjs"; import type { Observable } from "./observable/subject";
import type { WorldEvent } from "./observable/events"; import type { WorldEvent } from "./observable/events";
import type { WorldSnapshot } from "./serialization"; import type { WorldSnapshot } from "./serialization";