{"_id":"@balpal4495/world-engine","name":"@balpal4495/world-engine","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@balpal4495/world-engine","version":"1.0.0","description":"Portable simulation runtime - world state, event ledger, chronicle, and narrative context","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./sqlite":{"import":{"types":"./dist/sqlite.d.ts","default":"./dist/sqlite.js"},"require":{"types":"./dist/sqlite.d.cts","default":"./dist/sqlite.cjs"}}},"sideEffects":false,"engines":{"node":">=18.0.0"},"publishConfig":{"access":"public"},"scripts":{"build":"tsup","build:watch":"tsup --watch","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","prepublishOnly":"npm run build && npm test"},"author":{"name":"balpal4495"},"license":"ISC","optionalDependencies":{"better-sqlite3":"^12.10.0"},"devDependencies":{"@balpal4495/quorum":"^3.9.0","@types/better-sqlite3":"^7.6.13","@types/node":"^22.0.0","tsup":"^8.5.1","tsx":"^4.22.4","typescript":"^5.5.0","vitest":"^2.0.0"},"_id":"@balpal4495/world-engine@1.0.0","gitHead":"b26ba3b43456fd6a01246a90b9a56d96ed678b8c","_nodeVersion":"22.22.0","_npmVersion":"10.9.4","dist":{"integrity":"sha512-krUhUXOT1bDYwg/H+OSdlJys0nMeVJV0BMfdUyeM74u3oW7Tpq09UFuXJZo5jktubVZENG0mQOz59uzLbLQmpA==","shasum":"2e600da3ecd33ec12bb6301f47a3d8b51547117a","tarball":"https://registry.npmjs.org/@balpal4495/world-engine/-/world-engine-1.0.0.tgz","fileCount":16,"unpackedSize":538016,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCJeYMH9UVW3AEC+UpYL6010iu8zw1u/ubwAr/nCDB1HAIgE0V72JAn+f6tV4wK0PiXzFfZh4Gutiege5i3BmWLC9c="}]},"_npmUser":{"name":"balpal4495","email":"baljit4495@gmail.com"},"directories":{},"maintainers":[{"name":"balpal4495","email":"baljit4495@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/world-engine_1.0.0_1780267460447_0.990854717407321"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-31T22:44:20.354Z","1.0.0":"2026-05-31T22:44:20.574Z","modified":"2026-05-31T22:44:21.108Z"},"maintainers":[{"name":"balpal4495","email":"baljit4495@gmail.com"}],"description":"Portable simulation runtime - world state, event ledger, chronicle, and narrative context","author":{"name":"balpal4495"},"license":"ISC","readme":"# World Engine\n\nPortable simulation runtime for building worlds with memory.\n\nWorld Engine tracks entities, relationships, and events — then derives significance, causal chains, themes, and historical reports from them. Games are the primary use case, but it works anywhere you need a world that remembers what happened.\n\n```\nnpm install @balpal4495/world-engine\n```\n\n---\n\n## The problem\n\nMost simulations forget.\n\n```\nEvent → Outcome → Forgotten\n```\n\nWorld Engine turns that into:\n\n```\nEvent → Consequence → Historical Significance → Legacy\n```\n\nEvery meaningful action becomes part of history.\n\n---\n\n## Quick start\n\n```ts\nimport { WorldEngine } from \"@balpal4495/world-engine\"\n\nconst world = new WorldEngine()\n\n// Create entities\nconst hero = world.createEntity({ type: \"hero\", name: \"Night Falcon\" })\nconst villain = world.createEntity({ type: \"villain\", name: \"Shadow Lord\" })\n\n// Create a relationship\nworld.createRelationship({ source: hero.id, target: villain.id, type: \"rivals\" })\n\n// Advance time\nworld.tick()\n\n// Record what happened\nconst event = world.recordEvent({\n  actor: hero.id,\n  action: \"save_city\",\n  target: villain.id,\n  outcome: \"Shadow Lord driven back\",\n})\n\nworld.tick()\n\n// Ask the chronicle what mattered\nconst chronicle = world.chronicle()\nconsole.log(chronicle.getSignificantEvents())\nconsole.log(chronicle.getThemes())\nconsole.log(chronicle.generateReport())\n```\n\n---\n\n## Core concepts\n\n### Entities\n\nEverything in a world is an entity — hero, villain, kingdom, city, army, player.\n\n```ts\nconst city = world.createEntity({\n  type: \"city\",\n  name: \"Ironhold\",\n  attributes: { population: 12000 },\n})\n\nworld.getEntity(city.id)\nworld.destroyEntity(city.id)\n```\n\n### Relationships\n\nEntities connect to each other. Connections create context for events.\n\n```ts\nworld.createRelationship({ source: hero.id, target: city.id, type: \"protects\" })\nworld.getRelationships(hero.id)\nworld.endRelationship(relationshipId)\n```\n\n### Events\n\nEvents are the source of truth. They are immutable once recorded.\n\n```ts\nworld.recordEvent({\n  actor: hero.id,\n  action: \"defend\",\n  target: city.id,\n  outcome: \"Attack repelled\",\n  causedBy: previousEventId,   // optional causal link\n  metadata: { damage: 40 },    // optional arbitrary data\n})\n```\n\n### Ticks\n\nTime moves forward one tick at a time. Every entity, event, and relationship is stamped with the tick it occurred on.\n\n```ts\nworld.tick()          // advance by one tick\nworld.currentTick     // read the current tick\n```\n\n---\n\n## Chronicle\n\nChronicle analyses the event ledger and answers: *what mattered, why, and what caused what?*\n\n```ts\nconst chronicle = world.chronicle()\n\n// Events ranked by significance\nchronicle.getSignificantEvents()\n\n// Full causal chain from any event\nchronicle.getEventChain(eventId)\n\n// Recurring themes across history\nchronicle.getThemes()\n\n// Complete historical report\nchronicle.generateReport()\n```\n\nNo AI required. All analysis is deterministic.\n\n---\n\n## Rules\n\nRules enforce world-specific constraints before any action is committed.\n\n```ts\nconst world = new WorldEngine()\n\nworld.addRule({\n  name: \"no-self-target\",\n  onEvent(proposal) {\n    if (proposal.target === proposal.actor) {\n      return { rule: \"no-self-target\", reason: \"An entity cannot target itself.\" }\n    }\n  },\n})\n\nworld.addRule({\n  name: \"no-hero-villain-alliance\",\n  onCreateRelationship(proposal, ctx) {\n    const source = ctx.getEntity(proposal.source)\n    const target = ctx.getEntity(proposal.target)\n    if (source?.type === \"hero\" && target?.type === \"villain\" && proposal.type === \"ally\") {\n      return { rule: \"no-hero-villain-alliance\", reason: \"Heroes cannot ally villains.\" }\n    }\n  },\n})\n```\n\nViolations throw a `RuleViolationError` with the full list of violations.\n\n```ts\nimport { RuleViolationError } from \"@balpal4495/world-engine\"\n\ntry {\n  world.recordEvent({ actor: hero.id, action: \"fly\", target: hero.id })\n} catch (e) {\n  if (e instanceof RuleViolationError) {\n    console.log(e.violations) // [{ rule, reason }, ...]\n  }\n}\n```\n\nRules are pure functions — they never modify state, only observe and block.\n\n---\n\n## Archive\n\nFor long-running worlds, World Engine automatically compresses old history into archived eras, keeping the live ledger lean.\n\n```ts\nconst world = new WorldEngine({\n  eraLength: 100,       // compress every 100 ticks (default)\n  liveWindowSize: 50,   // keep the last 50 ticks in full detail (default)\n})\n```\n\n### Retiring entities\n\nWhen a character's story is complete, retire them. Their history is compressed into a legacy; the raw events are removed from the live ledger.\n\n```ts\nconst legacy = world.retireEntity(hero.id)\n// legacy.entity, legacy.significantEvents, legacy.dominantActions, legacy.summary\n\nworld.chronicle().getEntityLegacy(hero.id)\nworld.chronicle().getEntityLegacies()\nworld.chronicle().getArchivedEras()\n```\n\n---\n\n## Narrative\n\nNarrative turns history into prose. AI is optional — there is always a deterministic template fallback.\n\n```ts\nconst narrative = world.narrative()\n\n// Template-based (no AI)\nawait narrative.generateReport(chronicle.generateReport())\nawait narrative.describeEvent(event)\nawait narrative.describeChain(chain)\nawait narrative.describeThemes(themes)\n\n// AI-enriched\nconst narrative = world.narrative({\n  llm: async (messages) => {\n    // call your LLM provider here\n    return await myLLM(messages)\n  },\n})\n```\n\nThe simulation is fully functional if the LLM disappears. Only prose quality is affected.\n\n---\n\n## Persistence\n\nSave and load worlds with the built-in SQLite adapter (Node.js / Electron).\n\n```ts\nimport { WorldEngine } from \"@balpal4495/world-engine\"\nimport { SqliteAdapter } from \"@balpal4495/world-engine/sqlite\"\n\nconst adapter = new SqliteAdapter(\"./saves/game.db\")\n// or: new SqliteAdapter(\":memory:\") for tests\n\n// Save\nadapter.save(\"slot-1\", world.save())\n\n// Load\nconst snap = adapter.load(\"slot-1\")\nif (snap) world.load(snap)\n\n// List saves\nadapter.list() // [{ id, tick, savedAt }, ...]\n\n// Delete\nadapter.delete(\"slot-1\")\n\nadapter.close()\n```\n\nThe `PersistenceAdapter` interface is platform-agnostic — implement it for IndexedDB, AsyncStorage, or any other backend.\n\n```ts\nimport type { PersistenceAdapter } from \"@balpal4495/world-engine\"\n\nclass MyAdapter implements PersistenceAdapter {\n  save(id, snapshot) { /* ... */ }\n  load(id) { /* ... */ }\n  list() { /* ... */ }\n  delete(id) { /* ... */ }\n}\n```\n\n---\n\n## Save / load\n\nSnapshots are plain JSON — safe to serialise, store, and transfer.\n\n```ts\nconst snapshot = world.save()\n// { version: 1, tick, entities, relationships, events, archivedEras, entityLegacies }\n\nconst json = JSON.stringify(snapshot)\n\nconst world2 = new WorldEngine()\nworld2.load(JSON.parse(json))\n```\n\n---\n\n## Design rules\n\n- **No platform code in core.** Runs identically in browser, Node, Electron, React Native.\n- **No AI required.** Significance, themes, causal chains — all deterministic.\n- **AI never changes history.** The Narrative layer only describes what the simulation recorded.\n- **Events are immutable.** Nothing in the engine modifies a recorded event.\n- **Rules are pure.** They observe state, never change it.\n\n---\n\n## Development\n\n```bash\nnpm test          # run all tests (vitest)\nnpm run typecheck # TypeScript check\nnpm run build     # build ESM + CJS + types to dist/\n```\n\n---\n\n## Platform support\n\n| Platform        | Core engine | SQLite adapter |\n|-----------------|-------------|----------------|\n| Node.js         | ✓           | ✓              |\n| Electron        | ✓           | ✓              |\n| Browser         | ✓           | —              |\n| React Native    | ✓           | —              |\n| Bun / Deno      | ✓           | ✓ (via compat) |\n","readmeFilename":"README.md","_rev":"1-e99490c1c8bbee15f21936e10b3bc287"}