{"_id":"@async/json","_rev":"5-5b5cf13fc35ba0f901eb09078176692e","name":"@async/json","dist-tags":{"latest":"0.5.0"},"versions":{"0.2.0":{"name":"@async/json","version":"0.2.0","license":"MIT","_id":"@async/json@0.2.0","maintainers":[{"name":"async-npm","email":"async.npm@gdi2290.com"},{"name":"patrickjs","email":"npm.patrickjs@gdi2290.com"}],"homepage":"https://async.github.io/json/","bugs":{"url":"https://github.com/async/json/issues"},"dist":{"shasum":"648cf12600209ab6277de889df97a1d012dccabb","tarball":"https://registry.npmjs.org/@async/json/-/json-0.2.0.tgz","fileCount":34,"integrity":"sha512-tQ9BiWJm4gjomIRToy0XzXf0C6b77GOrI4AF2B3waYdUbSVFUUoC2KMZIz0Ns1vchHFSM4XT11a9MAp5aJuisg==","signatures":[{"sig":"MEQCICWDGLbeTBx+Xd5LVw6eV+ri2q1NmkqX+wVQQDApmXeDAiBojYhBneePCxgUiPMl6OqRVtgtWlNCXbP4JflYAOJaXA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@async%2fjson@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":189009},"type":"module","engines":{"node":">=24"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./redis":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"}},"gitHead":"a1eb8532225982032721fb1cca863f93ecc92465","scripts":{"test":"pnpm run build && node --test tests/*.test.js","build":"tsc -p tsconfig.json","typecheck":"tsc --noEmit -p tsconfig.json","pack:check":"npm --cache .async/npm-cache pack --dry-run","release:check":"pnpm run build && pnpm run test && pnpm run api-surface:check && pnpm run pack:check","pipeline:pages":"async-pipeline run pages","pipeline:verify":"async-pipeline run verify","pipeline:preview":"async-pipeline run preview","pipeline:publish":"async-pipeline run publish","api-surface:check":"api-contract check --manifest api-contract.json && api-contract ledger --manifest api-contract.json --check API_SURFACE.md","pipeline:snapshot":"async-pipeline run snapshot","pipeline:task:pack":"async-pipeline run-task pack","pipeline:task:test":"async-pipeline run-task test","pipeline:sync:check":"async-pipeline sync check","pipeline:task:build":"async-pipeline run-task build","api-surface:generate":"api-contract ledger --manifest api-contract.json --out API_SURFACE.md","pipeline:publish:npm":"async-pipeline publish npm --package .","pipeline:github:check":"async-pipeline github check","pipeline:sync:generate":"async-pipeline sync generate","pipeline:publish-github":"async-pipeline run publish-github","pipeline:release-doctor":"async-pipeline run release-doctor","pipeline:release:doctor":"async-pipeline release doctor --package .","pipeline:release:ensure":"async-pipeline release ensure --package .","pipeline:task:docs.site":"async-pipeline run-task docs.site","pipeline:github:generate":"async-pipeline github generate","pipeline:task:api-surface":"async-pipeline run-task api-surface","pipeline:publish:github:pr":"async-pipeline publish github pr --package .","pipeline:publish:github:main":"async-pipeline publish github main --package .","pipeline:publish:github:release":"async-pipeline publish github release --package ."},"_npmUser":{"name":"patrickjs","email":"npm.patrickjs@gdi2290.com"},"repository":{"url":"git+https://github.com/async/json.git","type":"git"},"_npmVersion":"11.16.0","description":"Use a JSON file or folder like a small database, with sidecar state, queries, history, and Redis-backed scale paths.","directories":{},"sideEffects":false,"_nodeVersion":"24.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.20.0","devDependencies":{"typescript":"6.0.3","@types/node":"24.12.4","@async/pipeline":"0.5.0","@async/api-contract":"0.1.1"},"_npmOperationalInternal":{"tmp":"tmp/json_0.2.0_1781508135437_0.8647460987129285","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@async/json","version":"0.3.0","license":"MIT","_id":"@async/json@0.3.0","maintainers":[{"name":"async-npm","email":"async.npm@gdi2290.com"},{"name":"patrickjs","email":"npm.patrickjs@gdi2290.com"}],"homepage":"https://async.github.io/json/","bugs":{"url":"https://github.com/async/json/issues"},"dist":{"shasum":"e966fd516653f74c369f4872f694158088bc0efd","tarball":"https://registry.npmjs.org/@async/json/-/json-0.3.0.tgz","fileCount":42,"integrity":"sha512-kiYLYqL2T3E59FVIoqCNWSd14ahmjr3H2WsdhWLFrrkF4DixPa8AbLDKLHn44T87PE/oacDdyW1Yk+e/9Pq7iw==","signatures":[{"sig":"MEUCIQChfCvZjVIiFgBUPnkX4E/XpW/wOIelNuyHTMTdtw23YQIgMwPSEd7T2OlI18HdlUuWloBQuWTWoXIQBXD3j6rcAis=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@async%2fjson@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":285463},"type":"module","engines":{"node":">=24"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./redis":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"}},"gitHead":"da03579fbed771a1cb7861964067d57a264ac2d5","scripts":{"test":"pnpm run build && node --test tests/*.test.js","build":"tsc -p tsconfig.json","typecheck":"tsc --noEmit -p tsconfig.json","pack:check":"npm --cache .async/npm-cache pack --dry-run","vendor:json5":"node ./scripts/vendor-json5-parser.js","release:check":"pnpm run vendor:json5:check && pnpm run build && pnpm run test && pnpm run api-surface:check && pnpm run pack:check","pipeline:pages":"async-pipeline run pages","pipeline:verify":"async-pipeline run verify","pipeline:preview":"async-pipeline run preview","pipeline:publish":"async-pipeline run publish","api-surface:check":"api-contract check --manifest api-contract.json && api-contract ledger --manifest api-contract.json --check API_SURFACE.md","bench:stable-json":"pnpm run build && node ./scripts/bench-stable-json.js","pipeline:snapshot":"async-pipeline run snapshot","pipeline:task:pack":"async-pipeline run-task pack","pipeline:task:test":"async-pipeline run-task test","vendor:json5:check":"node ./scripts/vendor-json5-parser.js --check","pipeline:sync:check":"async-pipeline sync check","pipeline:task:build":"async-pipeline run-task build","api-surface:generate":"api-contract ledger --manifest api-contract.json --out API_SURFACE.md","pipeline:publish:npm":"async-pipeline publish npm --package .","pipeline:github:check":"async-pipeline github check","pipeline:sync:generate":"async-pipeline sync generate","pipeline:publish-github":"async-pipeline run publish-github","pipeline:release-doctor":"async-pipeline run release-doctor","pipeline:release:doctor":"async-pipeline release doctor --package .","pipeline:release:ensure":"async-pipeline release ensure --package .","pipeline:task:docs.site":"async-pipeline run-task docs.site","pipeline:github:generate":"async-pipeline github generate","pipeline:task:api-surface":"async-pipeline run-task api-surface","pipeline:publish:github:pr":"async-pipeline publish github pr --package .","pipeline:publish:github:main":"async-pipeline publish github main --package .","pipeline:publish:github:release":"async-pipeline publish github release --package ."},"_npmUser":{"name":"patrickjs","email":"npm.patrickjs@gdi2290.com"},"repository":{"url":"git+https://github.com/async/json.git","type":"git"},"_npmVersion":"11.16.0","description":"Use a JSON file or folder like a small database, with sidecar state, queries, history, and Redis-backed scale paths.","directories":{},"sideEffects":false,"_nodeVersion":"24.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.20.0","devDependencies":{"json5":"2.2.3","typescript":"6.0.3","@types/node":"24.12.4","@async/pipeline":"0.5.0","@async/api-contract":"0.1.1"},"_npmOperationalInternal":{"tmp":"tmp/json_0.3.0_1781510027216_0.5902792942186723","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@async/json","version":"0.4.0","license":"MIT","_id":"@async/json@0.4.0","maintainers":[{"name":"async-npm","email":"async.npm@gdi2290.com"},{"name":"patrickjs","email":"npm.patrickjs@gdi2290.com"}],"homepage":"https://async.github.io/json/","bugs":{"url":"https://github.com/async/json/issues"},"dist":{"shasum":"7161f313421d79cfd48969e99f09528817de4959","tarball":"https://registry.npmjs.org/@async/json/-/json-0.4.0.tgz","fileCount":46,"integrity":"sha512-gDC5mgusfR1GFZbpzOOHeFqlymB7IM+jRIBfkP9O87azKaMKhNZZ5b/gi64oBRm+tAkFcK2AgllLCn9C3xR9Vw==","signatures":[{"sig":"MEYCIQDjC92JaMpjw10ZPtMdHUg/fpjkFCcnkYX+bOu3G9Uw5gIhAIQK+hZuvxFu/+qBskdQi24nkb2V/jYUzKTpZGhNSF0L","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@async%2fjson@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":303973},"type":"module","engines":{"node":">=24"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./redis":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"}},"gitHead":"eaca1243799f8ab53ce8b1174a37e8fe5d011eba","scripts":{"test":"pnpm run build && node --test tests/*.test.js","build":"tsc -p tsconfig.json","typecheck":"tsc --noEmit -p tsconfig.json","pack:check":"npm --cache .async/npm-cache pack --dry-run","vendor:json5":"node ./scripts/vendor-json5-parser.js","release:check":"pnpm run vendor:json5:check && pnpm run build && pnpm run test && pnpm run api-surface:check && pnpm run pack:check","pipeline:pages":"async-pipeline run pages","pipeline:verify":"async-pipeline run verify","pipeline:preview":"async-pipeline run preview","pipeline:publish":"async-pipeline run publish","api-surface:check":"api-contract check --manifest api-contract.json && api-contract ledger --manifest api-contract.json --check API_SURFACE.md","bench:stable-json":"pnpm run build && node ./scripts/bench-stable-json.js","pipeline:snapshot":"async-pipeline run snapshot","pipeline:task:pack":"async-pipeline run-task pack","pipeline:task:test":"async-pipeline run-task test","vendor:json5:check":"node ./scripts/vendor-json5-parser.js --check","pipeline:sync:check":"async-pipeline sync check","pipeline:task:build":"async-pipeline run-task build","api-surface:generate":"api-contract ledger --manifest api-contract.json --out API_SURFACE.md","pipeline:publish:npm":"async-pipeline publish npm --package .","pipeline:github:check":"async-pipeline github check","pipeline:sync:generate":"async-pipeline sync generate","pipeline:publish-github":"async-pipeline run publish-github","pipeline:release-doctor":"async-pipeline run release-doctor","pipeline:release:doctor":"async-pipeline release doctor --package .","pipeline:release:ensure":"async-pipeline release ensure --package .","pipeline:task:docs.site":"async-pipeline run-task docs.site","pipeline:github:generate":"async-pipeline github generate","pipeline:task:api-surface":"async-pipeline run-task api-surface","pipeline:publish:github:pr":"async-pipeline publish github pr --package .","pipeline:publish:github:main":"async-pipeline publish github main --package .","pipeline:publish:github:release":"async-pipeline publish github release --package ."},"_npmUser":{"name":"patrickjs","email":"npm.patrickjs@gdi2290.com"},"repository":{"url":"git+https://github.com/async/json.git","type":"git"},"_npmVersion":"11.16.0","description":"Use a JSON file or folder like a small database, with sidecar state, queries, history, and Redis-backed scale paths.","directories":{},"sideEffects":false,"_nodeVersion":"24.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.20.0","devDependencies":{"json5":"2.2.3","typescript":"6.0.3","@types/node":"24.12.4","@async/pipeline":"0.5.0","@async/api-contract":"0.1.1"},"_npmOperationalInternal":{"tmp":"tmp/json_0.4.0_1781511710053_0.45804772472929733","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@async/json","version":"0.5.0","description":"Use a JSON file or folder like a small database, with sidecar state, queries, history, and Redis-backed scale paths.","license":"MIT","type":"module","packageManager":"pnpm@10.20.0","engines":{"node":">=24"},"repository":{"type":"git","url":"git+https://github.com/async/json.git"},"bugs":{"url":"https://github.com/async/json/issues"},"homepage":"https://async.github.io/json/","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./redis":{"types":"./dist/redis.d.ts","default":"./dist/redis.js"}},"scripts":{"api-surface:check":"api-contract check --manifest api-contract.json && api-contract ledger --manifest api-contract.json --check API_SURFACE.md","api-surface:generate":"api-contract ledger --manifest api-contract.json --out API_SURFACE.md","bench:stable-json":"pnpm run build && node ./scripts/bench-stable-json.js","build":"tsc -p tsconfig.json","pack:check":"npm --cache .async/npm-cache pack --dry-run","pipeline:github:check":"async-pipeline github check","pipeline:github:generate":"async-pipeline github generate","pipeline:pages":"async-pipeline run-task docs.site","pipeline:publish":"async-pipeline run publish","pipeline:publish-github":"async-pipeline run publish-github","pipeline:publish:github:main":"async-pipeline publish github main --package .","pipeline:publish:github:pr":"async-pipeline publish github pr --package .","pipeline:publish:github:release":"async-pipeline publish github release --package .","pipeline:publish:npm":"async-pipeline publish npm --package .","pipeline:release-doctor":"async-pipeline run release-doctor","pipeline:release:doctor":"async-pipeline release doctor --package .","pipeline:release:ensure":"async-pipeline release ensure --package .","pipeline:snapshot":"async-pipeline run snapshot","pipeline:sync:check":"async-pipeline sync check","pipeline:sync:generate":"async-pipeline sync generate","pipeline:task:api-surface":"async-pipeline run-task api-surface","pipeline:task:build":"async-pipeline run-task build","pipeline:task:docs.site":"async-pipeline run-task docs.site","pipeline:task:pack":"async-pipeline run-task pack","pipeline:task:test":"async-pipeline run-task test","pipeline:verify":"async-pipeline run verify","release:check":"pnpm run vendor:json5:check && pnpm run build && pnpm run test && pnpm run api-surface:check && pnpm run pack:check","test":"pnpm run build && node --test tests/*.test.js","typecheck":"tsc --noEmit -p tsconfig.json","vendor:json5":"node ./scripts/vendor-json5-parser.js","vendor:json5:check":"node ./scripts/vendor-json5-parser.js --check"},"sideEffects":false,"publishConfig":{"access":"public"},"devDependencies":{"@async/api-contract":"0.1.1","@async/pipeline":"0.9.35","@types/node":"24.12.4","json5":"2.2.3","typescript":"6.0.3"},"gitHead":"323d143a3dd2577cffbb36542b94e4b692315c66","_id":"@async/json@0.5.0","_nodeVersion":"24.18.0","_npmVersion":"11.16.0","dist":{"integrity":"sha512-Haj0MKl7P/EX/8K+TpUWTZqLNMcrjW5uorqLcWyx8tRKAcuFKBCP/QZGunX7EiAkO67ZkdQ11kPnPSjGcByx2Q==","shasum":"0038d72bf25fe640648b6e9d880b8cf3d7c6eb24","tarball":"https://registry.npmjs.org/@async/json/-/json-0.5.0.tgz","fileCount":46,"unpackedSize":310940,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@async%2fjson@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHMeA4Omxu6JFhkqV1CLygoSGAxZklx+8rLyTU7H+6eNAiBsGMzhYahN2rMj3jPp447BflkNudYwL2kK3S99fNWYyw=="}]},"_npmUser":{"name":"patrickjs","email":"npm.patrickjs@gdi2290.com"},"directories":{},"maintainers":[{"name":"jackshelton","email":"me@jackshelton.com"},{"name":"async-npm","email":"async.npm@gdi2290.com"},{"name":"patrickjs","email":"npm.patrickjs@gdi2290.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/json_0.5.0_1783541454820_0.9255529021555575"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-15T07:22:15.260Z","modified":"2026-07-08T20:10:55.348Z","0.2.0":"2026-06-15T07:22:15.574Z","0.3.0":"2026-06-15T07:53:47.379Z","0.4.0":"2026-06-15T08:21:50.210Z","0.5.0":"2026-07-08T20:10:54.982Z"},"bugs":{"url":"https://github.com/async/json/issues"},"license":"MIT","homepage":"https://async.github.io/json/","repository":{"type":"git","url":"git+https://github.com/async/json.git"},"description":"Use a JSON file or folder like a small database, with sidecar state, queries, history, and Redis-backed scale paths.","maintainers":[{"name":"jackshelton","email":"me@jackshelton.com"},{"name":"async-npm","email":"async.npm@gdi2290.com"},{"name":"patrickjs","email":"npm.patrickjs@gdi2290.com"}],"readme":"# @async/json\n\nUse a JSON file or a folder of JSON files like a small database.\n\n```bash\npnpm add @async/json\n```\n\nThe package is ESM-only and supports Node.js 24 and newer.\n\n```js\nimport json from '@async/json';\n\nconst users = await json('./users.json');\nawait users.create({ name: 'Ada Lovelace', role: 'admin' });\nawait users.find({ where: { role: 'admin' } });\n\nconst db = await json('./db');\nawait db.users.find({ where: { role: 'admin' } });\nawait db.settings.get();\nawait db.collection('users').find({ where: { role: 'admin' } });\nawait db.document('settings').get();\n```\n\nBy default the visible JSON file is seed data and writes go to sidecar state\nunder `.async-json/state`. Use `writes: 'source'` only when the JSON file itself\nshould be rewritten.\n\nFolder database handles expose resources as properties. Callable controls stay\ncallable, so resources named `collection`, `document`, `resourceNames`, or\n`close` can still use property access while the control call keeps working:\n\n```js\nawait db.collection('users').all();\nawait db.collection.find(); // resource named \"collection\"\nawait db.resourceNames();\nawait db.resourceNames.find(); // resource named \"resourceNames\"\nawait db._.collection('_').all(); // explicit escape hatch\n```\n\n`@async/json` owns standalone JSON database semantics: collection/document\nruntime APIs, scalar and compound identity, append-only collections, encoded\npayload validation, sidecar state, local indexes, RedisJSON storage, stable\nstringify, and JSON5-compatible parsing. Use `@async/db` when you also need\nreaders, schemas, generated types, REST/GraphQL, operations, lifecycle, and\nstore graduation.\n\n## Nested Folders And Runtime-Created Resources\n\nFolder databases stay flat by default. Two opt-in options widen them:\n\n```js\nconst db = await json('./data', { recursive: true, create: true });\n\n// data/messages/general.json → resource \"messages/general\"\nawait db.document('messages/general').get();\n\n// unknown names become sidecar-backed resources on first use\nawait db.document('messages/new-channel').set('', [{ id: 'm1', text: 'hi' }]);\n```\n\n`recursive: true` discovers nested folders as path-named resources\n(`messages/general`). Dot-prefixed entries, including the sidecar state\nfolder, are always skipped.\n\n`create: true` lets `collection()`/`document()` grow resources for names that\ndon't exist yet. No visible seed file is created — state lives in the sidecar\nand survives reopens — so a folder of seed JSON can gain runtime data without\nthe source tree changing. Resource names are validated: no empty, dot-prefixed,\n`.`/`..`, or backslash segments.\n\n## Stable JSON Helpers\n\n`stableStringify()` produces deterministic JSON by sorting object keys\nlexically. Array order is preserved.\n\n```js\nimport { parseJson, registerJson, stableJson, stableStringify } from '@async/json';\n\nstableStringify({ b: 1, a: 2 });\n// {\"a\":2,\"b\":1}\n\nstableJson.stringify({ b: 1, a: 2 }, true);\n// {\n//   \"a\": 2,\n//   \"b\": 1\n// }\n```\n\nPass `pretty: true` for two-space output, or use `space` with the same number\nclamping and string truncation rules as `JSON.stringify()`:\n\n```js\nstableStringify({ b: 1, a: 2 }, { pretty: true });\nstableStringify({ b: 1, a: 2 }, { space: '\\t' });\n```\n\nSorting defaults to `true`. Disable it to preserve insertion order, or provide\na custom comparator:\n\n```js\nstableStringify({ b: 1, a: 2 }, { sort: false });\n\nstableStringify({ id: 1, metadata: {}, name: 'Ada' }, {\n  sort: (left, right) => left.length - right.length || left.localeCompare(right),\n});\n```\n\nThe second argument can be a replacer function. Replacers receive path and\ncycle context, so recursive structures can be rendered as JSON references:\n\n```js\nconst root = { id: 'root' };\nroot.self = root;\n\nstableStringify(root, (key, value, context) =>\n  context.circular\n    ? { $ref: `#/${context.refPath?.join('/') ?? ''}` }\n    : value,\n);\n```\n\n`parseJson()` accepts JSON5-compatible input, including comments, trailing\ncommas, unquoted keys, and single-quoted strings. `$ref` objects are not\nresolved automatically; revivers can resolve them in one place:\n\n```js\nconst graph = parseJson(`{\n  users: { u_1: { name: 'Ada' } },\n  owner: { $ref: '#/users/u_1' },\n}`, (key, value, context) =>\n  context.isRef ? context.resolvePointer(context.ref ?? '#') : value,\n);\n```\n\n`registerJson()` installs an opt-in `JSON` shim on `globalThis` or a provided\ntarget. The shim keeps native `JSON.stringify(value, replacer, space)` and\n`JSON.parse(text, reviver)` argument shapes while using `@async/json` parsing\nand stable stringify behavior. The returned function restores the previous\n`JSON` object.\n\n```js\nconst restore = registerJson();\ntry {\n  JSON.stringify({ b: 1, a: 2 });\n  JSON.parse('{a: 1}');\n} finally {\n  restore();\n}\n```\n\n## File Helpers\n\nUse `file.patch()` to update JSON files like `package.json` without sorting or\notherwise reordering existing object keys. It preserves the file's indentation\nand trailing newline, writes atomically, and returns `false` when the patch does\nnot change the file text.\n\n```js\nimport { file } from '@async/json';\n\nawait file.patch('./package.json', {\n  scripts: {\n    test: 'node --test',\n  },\n  devDependencies: {\n    typescript: '^6.0.0',\n  },\n});\n```\n\nObject patches deep-merge plain objects, replace arrays and scalar values, and\nappend new keys after existing keys. Callback patches can mutate the parsed\nobject directly or return a replacement value:\n\n```js\nawait file.patch('./package.json', (pkg) => {\n  pkg.scripts ??= {};\n  pkg.scripts.lint = 'eslint .';\n});\n```\n\n## Identity, Logs, And Encoded Payloads\n\nSingle-field resources keep using `id` by default:\n\n```js\nconst users = await json('./users.json');\nawait users.get('u_1');\n```\n\nCompound identity uses object keys instead of delimiter-encoded ids:\n\n```js\nconst memberships = await json('./memberships.json', {\n  identity: { fields: ['orgId', 'userId'] },\n  indexes: ['role'],\n});\n\nawait memberships.get({ orgId: 'o_1', userId: 'u_1' });\nawait memberships.patch({ orgId: 'o_1', userId: 'u_1' }, { role: 'admin' });\n```\n\nAppend-only collections allow `append()` and block create/update/delete/replace\nAPIs:\n\n```js\nconst events = await json('./events.json', {\n  writePolicy: 'append-only',\n});\n\nawait events.append({ id: 'e_1', type: 'created' });\n```\n\nBytes fields validate JSON-safe encoded strings while keeping payloads opaque:\n\n```js\nconst updates = await json('./updates.json', {\n  fields: {\n    id: { type: 'string', required: true },\n    update: { type: 'bytes', encoding: 'base64url', required: true },\n  },\n});\n\nawait updates.append({ id: 'u_1', update: 'YWJjZA' });\n```\n\n## Redis JSON\n\n```js\nimport json from '@async/json';\nimport { redisJson } from '@async/json/redis';\n\nconst db = await json('./db', {\n  store: redisJson({ client, prefix: 'app:' }),\n  indexes: {\n    users: [{ fields: ['email'] }],\n  },\n});\n```\n\nRedis mode stores collection records as per-record Redis JSON keys. Declared\nindexes can be mapped to Redis Search; indexes are not created implicitly from\nobserved queries.\n\n## Local Development\n\n```bash\npnpm install\npnpm run build\npnpm run test\npnpm run release:check\n```\n\n`release:check` builds the package, runs tests, verifies the API surface\nledger, and runs a package dry-run.\n\nGenerated files:\n\n- `dist/` is build output and is not committed.\n- `.async/pages/` is generated GitHub Pages output and is not committed.\n- `.async/`, `.async-json/`, `node_modules/`, and `*.tgz` are local runtime,\n  cache, dependency, and package output.\n- `.github/workflows/async-pipeline.yml` is generated from `pipeline.ts`.\n\n## Website\n\nThe project site is published through GitHub Pages at\nhttps://async.github.io/json/.\n\n## Release Status\n\n`@async/json` publishes as a public npm package. Public API changes should\nupdate `api-contract.json`, regenerate `API_SURFACE.md`, update this README,\nand add tests in the same change.\n","readmeFilename":"README.md"}