{"_id":"@deepinnet/service-mvt-middleware-core","name":"@deepinnet/service-mvt-middleware-core","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.1":{"name":"@deepinnet/service-mvt-middleware-core","version":"0.0.1","description":"Core tile engine, shared types, and PostgreSQL adapters for DeepInNet vector tile services.","license":"Apache-2.0","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","publishConfig":{"access":"public"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./postgres":{"types":"./dist/postgres.d.ts","import":"./dist/postgres.js","require":"./dist/postgres.cjs"}},"keywords":["mvt","vector-tile","postgres","postgis","tile-engine"],"scripts":{"build":"tsup src/index.ts src/postgres.ts --format esm,cjs --dts --clean --target es2019 && node ../../scripts/obfuscate-dist.mjs dist","clean":"rm -rf dist","test":"pnpm run build && node --test test/**/*.test.mjs","typecheck":"tsc -p tsconfig.json --noEmit"},"dependencies":{"@deepinnet/geojson-to-mvt":"0.0.1","pg":"^8.16.0","zod":"^3.24.3"},"author":{"name":"DeepInNet"},"devDependencies":{"@types/pg":"^8.15.5"},"_id":"@deepinnet/service-mvt-middleware-core@0.0.1","gitHead":"0684ad6771e806d99048408f00d23745d85ef051","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-ZELpLgRuR/3WuiDVgdq+XmU26eanUW6GUXbs5IcfHVtZRgPoqjHOrQhhjz+D5ysAaHdJhqElOE7YU73i61jjmA==","shasum":"1a858565755b78ce03c3793f2fcac04cd557a18d","tarball":"https://registry.npmjs.org/@deepinnet/service-mvt-middleware-core/-/service-mvt-middleware-core-0.0.1.tgz","fileCount":14,"unpackedSize":226797,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCWttw5iE6dACNG65TDSdFmaYcOoGdYHr4NEAdehlj1MgIhAJqXZQecEmOcmXkJQ+gQucutjSutq3pzkEdUhS08+SfH"}]},"_npmUser":{"name":"shenduzhilian","email":"shenduzhilian@gmail.com"},"directories":{},"maintainers":[{"name":"weiwei2020","email":"1032159552@qq.com"},{"name":"wxg-james","email":"346775171@qq.com"},{"name":"shenduzhilian","email":"shenduzhilian@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/service-mvt-middleware-core_0.0.1_1777521242613_0.25910311450393064"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-30T03:54:02.532Z","0.0.1":"2026-04-30T03:54:02.761Z","modified":"2026-04-30T03:54:03.027Z"},"maintainers":[{"name":"weiwei2020","email":"1032159552@qq.com"},{"name":"wxg-james","email":"346775171@qq.com"},{"name":"shenduzhilian","email":"shenduzhilian@gmail.com"}],"description":"Core tile engine, shared types, and PostgreSQL adapters for DeepInNet vector tile services.","keywords":["mvt","vector-tile","postgres","postgis","tile-engine"],"author":{"name":"DeepInNet"},"license":"Apache-2.0","readme":"# @deepinnet/service-mvt-middleware-core\n\n底层核心包，提供：\n\n- 图层配置类型\n- 二维 / 三维瓦片引擎\n- PostgreSQL / PostGIS adapter\n- filter / cluster / gzip 等核心能力\n\n这个包**不负责 HTTP 路由**。如果你想要开箱即用的 NestJS 路由层，请使用：\n\n- `@deepinnet/service-mvt-middleware`\n\n如果你要自己接 Express、Fastify、Koa、原生 Node HTTP、Serverless handler，或者只想在服务内部直接拿到瓦片二进制结果，这个包就是直接使用层。\n\n## 安装\n\n```bash\npnpm add @deepinnet/service-mvt-middleware-core\n```\n\n## 这个包怎么接入\n\n最常见的接入方式分两种：\n\n1. 直接用内置 PostgreSQL adapter  \n   你只提供图层配置和数据库连接，`createTileEngine(...)` 自动完成二维 / 三维查询。\n\n2. 自己实现查询执行器  \n   你把 `queryExecutor` / `query3DExecutor` 接到自己的数据层，这样可以脱离 PostgreSQL adapter。\n\n### 方式一：直接用内置 PostgreSQL adapter\n\n```ts\nimport { createTileEngine } from \"@deepinnet/service-mvt-middleware-core\";\n\nconst engine = createTileEngine({\n  layers: [\n    {\n      layer: \"data\",\n      sourceType: \"table\",\n      sourceObject: \"public.geom\",\n      idField: \"id\",\n      geomField: \"geometry\",\n      geom3dField: \"geometry3d\",\n      sourceCrs: \"4326\",\n      defaultOutputCrs: \"gcj02\",\n      supportedOutputCrs: [\"4326\", \"gcj02\"],\n      propertyWhitelist: [\"name\", \"color\"],\n      gzip: {\n        enabled: true,\n      },\n      clusterEnabled: true,\n      clusterMaxZoom: 15,\n      clusterCellSize: 256,\n      clusterMaxFeatures: 2048,\n      clusterFields: {\n        color: {\n          field: \"color\",\n          op: \"top\",\n        },\n      },\n    },\n  ],\n  database: {\n    connectionString: process.env.DATABASE_URL,\n  },\n  defaults: {\n    sourceCrs: \"4326\",\n    defaultOutputCrs: \"gcj02\",\n  },\n  tile: {\n    extent: 4096,\n    buffer: 64,\n    maxFeatures: 20000,\n  },\n});\n```\n\n### 方式二：自己提供二维 / 三维查询执行器\n\n```ts\nimport { createTileEngine } from \"@deepinnet/service-mvt-middleware-core\";\n\nconst engine = createTileEngine({\n  layers: [\n    {\n      layer: \"data\",\n      sourceType: \"table\",\n      sourceObject: \"public.geom\",\n      idField: \"id\",\n      geomField: \"geometry\",\n    },\n  ],\n  queryExecutor: async (context) => {\n    // 这里由你自己返回二维 MVT 二进制\n    return {\n      body: new Uint8Array(),\n      contentType: \"application/vnd.mapbox-vector-tile\",\n    };\n  },\n  query3DExecutor: async (context) => {\n    // 这里由你自己返回三维自定义 PBF 二进制\n    return {\n      body: new Uint8Array(),\n      contentType: \"application/octet-stream\",\n    };\n  },\n});\n```\n\n## 如何请求二维 / 三维瓦片\n\n这个包本身不处理 HTTP，但你最终通常会把外部请求参数映射到：\n\n- `engine.queryTile(input)`  \n  返回二维 MVT\n\n- `engine.query3DTile(input)`  \n  返回三维自定义 PBF\n\n### 最小调用示例\n\n二维：\n\n```ts\nconst result = await engine.queryTile({\n  layer: \"data\",\n  z: 8,\n  x: 209,\n  y: 111,\n  crs: \"gcj02\",\n  fields: [\"name\", \"color\"],\n});\n```\n\n三维：\n\n```ts\nconst result = await engine.query3DTile({\n  layer: \"data\",\n  z: 8,\n  x: 209,\n  y: 111,\n  crs: \"gcj02\",\n});\n```\n\n返回结果 `TileQueryResult` 中最常用的字段：\n\n| 字段 | 含义 |\n| --- | --- |\n| `status` | HTTP 语义状态码，通常是 `200` 或 `204`。 |\n| `headers` | 额外响应头，通常是缓存或扩展头。 |\n| `body` | 瓦片二进制内容。 |\n| `contentType` | 二维通常是 `application/vnd.mapbox-vector-tile`，三维通常是 `application/octet-stream`。 |\n| `isEmpty` | 是否为空瓦片。 |\n| `compression` | 压缩标志，例如 `{ gzip: true }`。是否真正写出 `Content-Encoding` 取决于你的 HTTP 层。 |\n\n## 如果你要自己接 HTTP\n\n最常见的模式，是把 URL 和 query 参数手动映射成 `TileQueryInput`。\n\n### Express 示例\n\n```ts\nimport express from \"express\";\nimport { createTileEngine } from \"@deepinnet/service-mvt-middleware-core\";\n\nconst app = express();\nconst engine = createTileEngine({\n  layers: [\n    {\n      layer: \"data\",\n      sourceType: \"table\",\n      sourceObject: \"public.geom\",\n      idField: \"id\",\n      geomField: \"geometry\",\n      geom3dField: \"geometry3d\",\n    },\n  ],\n  database: {\n    connectionString: process.env.DATABASE_URL,\n  },\n});\n\nfunction parseFields(value?: string): string[] | undefined {\n  if (!value) {\n    return undefined;\n  }\n\n  return value\n    .split(\",\")\n    .map((field) => field.trim())\n    .filter(Boolean);\n}\n\napp.get(\"/tiles/:layer/:z/:x/:y.pbf\", async (req, res) => {\n  const result = await engine.queryTile({\n    layer: req.params.layer,\n    z: Number(req.params.z),\n    x: Number(req.params.x),\n    y: Number(req.params.y),\n    crs: req.query.crs as \"4326\" | \"gcj02\" | undefined,\n    minZoom: req.query.minZoom ? Number(req.query.minZoom) : undefined,\n    maxZoom: req.query.maxZoom ? Number(req.query.maxZoom) : undefined,\n    fields: parseFields(req.query.fields as string | undefined),\n    from: req.query.from as string | undefined,\n    to: req.query.to as string | undefined,\n    version: req.query.v as string | undefined,\n  });\n\n  if (result.contentType) {\n    res.setHeader(\"Content-Type\", result.contentType);\n  }\n\n  for (const [key, value] of Object.entries(result.headers)) {\n    res.setHeader(key, value);\n  }\n\n  if (result.compression?.gzip) {\n    res.setHeader(\"Content-Encoding\", \"gzip\");\n  }\n\n  res.status(result.status).send(Buffer.from(result.body));\n});\n```\n\n`TileQueryInput.filter` 仍然可用，但建议由服务端内部逻辑填充。默认的 Nest middleware 不会把它暴露成公开 query 参数；如果你自己接 HTTP，请按自己的安全边界决定是否注入该字段。\n\n## 参数怎么传\n\n### `TileQueryInput`\n\n`queryTile(...)` 和 `query3DTile(...)` 使用同一组查询输入。\n\n| 参数 | 类型 | 是否必填 | 含义 | 示例 |\n| --- | --- | --- | --- | --- |\n| `layer` | `string` | 是 | 图层名，必须命中 `layers` 配置中的某一项。 | `\"data\"` |\n| `z` | `number` | 是 | 缩放级别，必须是大于等于 `0` 的整数。 | `8` |\n| `x` | `number` | 是 | 当前 `z` 层级下的瓦片列号。 | `209` |\n| `y` | `number` | 是 | 当前 `z` 层级下的瓦片行号。 | `111` |\n| `crs` | `\"4326\" \\| \"gcj02\"` | 否 | 输出坐标系。未传时使用图层的 `defaultOutputCrs`。 | `\"gcj02\"` |\n| `minZoom` | `number` | 否 | 请求级最小缩放限制，会与图层和默认配置一起合并。 | `6` |\n| `maxZoom` | `number` | 否 | 请求级最大缩放限制，会与图层和默认配置一起合并。 | `16` |\n| `fields` | `string[]` | 否 | 希望返回的属性字段数组，最终仍受 `propertyWhitelist` 限制。 | `[\"name\", \"color\"]` |\n| `filter` | `TileFilterExpression` | 否 | 结构化过滤条件，适合由服务端内部逻辑注入。 | `{ op: \"eq\", field: \"name\", value: \"point-1\" }` |\n| `from` | `string` | 否 | 起始时间，要求图层配置了 `timeField`。 | `\"2026-01-01T00:00:00Z\"` |\n| `to` | `string` | 否 | 结束时间，要求图层配置了 `timeField`。 | `\"2026-12-31T23:59:59Z\"` |\n| `version` | `string` | 否 | 版本字符串，主要参与缓存键区分。 | `\"2026-04-30\"` |\n\n### `filter` 怎么写\n\n当前支持的结构化过滤操作符：\n\n- `eq`\n- `in`\n- `range`\n- `and`\n- `or`\n\n#### `eq`\n\n```ts\n{\n  op: \"eq\",\n  field: \"name\",\n  value: \"point-1\",\n}\n```\n\n#### `in`\n\n```ts\n{\n  op: \"in\",\n  field: \"color\",\n  values: [\"#ff0000\", \"#00ff00\"],\n}\n```\n\n#### `range`\n\n```ts\n{\n  op: \"range\",\n  field: \"created_at\",\n  min: \"2026-01-01T00:00:00Z\",\n  max: \"2026-12-31T23:59:59Z\",\n}\n```\n\n#### `and`\n\n```ts\n{\n  op: \"and\",\n  filters: [\n    { op: \"eq\", field: \"name\", value: \"point-1\" },\n    { op: \"in\", field: \"color\", values: [\"#ff0000\", \"#00ff00\"] },\n  ],\n}\n```\n\n#### `or`\n\n```ts\n{\n  op: \"or\",\n  filters: [\n    { op: \"eq\", field: \"name\", value: \"point-1\" },\n    { op: \"eq\", field: \"name\", value: \"point-2\" },\n  ],\n}\n```\n\n## 该怎么配置图层\n\n### `LayerConfigInput`\n\n图层配置决定某个 `layer` 的数据来源和行为。\n\n| 参数 | 类型 | 是否必填 | 含义 |\n| --- | --- | --- | --- |\n| `layer` | `string` | 是 | 图层名，也是查询时传入的 `layer` 标识。 |\n| `sourceType` | `\"table\" \\| \"view\" \\| \"sql\"` | 是 | 数据源类型。当前 PostgreSQL adapter 只支持 `table` 和 `view`。 |\n| `sourceObject` | `string` | 是 | 源表名或视图名，例如 `public.geom`。 |\n| `idField` | `string` | 是 | 主键字段，用于返回要素 `id`。 |\n| `geomField` | `string` | 条件必填 | 二维几何字段。与 `wktField` 至少提供一个。 |\n| `geom3dField` | `string` | 否 | 三维几何字段。三维查询优先使用它。 |\n| `wktField` | `string` | 条件必填 | WKT 文本字段，可作为 `geomField` 的替代输入。 |\n| `timeField` | `string` | 否 | 时间字段，启用 `from/to` 过滤时必须提供。 |\n| `sourceCrs` | `\"4326\" \\| \"gcj02\"` | 否 | 源数据坐标系。 |\n| `defaultOutputCrs` | `\"4326\" \\| \"gcj02\"` | 否 | 请求未显式传 `crs` 时的默认输出坐标系。 |\n| `supportedOutputCrs` | `OutputCrs[]` | 否 | 当前图层允许输出的坐标系集合。 |\n| `propertyWhitelist` | `string[]` | 否 | 允许暴露给客户端的属性字段白名单。 |\n| `minZoom` | `number` | 否 | 图层级最小缩放限制。 |\n| `maxZoom` | `number` | 否 | 图层级最大缩放限制。 |\n| `clusterEnabled` | `boolean` | 否 | 是否启用 cluster。 |\n| `clusterMaxZoom` | `number` | 条件必填 | 当 `clusterEnabled=true` 时必须配置，表示 `z <= clusterMaxZoom` 时走聚合路径。 |\n| `clusterCellSize` | `number` | 否 | cluster 网格尺寸，数值越大聚合越粗。 |\n| `clusterMaxFeatures` | `number` | 否 | cluster 结果最多输出多少条聚合要素。 |\n| `clusterFields` | `Record<string, TileClusterFieldConfig>` | 否 | 聚合属性字段配置。 |\n| `clusterAltitudeMode` | `\"max\"` | 否 | 三维 cluster 高程聚合规则，当前只支持 `max`。 |\n| `gzip` | `{ enabled?: boolean }` | 否 | 响应压缩配置，`gzip.enabled=true` 时结果会标记为 gzip。 |\n\n### `clusterFields` 要怎么写\n\n`clusterFields` 的 key 是聚合后输出的属性名，value 用来指定源字段和聚合算子。\n\n```ts\nclusterFields: {\n  color: {\n    field: \"color\",\n    op: \"top\",\n  },\n  maxHeight: {\n    field: \"height\",\n    op: \"max\",\n  },\n}\n```\n\n当前支持的聚合算子：\n\n- `sum`\n- `min`\n- `max`\n- `first`\n- `top`\n\n约束：\n\n- 输出别名不能与系统保留字段冲突，如 `id`、`count`、`isCluster`、`clusterId`\n- 源字段必须出现在 `propertyWhitelist` 中\n\n## 默认配置怎么传\n\n### `TileEngineDefaults`\n\n`defaults` 用来提供图层缺省值。\n\n| 参数 | 类型 | 含义 |\n| --- | --- | --- |\n| `sourceCrs` | `\"4326\" \\| \"gcj02\"` | 图层未显式声明时的默认源坐标系。 |\n| `defaultOutputCrs` | `\"4326\" \\| \"gcj02\"` | 图层未显式声明时的默认输出坐标系。 |\n| `minZoom` | `number` | 图层未显式声明时的默认最小缩放限制。 |\n| `maxZoom` | `number` | 图层未显式声明时的默认最大缩放限制。 |\n| `clusterEnabled` | `boolean` | 图层未显式声明时是否默认启用 cluster。 |\n| `clusterMaxZoom` | `number` | 图层未显式声明时的 cluster 阈值。 |\n| `clusterCellSize` | `number` | 图层未显式声明时的 cluster 网格尺寸。 |\n| `clusterMaxFeatures` | `number` | 图层未显式声明时的 cluster 输出上限。 |\n| `clusterFields` | `object` | 图层未显式声明时的聚合属性配置。 |\n| `clusterAltitudeMode` | `\"max\"` | 图层未显式声明时的三维聚合高程规则。 |\n| `gzip` | `{ enabled?: boolean }` | 图层未显式声明时的响应压缩配置。 |\n\n## `createTileEngine(...)` 还能接哪些参数\n\n### `TileEngineOptions`\n\n除了 `layers` 和 `defaults`，还支持这些选项：\n\n| 参数 | 类型 | 含义 |\n| --- | --- | --- |\n| `database` | `PostgresDatabaseOptions` | PostgreSQL / PostGIS 连接配置。 |\n| `tile` | `TileRenderOptions` | 瓦片渲染参数，例如 `extent`、`buffer`、`maxFeatures`。 |\n| `queryExecutor` | `TileQueryExecutor` | 自定义二维查询执行器，会覆盖默认 PostgreSQL adapter。 |\n| `query3DExecutor` | `TileQueryExecutor` | 自定义三维查询执行器，会覆盖默认 PostgreSQL adapter。 |\n| `metadataResolver` | `TileMetadataResolver` | 自定义 metadata 扩展逻辑。 |\n| `cacheKeyVersion` | `string` | 缓存键版本号，用于主动打断旧缓存。 |\n\n### `PostgresDatabaseOptions`\n\n| 参数 | 类型 | 含义 |\n| --- | --- | --- |\n| `pool` | `Pool` | 已存在的 `pg.Pool` 实例。 |\n| `connectionString` | `string` | 连接字符串，例如 `postgresql://user:pass@host:5432/db`。 |\n| `poolConfig` | `PoolConfig` | `pg` 的细粒度连接池配置。 |\n| `healthCheckQuery` | `string` | 健康检查 SQL，默认是 `SELECT 1`。 |\n\n### `TileRenderOptions`\n\n| 参数 | 类型 | 含义 |\n| --- | --- | --- |\n| `extent` | `number` | MVT 渲染 extent。 |\n| `buffer` | `number` | 瓦片边缘缓冲区。 |\n| `maxFeatures` | `number` | 单次查询允许返回的最大要素数。 |\n\n## 常见接入模式\n\n### 只做二维 MVT 服务\n\n- 配 `geomField`\n- 调 `queryTile(...)`\n- 不一定需要 `geom3dField`\n\n### 同时做二维和三维\n\n- 同时配 `geomField` 和 `geom3dField`\n- 二维走 `queryTile(...)`\n- 三维走 `query3DTile(...)`\n\n### 只做三维\n\n- 推荐配 `geom3dField`\n- 如果没有 `geom3dField`，但有 `geomField` / `wktField`，三维链路会尝试回退并补 `z=0`\n\n## 默认行为与约束\n\n- `crs` 当前仅支持 `4326` 和 `gcj02`。\n- `fields` 最终受 `propertyWhitelist` 限制。\n- `filter` 不是任意 SQL 字符串，而是结构化 JSON 条件。\n- `from/to` 要求图层配置了 `timeField`。\n- `minZoom/maxZoom` 会和图层配置、默认配置一起合并。\n- `clusterEnabled=true` 时必须提供 `clusterMaxZoom`。\n- 三维 `clusterAltitudeMode` 当前仅支持 `max`。\n- `gzip.enabled=true` 时结果会带 gzip 压缩标志，是否真正写成 HTTP `Content-Encoding` 取决于你的 HTTP 层。\n- PostgreSQL adapter 当前只支持 `table` 和 `view`，不支持 `sql` 数据源。\n- 如果 `supportedOutputCrs` 配了值，请求里的 `crs` 必须命中该集合。\n\n## 主要导出\n\n主入口：\n\n- `createTileEngine`\n- `buildCacheKey`\n- `loadLayerConfig`\n- `TileError`\n- `isTileError`\n\nPostgreSQL 入口：\n\n- `createPostgresTileExecutor`\n- `createPostgres3DTileExecutor`\n- `createPostgresPool`\n- `createPostgresHealthIndicator`\n- `DEFAULT_POSTGRES_HEALTH_QUERY`\n- `DEFAULT_TILE_RENDER_OPTIONS`\n\n## 主要能力\n\n- 二维标准 MVT 查询\n- 三维自定义 PBF 查询\n- JSON filter 表达式\n- 二维 / 三维 gzip 压缩标志\n- 二维 / 三维 cluster\n- PostgreSQL / PostGIS 查询适配\n- 4326 / gcj02 输出转换\n\n## 导入示例\n\n```ts\nimport { createTileEngine } from \"@deepinnet/service-mvt-middleware-core\";\nimport { createPostgresTileExecutor } from \"@deepinnet/service-mvt-middleware-core/postgres\";\n```\n\n## 协议\n\n本包按 `Apache-2.0` 协议分发。\n","readmeFilename":"README.md","_rev":"1-dd153fd942d00fc26a4fcba9bacf7ca2"}