{"_id":"@_all_docs/view","_rev":"2-28ced9bf83ab1466931afbdc84c4fe7c","name":"@_all_docs/view","dist-tags":{"latest":"0.5.1"},"versions":{"1.0.0":{"name":"@_all_docs/view","version":"1.0.0","_id":"@_all_docs/view@1.0.0","maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"}],"dist":{"shasum":"d327dbc7fccf6cfb1ead78a852972dd328c71d1a","tarball":"https://registry.npmjs.org/@_all_docs/view/-/view-1.0.0.tgz","fileCount":12,"integrity":"sha512-ehEvnLrTdQ6omJWddZVeE+dIDTS+Z7KNeU0a4xxpujhLkVUloq+eUMZm+AQpiflq5PcgAdanK/Oh2DALAv13Qw==","signatures":[{"sig":"MEQCIFocFtlZxhGq9q/r6DvoO61GoufJFaO4wGqOVPqOdK9KAiBPhHd6nLQe1BMke+bk4TNTa6yYI/L2HhOi5OUT70q+zQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":50021},"main":"index.js","type":"module","_from":"file:_all_docs-view-1.0.0.tgz","exports":{".":"./index.js"},"scripts":{"test":"node --test *.test.js"},"_npmUser":{"name":"indexzero","email":"charlie.robbins@gmail.com"},"_resolved":"/tmp/e22e353dbf564f5c1b71b1fc3b39d27f/_all_docs-view-1.0.0.tgz","_integrity":"sha512-ehEvnLrTdQ6omJWddZVeE+dIDTS+Z7KNeU0a4xxpujhLkVUloq+eUMZm+AQpiflq5PcgAdanK/Oh2DALAv13Qw==","_npmVersion":"11.6.2","description":"View abstraction for _all_docs cache queries","directories":{},"_nodeVersion":"24.13.0","dependencies":{"@_all_docs/cache":"0.1.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/view_1.0.0_1770002810457_0.06885848913543291","host":"s3://npm-registry-packages-npm-production"}},"0.5.1":{"name":"@_all_docs/view","version":"0.5.1","description":"View abstraction for _all_docs cache queries","type":"module","repository":{"type":"git","url":"git+https://github.com/indexzero/_all_docs.git","directory":"src/view"},"author":{"name":"Charlie Robbins","email":"npm@charlie.dev"},"license":"Apache-2.0","main":"index.js","exports":{".":"./index.js"},"dependencies":{"@_all_docs/cache":"0.5.1"},"scripts":{"test":"node --test *.test.js"},"_id":"@_all_docs/view@0.5.1","bugs":{"url":"https://github.com/indexzero/_all_docs/issues"},"homepage":"https://github.com/indexzero/_all_docs#readme","_integrity":"sha512-/0WkDxKpaX+6+DuxMChnyihMiSHqPzje7WaYSPSrXkC3DTACbLyEWy0+t4R2hcbSEpcLWzOcGXhNB2q+PIK5YQ==","_resolved":"/tmp/78c89f3a91acc579329d2b6fcda035c1/_all_docs-view-0.5.1.tgz","_from":"file:_all_docs-view-0.5.1.tgz","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-/0WkDxKpaX+6+DuxMChnyihMiSHqPzje7WaYSPSrXkC3DTACbLyEWy0+t4R2hcbSEpcLWzOcGXhNB2q+PIK5YQ==","shasum":"d60f803c3b149c8f3de37f3c0f94330de22a5bde","tarball":"https://registry.npmjs.org/@_all_docs/view/-/view-0.5.1.tgz","fileCount":12,"unpackedSize":54303,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@_all_docs%2fview@0.5.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCGAPUGx/kV4KdKXVoBdgbbuQA+OgO37MhpnqWuE6t/EAIgIUaDZ8BN8LznuT+RFW1nKx4qcwS2nt3NOQ50yZKbjKg="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:05dff91c-b2cc-49c7-9b67-462f0ac9b80b"}},"directories":{},"maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/view_0.5.1_1770010119282_0.12402166337528486"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-02T03:26:50.352Z","modified":"2026-02-02T05:28:39.725Z","1.0.0":"2026-02-02T03:26:50.593Z","0.5.1":"2026-02-02T05:28:39.428Z"},"description":"View abstraction for _all_docs cache queries","maintainers":[{"name":"indexzero","email":"charlie.robbins@gmail.com"}],"readme":"# @_all_docs/view\n\nViews provide a way to query and join cached registry data using origin expressions.\n\n## What is a View?\n\nA **view** is:\n1. A **predicate** (origin filter) that selects which cache entries to scan\n2. A **projection** (field selection) that transforms each record\n\nViews are stored as JSON files and can be queried or joined with other views.\n\n## Why Views?\n\nThe `_all_docs` cache stores packuments keyed by `v1:{type}:{origin}:{hex-name}`. When you want to find packages with `preinstall` scripts that don't declare `node-gyp` as a dependency (a potential supply chain red flag), you need to:\n\n1. Scan all npm entries for packages with install scripts\n2. For each, check if it depends on node-gyp\n3. Output packages that have scripts but no node-gyp\n\nWithout views, this requires knowing the internal cache key structure. Views abstract this away:\n\n```bash\n# Define packages with install scripts\n_all_docs view define has-scripts --origin npm \\\n  --select 'name, scripts.preinstall, scripts.postinstall'\n\n# Define packages that use node-gyp\n_all_docs view define uses-gyp --origin npm \\\n  --select 'name, dependencies, devDependencies'\n\n# Find packages with install scripts but NO node-gyp dependency\n_all_docs view join has-scripts uses-gyp --diff | \\\n  jq 'select(.left.scripts.preinstall != null or .left.scripts.postinstall != null)'\n```\n\nSimilarly, views make it easy to extract dependency graph edges for services like [deps.dev](https://deps.dev):\n\n```bash\n# Define a view that extracts dependency edges\n_all_docs view define npm-edges --origin npm \\\n  --select 'name, dependencies|keys as deps, devDependencies|keys as devDeps'\n\n# Stream edges in a format suitable for graph databases\n_all_docs view query npm-edges | jq -c '\n  .name as $src |\n  ((.deps // [])[] | {from: $src, to: ., type: \"runtime\"}),\n  ((.devDeps // [])[] | {from: $src, to: ., type: \"dev\"})\n'\n```\n\n## View Definition\n\nA view has these properties:\n\n| Property | Description |\n|----------|-------------|\n| `name` | Unique identifier for the view |\n| `origin` | Encoded origin key (e.g., `npm`, `paces.exale.com~javpt`) |\n| `registry` | Original registry URL (optional, for display) |\n| `type` | Entity type: `packument` (default) or `partition` |\n| `select` | Field projection expression |\n\n## Select Expression Syntax\n\nThe select expression defines which fields to include and how to transform them.\n\n### Simple Fields\n```\nname, description, license\n```\n\n### Nested Fields\n```\ntime.modified, repository.url\n```\n\n### Transforms\n```\nversions|keys                    # Get object keys as array\nversions|keys|length             # Count of versions\ndependencies|keys|sort           # Sorted dependency names\n```\n\n### Aliases\n```\nversions|keys as version_list    # Rename the output field\ntime.modified as modified        # Simplify nested field name\n```\n\n### Available Transforms\n\n| Transform | Description |\n|-----------|-------------|\n| `keys` | Object keys as array |\n| `values` | Object values as array |\n| `length` | Array or string length |\n| `first` | First element |\n| `last` | Last element |\n| `sort` | Sort array |\n| `reverse` | Reverse array |\n| `unique` | Deduplicate array |\n| `compact` | Remove null/undefined |\n| `flatten` | Flatten nested arrays |\n| `entries` | Object entries as [key, value] pairs |\n| `sum` | Sum numeric array |\n| `min` | Minimum value |\n| `max` | Maximum value |\n\n## CLI Commands\n\n### Define a View\n```bash\n_all_docs view define <name> --origin <key> [--select <expr>]\n_all_docs view define <name> --registry <url> [--select <expr>]\n\n# Examples\n_all_docs view define npm-pkgs --origin npm\n_all_docs view define npm-vers --origin npm --select 'name, versions|keys as versions'\n_all_docs view define private --registry https://npm.company.com\n_all_docs view define has-scripts --origin npm --select 'name, scripts.preinstall, scripts.postinstall'\n```\n\n### List Views\n```bash\n_all_docs view list\n_all_docs view list --json\n```\n\n### Show View Details\n```bash\n_all_docs view show npm-vers\n_all_docs view show npm-vers --json\n```\n\n### Query a View\n```bash\n_all_docs view query <name> [options]\n\n# Examples\n_all_docs view query npm-vers                    # Stream all as ndjson\n_all_docs view query npm-vers --limit 100        # First 100 records\n_all_docs view query npm-vers --count            # Just the count\n_all_docs view query npm-vers --filter \"name=lodash\"\n_all_docs view query npm-vers --collect > all.json\n```\n\n### Join Two Views\n```bash\n_all_docs view join <left> <right> [options]\n\n# Join types\n--left      # All from left, matching from right (default)\n--inner     # Only records in both\n--right     # All from right, matching from left\n--full      # All records from both\n--diff      # Records in left but not in right\n```\n\n**Example: Find suspicious packages with install scripts but no node-gyp dependency**\n\nPackages with `preinstall` or `postinstall` scripts typically need them for native compilation (node-gyp). Packages with these scripts that *don't* depend on node-gyp may warrant investigation.\n\n```bash\n# View 1: Packages with install scripts\n_all_docs view define has-install-scripts --origin npm \\\n  --select 'name, scripts.preinstall, scripts.postinstall' \\\n  --where 'scripts.preinstall != null || scripts.postinstall != null'\n\n# View 2: Packages that depend on node-gyp\n_all_docs view define uses-node-gyp --origin npm \\\n  --select 'name' \\\n  --where 'dependencies[\"node-gyp\"] != null || devDependencies[\"node-gyp\"] != null'\n\n# Find packages with install scripts but NO node-gyp dependency\n_all_docs view join has-install-scripts uses-node-gyp --diff | head -100\n```\n\n### Delete a View\n```bash\n_all_docs view delete <name>\n```\n\n## Programmatic API\n\n```javascript\nimport { View, ViewStore, queryView, joinViews, diffViews } from '@_all_docs/view';\nimport { Cache } from '@_all_docs/cache';\n\n// Create and save a view\nconst view = new View({\n  name: 'npm-packages',\n  origin: 'npm',\n  select: 'name, versions|keys as versions'\n});\n\nconst store = new ViewStore('/path/to/config');\nawait store.save(view);\n\n// Query a view\nconst cache = new Cache({ cacheDir: '/path/to/cache' });\n\nfor await (const record of queryView(view, cache)) {\n  console.log(record);  // { name: 'lodash', versions: ['1.0.0', '2.0.0', ...] }\n}\n\n// Extract dependency graph edges (deps.dev style)\nconst edgeView = new View({\n  name: 'npm-edges',\n  origin: 'npm',\n  select: 'name, dependencies|keys as deps, devDependencies|keys as devDeps'\n});\n\nfor await (const pkg of queryView(edgeView, cache)) {\n  for (const dep of pkg.deps || []) {\n    console.log({ from: pkg.name, to: dep, type: 'runtime' });\n  }\n  for (const dep of pkg.devDeps || []) {\n    console.log({ from: pkg.name, to: dep, type: 'dev' });\n  }\n}\n```\n\n## How Joins Work\n\nJoins use **O(1) lookups** for the right side by constructing cache keys directly:\n\n1. Stream all records from the left view's origin prefix\n2. For each record, construct the right-side cache key using the join key\n3. Fetch the right record directly (no scanning)\n4. Output based on join type\n\nThis makes joins efficient even with millions of records.\n\n## Storage\n\nViews are stored in `{configDir}/views/{name}.view.json`:\n\n```json\n{\n  \"name\": \"npm-versions\",\n  \"origin\": \"npm\",\n  \"registry\": null,\n  \"type\": \"packument\",\n  \"select\": \"name, versions|keys as versions, time.modified\",\n  \"createdAt\": \"2024-01-15T10:30:00.000Z\"\n}\n```\n","readmeFilename":"README.md","homepage":"https://github.com/indexzero/_all_docs#readme","repository":{"type":"git","url":"git+https://github.com/indexzero/_all_docs.git","directory":"src/view"},"author":{"name":"Charlie Robbins","email":"npm@charlie.dev"},"bugs":{"url":"https://github.com/indexzero/_all_docs/issues"},"license":"Apache-2.0"}