@tracepass/dpp-identifiers

中文文档
0.19.0发布于 2026/11/03Apache-2.0TypeScriptES Module构建来源已验证

Validate and parse EU Digital Product Passport identifiers (EN 18219) — GS1 Digital Link, ISO/IEC 15459, IEC 61406, DID, DOI. Pure functions, no network, no third-party runtime dependencies.

原始使用文档
文档目录10 节
  1. Validate a product identifier
  2. Supported schemes (EN 18219)
  3. Validate operator and facility identifiers
  4. Build and parse resolver paths
  5. parseResolverPath and consumer pre-emption
  6. Read the canonical identifier
  7. Character constraint (EN 18219 clause 4.3.2)
  8. Zero dependencies
  9. Out of scope
  10. License
TracePass

@tracepass/dpp-identifiers

Validate and parse EU Digital Product Passport identifiers per EN 18219 — GS1, ISO/IEC 15459, IEC 61406, DID, DOI. Pure functions, no network, no third-party runtime dependencies.

License Dependencies

Part of tracepass-open · Maintained by TracePass


npm install @tracepass/dpp-identifiers

Validate any identifier scheme EN 18219 permits for a Digital Product Passport, and build/parse the resolver paths they map to.

Validate a product identifier

import { validateProductIdentifier } from "@tracepass/dpp-identifiers";
import type { ProductIdentifier } from "@tracepass/dpp-types";

// GS1 Digital Link — normalises GTIN-8/12/13 to 14 digits
const gs1: ProductIdentifier = {
  scheme: "gs1",
  gtin: "5449000000996",   // GTIN-13 — normalised to "05449000000996"
  serialNumber: "SN-001",
};
const r = validateProductIdentifier(gs1);
// r.ok === true; r.value.gtin === "05449000000996"

// ISO/IEC 15459 — required for battery passports (Art. 77(3))
const iso15459: ProductIdentifier = {
  scheme: "iso15459",
  issuingAgencyCode: "MH",   // starts with a letter (GS1 holds the digit range)
  primaryId: "BATT-2026-001",
  raw: "MHBATT-2026-001",
};
validateProductIdentifier(iso15459).ok;  // true

// Battery passports: only gs1 and iso15459 are allowed
validateProductIdentifier(
  { scheme: "did", did: "did:web:example.com", method: "web" },
  { category: "battery" },
).ok;  // false — Battery Regulation Art. 77(3) requires ISO/IEC 15459

Supported schemes (EN 18219)

schemeEN 18219Notes
gs1scheme 1GTIN-8/12/13/14 accepted; always normalised to 14 digits
iso15459scheme 1Non-GS1 issuing agency. Agency code first char must be a letter
iec61406scheme 2https URL, ASCII-only, RFC 3986-valid
didscheme 3W3C DID Core syntax only; no method allow-list
doischeme 510.<registrant>/<suffix>; accepts doi: / https://doi.org/ prefixes. granularity (model | batch | item) is required: clause 5.6.2(b)

Battery passports accept only gs1 and iso15459: Battery Regulation Art. 77(3) requires ISO/IEC 15459 until a delegated act replaces it with EN 18219.

LEI is not a product identifier. EN 18219 clause 5.1 names schemes 1–5 for products; LEI does not appear. Pass LEI as an operator identifier instead: { scheme: "iso6523", icd: "0199", value: "<20-char LEI>" }.

Validate operator and facility identifiers

import { validateOperatorIdentifier, validateFacilityIdentifier } from "@tracepass/dpp-identifiers";

// ISO 7064 MOD 97-10 check for LEI
validateOperatorIdentifier({ scheme: "iso6523", icd: "0199", value: "5493001KJTIIGC8Y1R12" }).ok;  // true

// GLN (icd 0088) validated with GS1 mod-10
validateOperatorIdentifier({ scheme: "iso6523", icd: "0088", value: "5412345000013" }).ok;  // true

// Facility — the same four schemes as operators (EN 18219 clauses 6.2–6.5)
validateFacilityIdentifier({ scheme: "gln", gln: "5412345000013", extension: "DOCK-1" }).ok;  // true
validateFacilityIdentifier({ scheme: "did", did: "did:web:plant.example.com" }).ok;  // true

Build and parse resolver paths

import { identifierToUri, identifierOwnUrl, parseResolverPath, diQueryCandidateKeys, identifierKey } from "@tracepass/dpp-identifiers";

const gs1 = { scheme: "gs1", gtin: "05449000000996", serialNumber: "SN-001" } as const;

// Build a resolver URL
identifierToUri(gs1, "id.example.com");
// "https://id.example.com/01/05449000000996/21/SN-001"

// Parse back
parseResolverPath("/01/05449000000996/21/SN-001");
// { scheme: "gs1", gtin: "05449000000996", serialNumber: "SN-001" }

