@tracepass / dpp-identifiers
根据EN 18219 - GS1, ISO/IEC 15459, IEC 61406, DID, DOI验证和解析欧盟数字产品护照标识符。纯函数,没有网络,没有第三方运行时依赖。
的一部分 tracepass-open ·由 TracePass
npm install @tracepass/dpp-identifiers
验证EN 18219允许的数字产品护照的任何标识符方案,并且 构建/解析它们映射到的解析器路径。
验证产品标识符
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
支持的方案(EN 18219)
scheme | EN 18219 | 笔记 |
|---|---|---|
gs1 | 方案1 | GTIN-8/12/13/14接受;始终归一化为14位数字 |
iso15459 | 方案1 | 非gs1发行机构。机构代码第一个字符必须是一个字母 |
iec61406 | 方案2 | https URL,仅限ascii, RFC 3986有效 |
did | 方案3 | 只支持W3C DID核心语法;No method allow-list |
doi | 计划5 | 10.<registrant>/<suffix>; 接受 doi: / https://doi.org/ 前缀。 granularity (model | batch | item):第5.6.2(b)条 |
电池的护照 只接受 gs1 和 iso15459电池法规第77(3)条
要求ISO/IEC 15459,直到授权法案用EN 18219取代它。
LEI不是产品标识符。 EN 18219条款5.1为方案1-5命名
产品;LEI没有出现。将LEI作为 运营商 标识符:
{ scheme: "iso6523", icd: "0199", value: "<20-char LEI>" }.
验证操作员和设备标识符
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
构建和解析解析器路径
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 返回 null 用于 iec61406, did 和 doi -这些都是自己的
规范URL(使用) identifierOwnUrl 相反)。
parseResolverPath 以及消费者的优先购买权
parseResolverPath 也读得更老 /<AGENCY>/<primaryId>[/<serial>] 路径,也就是URL
已经以那种形式打印出来的一直在解析。 identifierToUri 不再排放二氧化碳。它
执行ISO/IEC 15459机构代码形状:第一个路径段
必须是1-3个大写字母匹配吗 [A-Z][A-Z0-9]{0,2}。这会拒绝常见的应用路由:
| 路径 | 结果 |
|---|---|
/about/team | null -“about”是5个字符 |
/mh/ABC | null -“mh”是小写的; identifierToUri 总是大写 |
/MH/BAT-001 | {scheme: "iso15459", …} ✓ |
/01/0544…/21/SN | {scheme: "gs1", …} ✓ |
短的大写路由仍然是不明确的。 /P/x 解析为 {issuingAgencyCode: "P", primaryId: "x"} 因为 "P" 是一个有效的1字符机构代码。消费者必须匹配他们的
自己的短应用路线(/P, /Q, /DOCS),然后再委托给 parseResolverPath.
读取规范标识符
在EN 18219之前编写的代码只设置 passport.gs1,以及新的代码集
passport.identifier,使用 resolveProductIdentifier 读现存的:
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 两个字段都设置时优先。
字符约束(EN 18219条款4.3.2)
所有标识符字段必须使用ISO/IEC 646字符-可打印ASCII (0x20-0x7E)。 非ascii输入被拒绝,并出现错误命名违规字段。
零依赖关系
这个包是基于普通对象的纯TypeScript。它只导入两个兄弟包
(@tracepass/dpp-types 类型和 @tracepass/gs1-utils GTIN/GLN算法),
这两种方法都不添加第三方运行时依赖项。
超出范围
- EN 18219方案 (RAIN RFID和2D条码)编码相同的ISO/IEC 15459 有限容量载波上的标识符。这是一个数据运营商的问题,而不是一个独立的问题 标识符类型。
- DID方法允许列表。 第5.4.2条只要求DID核心。是:网络,是:以太和 Did:ebsi是一个推荐(5.4.3)。任何DID核心语法都可以接受。
- 作为操作符标识符的DID 还需要一个可验证的证书 授权公司登记册(第6.4.2(b)条)。验证器只检查语法。