// ISO/IEC 15459 → the ISO/IEC 18975 query form (EN 18219 Table B.12)
identifierToUri(
  { scheme: "iso15459", issuingAgencyCode: "QC", primaryId: "ELMI12345", serial: "654321", raw: "QC/ELMI12345/654321" },
  "id.example.com",
);
// "https://id.example.com/?.25P=QCELMI12345&.S=654321"

// A resolver splits .25P back into candidate keys (the agency code is 1–3 characters)
diQueryCandidateKeys("QCELMI12345", "654321");
// ["iso15459:Q:CELMI12345:654321", "iso15459:QC:ELMI12345:654321", "iso15459:QCE:LMI12345:654321"]

// A DOI carries its own URL; no resolver needed
identifierOwnUrl({ scheme: "doi", doi: "10.1234/my-passport", granularity: "model" });
// "https://doi.org/10.1234/my-passport"

// Canonical key for deduplication
identifierKey(gs1);  // "gs1:05449000000996:SN-001"

identifierToUri returns null for iec61406, did and doi — those carry their own canonical URL (use identifierOwnUrl instead).

parseResolverPath and consumer pre-emption

parseResolverPath also reads the older /<AGENCY>/<primaryId>[/<serial>] path, so a URL already printed in that form keeps resolving. identifierToUri no longer emits it. It enforces the ISO/IEC 15459 agency-code shape: the first path segment must be 1–3 uppercase chars matching [A-Z][A-Z0-9]{0,2}. This rejects common app routes:

PathResult
/about/teamnull — "about" is 5 chars
/mh/ABCnull — "mh" is lowercase; identifierToUri always emits uppercase
/MH/BAT-001{scheme: "iso15459", …} ✓
/01/0544…/21/SN{scheme: "gs1", …} ✓

Short uppercase routes are still ambiguous. /P/x parses as {issuingAgencyCode: "P", primaryId: "x"} because "P" is a valid 1-char agency code. Consumers MUST match their own short app routes (/P, /Q, /DOCS, …) before delegating to parseResolverPath.

Read the canonical identifier

When code that was written before EN 18219 only sets passport.gs1, and new code sets passport.identifier, use resolveProductIdentifier to read whichever is present:

import { resolveProductIdentifier } from "@tracepass/dpp-identifiers";

const id = resolveProductIdentifier(passport);
// → passport.identifier, or lifted Gs1Identifier from passport.gs1, or undefined
if (!id) throw new Error("No product identifier");

identifier takes precedence when both fields are set.

Character constraint (EN 18219 clause 4.3.2)

All identifier fields must use ISO/IEC 646 characters — printable ASCII (0x20–0x7E). Non-ASCII input is rejected with an error naming the offending field.

Zero dependencies

This package is pure TypeScript over plain objects. It imports only two sibling packages (@tracepass/dpp-types for types and @tracepass/gs1-utils for GTIN/GLN arithmetic), neither of which adds a third-party runtime dependency.

Out of scope

  • EN 18219 scheme 4 (RAIN RFID and 2D codes) encodes the same ISO/IEC 15459 identifiers on a limited-capacity carrier. It is a data-carrier concern, not a separate identifier type.
  • DID method allow-list. Clause 5.4.2 requires DID Core only. did:web, did:ethr and did:ebsi are a recommendation (5.4.3). Any DID Core syntax is accepted.
  • A DID as an operator identifier also needs a verifiable credential from an authorised company register (clause 6.4.2(b)). The validator checks the syntax only.

License

Apache-2.0.

版本与发布记录

@tracepass/dpp-identifiers 共发布 18 个版本,首次发布于 2026/09/28;过去 12 个月发布了 18 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.19.0latest2026/11/03今天
0.18.42026/10/303 天前
0.18.32026/10/303 天前
0.18.22026/10/304 天前
0.18.12026/10/304 天前
0.18.02026/10/295 天前

关于 @tracepass/dpp-identifiers 的常见问题

@tracepass/dpp-identifiers 是什么?

验证和解析欧盟数字产品护照标识符(EN 18219) - GS1数字链接,ISO/IEC 15459, IEC 61406, DID, DOI。纯函数,没有网络,没有第三方运行时依赖。

如何安装 @tracepass/dpp-identifiers?

在项目目录运行 npm install @tracepass/dpp-identifiers,或使用 pnpm add / yarn add / bun add。

@tracepass/dpp-identifiers 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@tracepass/dpp-identifiers 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@tracepass/dpp-identifiers 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@tracepass/dpp-identifiers 可以商用吗?

@tracepass/dpp-identifiers 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@tracepass/dpp-identifiers 还在维护吗?

最新版本 0.19.0 发布于 2026/11/03(今天);至今共发布 18 个版本,其中过去 12 个月发布了 18 个;首次发布于 2026/09/28。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。