@alpaca-software/40kdc-data 0.5.14 → 0.5.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abilities-resolver/resolver.d.ts.map +1 -1
- package/dist/abilities-resolver/resolver.js +6 -2
- package/dist/abilities-resolver/resolver.js.map +1 -1
- package/dist/audit-coverage.d.ts.map +1 -1
- package/dist/audit-coverage.js +90 -1
- package/dist/audit-coverage.js.map +1 -1
- package/dist/audit-phrasing.d.ts +64 -0
- package/dist/audit-phrasing.d.ts.map +1 -0
- package/dist/audit-phrasing.js +208 -0
- package/dist/audit-phrasing.js.map +1 -0
- package/dist/build-share-registry.d.ts +2 -0
- package/dist/build-share-registry.d.ts.map +1 -0
- package/dist/build-share-registry.js +115 -0
- package/dist/build-share-registry.js.map +1 -0
- package/dist/cli.js +7 -0
- package/dist/cli.js.map +1 -1
- package/dist/codegen-data.js +23 -0
- package/dist/codegen-data.js.map +1 -1
- package/dist/commands/translate.d.ts.map +1 -1
- package/dist/commands/translate.js +2 -5
- package/dist/commands/translate.js.map +1 -1
- package/dist/commands/validate-all.d.ts.map +1 -1
- package/dist/commands/validate-all.js +8 -3
- package/dist/commands/validate-all.js.map +1 -1
- package/dist/data/bundle.generated.js +1 -1
- package/dist/data/bundle.generated.js.map +1 -1
- package/dist/data/dataset.d.ts +33 -1
- package/dist/data/dataset.d.ts.map +1 -1
- package/dist/data/dataset.js +107 -1
- package/dist/data/dataset.js.map +1 -1
- package/dist/data/index.d.ts +4 -0
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +4 -0
- package/dist/data/index.js.map +1 -1
- package/dist/data/types.d.ts +3 -1
- package/dist/data/types.d.ts.map +1 -1
- package/dist/data/types.js +1 -0
- package/dist/data/types.js.map +1 -1
- package/dist/gen-conformance.js +162 -0
- package/dist/gen-conformance.js.map +1 -1
- package/dist/generated.d.ts +125 -0
- package/dist/generated.d.ts.map +1 -1
- package/dist/generated.js.map +1 -1
- package/dist/integrity.d.ts +32 -0
- package/dist/integrity.d.ts.map +1 -0
- package/dist/integrity.js +120 -0
- package/dist/integrity.js.map +1 -0
- package/dist/report.d.ts +1 -1
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +2 -2
- package/dist/report.js.map +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +42 -0
- package/dist/runner.js.map +1 -1
- package/dist/share/codec.d.ts +83 -0
- package/dist/share/codec.d.ts.map +1 -0
- package/dist/share/codec.js +230 -0
- package/dist/share/codec.js.map +1 -0
- package/dist/share/index.d.ts +30 -0
- package/dist/share/index.d.ts.map +1 -0
- package/dist/share/index.js +36 -0
- package/dist/share/index.js.map +1 -0
- package/dist/share/registry.d.ts +42 -0
- package/dist/share/registry.d.ts.map +1 -0
- package/dist/share/registry.generated.d.ts +4 -0
- package/dist/share/registry.generated.d.ts.map +1 -0
- package/dist/share/registry.generated.js +4 -0
- package/dist/share/registry.generated.js.map +1 -0
- package/dist/share/registry.js +68 -0
- package/dist/share/registry.js.map +1 -0
- package/dist/translate/condition.d.ts +1 -0
- package/dist/translate/condition.d.ts.map +1 -1
- package/dist/translate/condition.js +101 -7
- package/dist/translate/condition.js.map +1 -1
- package/dist/translate/effect.d.ts +28 -20
- package/dist/translate/effect.d.ts.map +1 -1
- package/dist/translate/effect.js +483 -114
- package/dist/translate/effect.js.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +1 -0
- package/dist/validate.js.map +1 -1
- package/package.json +3 -1
- package/schemas/core/allied-rule.schema.json +115 -0
- package/schemas/core/detachment.schema.json +38 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `share-v1` compact list codec.
|
|
3
|
+
*
|
|
4
|
+
* A {@link ShareList} — the lossless essential subset of a roster — is packed
|
|
5
|
+
* into unsigned LEB128 varints over registry indices (see {@link ShareRegistry})
|
|
6
|
+
* and base64url-encoded. Entity ids become 1–3 byte integers instead of full
|
|
7
|
+
* kebab-case strings, so a 2000-pt list fits in a few hundred URL-safe chars.
|
|
8
|
+
*
|
|
9
|
+
* The format is deliberately gzip-free: at list sizes the deflate header costs
|
|
10
|
+
* more than it saves, and a raw varint buffer is trivially reproducible
|
|
11
|
+
* byte-for-byte across the TS, Rust, and Python implementations (the
|
|
12
|
+
* `conformance/share` corpus pins it). Free text (`name`, `grants`) rides along
|
|
13
|
+
* as length-prefixed UTF-8 — the only incompressible part.
|
|
14
|
+
*
|
|
15
|
+
* Wire layout (all integers unsigned LEB128 unless noted):
|
|
16
|
+
* ```
|
|
17
|
+
* formatVersion 1 byte (= SHARE_FORMAT_VERSION)
|
|
18
|
+
* registryVersion varint
|
|
19
|
+
* name varint len + UTF-8 bytes
|
|
20
|
+
* factionRef varint (0 = none; else faction index + 1)
|
|
21
|
+
* battleSize varint (0 = incursion, 1 = strike-force)
|
|
22
|
+
* dispositionRef varint (0 = none; else disposition index + 1)
|
|
23
|
+
* detachments varint count, then [detachment index]...
|
|
24
|
+
* units varint count, then per unit:
|
|
25
|
+
* unitIdx varint
|
|
26
|
+
* modelCount varint
|
|
27
|
+
* flags varint bit0 warlord | bit1 enh | bit2 attach | bit3 ally | bit4 grants
|
|
28
|
+
* [enhIdx] varint if enh
|
|
29
|
+
* [attachOrd] varint if attach (ordinal into THIS unit list)
|
|
30
|
+
* [allyFaction,allyRule] varint×2 if ally
|
|
31
|
+
* [grants] varint count, then [varint len + UTF-8]... if grants
|
|
32
|
+
* loadout varint count, then [wargearIdx varint, count varint]...
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @packageDocumentation
|
|
36
|
+
*/
|
|
37
|
+
import { ShareRegistryIndex } from "./registry.js";
|
|
38
|
+
/** Current wire-format version. Bumped only if the byte layout changes. */
|
|
39
|
+
export declare const SHARE_FORMAT_VERSION = 1;
|
|
40
|
+
/** Battle sizes, encoded by position. */
|
|
41
|
+
declare const BATTLE_SIZES: readonly ["incursion", "strike-force"];
|
|
42
|
+
export type ShareBattleSize = (typeof BATTLE_SIZES)[number];
|
|
43
|
+
/** One weapon/wargear selection: id and how many. */
|
|
44
|
+
export type ShareLoadoutEntry = [wargearId: string, count: number];
|
|
45
|
+
/** One unit in a {@link ShareList}. Mirrors the builder's per-row essentials. */
|
|
46
|
+
export interface ShareUnit {
|
|
47
|
+
datasheetId: string;
|
|
48
|
+
modelCount: number;
|
|
49
|
+
isWarlord: boolean;
|
|
50
|
+
enhancementId: string | null;
|
|
51
|
+
/** Source faction id when this is an *allied* unit; null for own-faction. */
|
|
52
|
+
allyFactionId: string | null;
|
|
53
|
+
/** Allied-rule id this unit was included under; null when not an ally. */
|
|
54
|
+
allyRuleId: string | null;
|
|
55
|
+
/** Ordinal (into {@link ShareList.units}) of the bodyguard this leader joins. */
|
|
56
|
+
attachedToOrdinal: number | null;
|
|
57
|
+
/** Detachment keyword grants the player assigned to this unit. */
|
|
58
|
+
grants: string[];
|
|
59
|
+
loadout: ShareLoadoutEntry[];
|
|
60
|
+
}
|
|
61
|
+
/** The lossless, serializable essence of a list — what a share link carries. */
|
|
62
|
+
export interface ShareList {
|
|
63
|
+
name: string;
|
|
64
|
+
factionId: string | null;
|
|
65
|
+
detachmentIds: string[];
|
|
66
|
+
battleSize: ShareBattleSize;
|
|
67
|
+
disposition: string | null;
|
|
68
|
+
units: ShareUnit[];
|
|
69
|
+
}
|
|
70
|
+
/** Outcome of {@link decodeShareList}: a list, or why it couldn't be read. */
|
|
71
|
+
export type DecodeResult = {
|
|
72
|
+
ok: true;
|
|
73
|
+
list: ShareList;
|
|
74
|
+
} | {
|
|
75
|
+
ok: false;
|
|
76
|
+
reason: "malformed" | "stale-registry";
|
|
77
|
+
};
|
|
78
|
+
/** Encode a {@link ShareList} into a URL-safe `share-v1` token. */
|
|
79
|
+
export declare function encodeShareList(list: ShareList, registry: ShareRegistryIndex): string;
|
|
80
|
+
/** Decode a `share-v1` token, or report why it can't be read. */
|
|
81
|
+
export declare function decodeShareList(token: string, registry: ShareRegistryIndex): DecodeResult;
|
|
82
|
+
export {};
|
|
83
|
+
//# sourceMappingURL=codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/share/codec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,EAAE,kBAAkB,EAAkB,MAAM,eAAe,CAAC;AAEnE,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,yCAAyC;AACzC,QAAA,MAAM,YAAY,wCAAyC,CAAC;AAC5D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,qDAAqD;AACrD,MAAM,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AAEnE,iFAAiF;AACjF,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,6EAA6E;IAC7E,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,0EAA0E;IAC1E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iFAAiF;IACjF,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kEAAkE;IAClE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAED,gFAAgF;AAChF,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,eAAe,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAED,8EAA8E;AAC9E,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,gBAAgB,CAAA;CAAE,CAAC;AAqH1D,mEAAmE;AACnE,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CA8CrF;AAWD,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,GAAG,YAAY,CAkEzF"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/** Current wire-format version. Bumped only if the byte layout changes. */
|
|
2
|
+
export const SHARE_FORMAT_VERSION = 1;
|
|
3
|
+
/** Battle sizes, encoded by position. */
|
|
4
|
+
const BATTLE_SIZES = ["incursion", "strike-force"];
|
|
5
|
+
// ── varint + buffer helpers ──────────────────────────────────────────────────
|
|
6
|
+
/** Growable byte sink with LEB128 + length-prefixed UTF-8 writers. */
|
|
7
|
+
class ByteWriter {
|
|
8
|
+
bytes = [];
|
|
9
|
+
enc = new TextEncoder();
|
|
10
|
+
byte(b) {
|
|
11
|
+
this.bytes.push(b & 0xff);
|
|
12
|
+
}
|
|
13
|
+
/** Unsigned LEB128. */
|
|
14
|
+
varint(value) {
|
|
15
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
16
|
+
throw new RangeError(`varint expects a non-negative integer, got ${value}`);
|
|
17
|
+
}
|
|
18
|
+
let v = value;
|
|
19
|
+
while (v >= 0x80) {
|
|
20
|
+
this.bytes.push((v & 0x7f) | 0x80);
|
|
21
|
+
v = Math.floor(v / 0x80);
|
|
22
|
+
}
|
|
23
|
+
this.bytes.push(v);
|
|
24
|
+
}
|
|
25
|
+
/** Length-prefixed UTF-8 string. */
|
|
26
|
+
str(s) {
|
|
27
|
+
const utf8 = this.enc.encode(s);
|
|
28
|
+
this.varint(utf8.length);
|
|
29
|
+
for (const b of utf8)
|
|
30
|
+
this.bytes.push(b);
|
|
31
|
+
}
|
|
32
|
+
toBytes() {
|
|
33
|
+
return Uint8Array.from(this.bytes);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Cursor over a byte buffer. Throws {@link MalformedError} past the end. */
|
|
37
|
+
class ByteReader {
|
|
38
|
+
bytes;
|
|
39
|
+
pos = 0;
|
|
40
|
+
dec = new TextDecoder("utf-8", { fatal: false });
|
|
41
|
+
constructor(bytes) {
|
|
42
|
+
this.bytes = bytes;
|
|
43
|
+
}
|
|
44
|
+
byte() {
|
|
45
|
+
if (this.pos >= this.bytes.length)
|
|
46
|
+
throw new MalformedError();
|
|
47
|
+
return this.bytes[this.pos++];
|
|
48
|
+
}
|
|
49
|
+
varint() {
|
|
50
|
+
let result = 0;
|
|
51
|
+
let shift = 1;
|
|
52
|
+
for (;;) {
|
|
53
|
+
const b = this.byte();
|
|
54
|
+
result += (b & 0x7f) * shift;
|
|
55
|
+
if ((b & 0x80) === 0)
|
|
56
|
+
return result;
|
|
57
|
+
shift *= 0x80;
|
|
58
|
+
if (shift > Number.MAX_SAFE_INTEGER)
|
|
59
|
+
throw new MalformedError();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
str() {
|
|
63
|
+
const len = this.varint();
|
|
64
|
+
if (this.pos + len > this.bytes.length)
|
|
65
|
+
throw new MalformedError();
|
|
66
|
+
const slice = this.bytes.subarray(this.pos, this.pos + len);
|
|
67
|
+
this.pos += len;
|
|
68
|
+
return this.dec.decode(slice);
|
|
69
|
+
}
|
|
70
|
+
atEnd() {
|
|
71
|
+
return this.pos >= this.bytes.length;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Internal: thrown when the byte stream is truncated or self-inconsistent. */
|
|
75
|
+
class MalformedError extends Error {
|
|
76
|
+
}
|
|
77
|
+
/** Internal: thrown when an index isn't in the decoder's registry. */
|
|
78
|
+
class StaleRegistryError extends Error {
|
|
79
|
+
}
|
|
80
|
+
// ── base64url (no padding) ────────────────────────────────────────────────────
|
|
81
|
+
function bytesToBase64url(bytes) {
|
|
82
|
+
let bin = "";
|
|
83
|
+
for (let i = 0; i < bytes.length; i += 0x8000) {
|
|
84
|
+
bin += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
|
|
85
|
+
}
|
|
86
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
87
|
+
}
|
|
88
|
+
function base64urlToBytes(token) {
|
|
89
|
+
const b64 = token.replace(/-/g, "+").replace(/_/g, "/");
|
|
90
|
+
const padded = b64 + "=".repeat((4 - (b64.length % 4)) % 4);
|
|
91
|
+
const bin = atob(padded);
|
|
92
|
+
const out = new Uint8Array(bin.length);
|
|
93
|
+
for (let i = 0; i < bin.length; i++)
|
|
94
|
+
out[i] = bin.charCodeAt(i);
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
// ── encode ────────────────────────────────────────────────────────────────────
|
|
98
|
+
const FLAG_WARLORD = 1 << 0;
|
|
99
|
+
const FLAG_ENH = 1 << 1;
|
|
100
|
+
const FLAG_ATTACH = 1 << 2;
|
|
101
|
+
const FLAG_ALLY = 1 << 3;
|
|
102
|
+
const FLAG_GRANTS = 1 << 4;
|
|
103
|
+
/** Resolve an id to its registry slot, or fail loudly (registry is behind). */
|
|
104
|
+
function requireIndex(idx, kind, id) {
|
|
105
|
+
const i = idx.index(kind, id);
|
|
106
|
+
if (i === undefined) {
|
|
107
|
+
throw new Error(`share registry has no ${kind} id "${id}" — run \`npm run registry:build\` and commit the result`);
|
|
108
|
+
}
|
|
109
|
+
return i;
|
|
110
|
+
}
|
|
111
|
+
/** Encode a {@link ShareList} into a URL-safe `share-v1` token. */
|
|
112
|
+
export function encodeShareList(list, registry) {
|
|
113
|
+
const w = new ByteWriter();
|
|
114
|
+
w.byte(SHARE_FORMAT_VERSION);
|
|
115
|
+
w.varint(registry.version);
|
|
116
|
+
w.str(list.name);
|
|
117
|
+
w.varint(list.factionId === null ? 0 : requireIndex(registry, "faction", list.factionId) + 1);
|
|
118
|
+
w.varint(BATTLE_SIZES.indexOf(list.battleSize) < 0 ? 0 : BATTLE_SIZES.indexOf(list.battleSize));
|
|
119
|
+
w.varint(list.disposition === null ? 0 : requireIndex(registry, "disposition", list.disposition) + 1);
|
|
120
|
+
w.varint(list.detachmentIds.length);
|
|
121
|
+
for (const id of list.detachmentIds)
|
|
122
|
+
w.varint(requireIndex(registry, "detachment", id));
|
|
123
|
+
w.varint(list.units.length);
|
|
124
|
+
for (const u of list.units) {
|
|
125
|
+
w.varint(requireIndex(registry, "unit", u.datasheetId));
|
|
126
|
+
w.varint(u.modelCount);
|
|
127
|
+
const flags = (u.isWarlord ? FLAG_WARLORD : 0) |
|
|
128
|
+
(u.enhancementId ? FLAG_ENH : 0) |
|
|
129
|
+
(u.attachedToOrdinal !== null ? FLAG_ATTACH : 0) |
|
|
130
|
+
(u.allyRuleId || u.allyFactionId ? FLAG_ALLY : 0) |
|
|
131
|
+
(u.grants.length > 0 ? FLAG_GRANTS : 0);
|
|
132
|
+
w.varint(flags);
|
|
133
|
+
if (u.enhancementId)
|
|
134
|
+
w.varint(requireIndex(registry, "enhancement", u.enhancementId));
|
|
135
|
+
if (u.attachedToOrdinal !== null)
|
|
136
|
+
w.varint(u.attachedToOrdinal);
|
|
137
|
+
if (flags & FLAG_ALLY) {
|
|
138
|
+
// An ally carries both its source faction and the rule it came in under;
|
|
139
|
+
// either may be absent in odd data, so encode 0 = none with +1 offset.
|
|
140
|
+
w.varint(u.allyFactionId === null ? 0 : requireIndex(registry, "faction", u.allyFactionId) + 1);
|
|
141
|
+
w.varint(u.allyRuleId === null ? 0 : requireIndex(registry, "ally_rule", u.allyRuleId) + 1);
|
|
142
|
+
}
|
|
143
|
+
if (u.grants.length > 0) {
|
|
144
|
+
w.varint(u.grants.length);
|
|
145
|
+
for (const g of u.grants)
|
|
146
|
+
w.str(g);
|
|
147
|
+
}
|
|
148
|
+
w.varint(u.loadout.length);
|
|
149
|
+
for (const [wid, count] of u.loadout) {
|
|
150
|
+
w.varint(requireIndex(registry, "wargear", wid));
|
|
151
|
+
w.varint(count);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return bytesToBase64url(w.toBytes());
|
|
155
|
+
}
|
|
156
|
+
// ── decode ─────────────────────────────────────────────────────────────────────
|
|
157
|
+
/** Look up a slot's id, treating an out-of-range slot as a stale registry. */
|
|
158
|
+
function requireId(idx, kind, slot) {
|
|
159
|
+
const id = idx.id(kind, slot);
|
|
160
|
+
if (id === undefined)
|
|
161
|
+
throw new StaleRegistryError();
|
|
162
|
+
return id;
|
|
163
|
+
}
|
|
164
|
+
/** Decode a `share-v1` token, or report why it can't be read. */
|
|
165
|
+
export function decodeShareList(token, registry) {
|
|
166
|
+
try {
|
|
167
|
+
const r = new ByteReader(base64urlToBytes(token));
|
|
168
|
+
if (r.byte() !== SHARE_FORMAT_VERSION)
|
|
169
|
+
return { ok: false, reason: "malformed" };
|
|
170
|
+
r.varint(); // registryVersion — informational; bounds checks below gate staleness.
|
|
171
|
+
const name = r.str();
|
|
172
|
+
const factionRef = r.varint();
|
|
173
|
+
const factionId = factionRef === 0 ? null : requireId(registry, "faction", factionRef - 1);
|
|
174
|
+
const battleSize = BATTLE_SIZES[r.varint()] ?? "strike-force";
|
|
175
|
+
const dispositionRef = r.varint();
|
|
176
|
+
const disposition = dispositionRef === 0 ? null : requireId(registry, "disposition", dispositionRef - 1);
|
|
177
|
+
const detCount = r.varint();
|
|
178
|
+
const detachmentIds = [];
|
|
179
|
+
for (let i = 0; i < detCount; i++) {
|
|
180
|
+
detachmentIds.push(requireId(registry, "detachment", r.varint()));
|
|
181
|
+
}
|
|
182
|
+
const unitCount = r.varint();
|
|
183
|
+
const units = [];
|
|
184
|
+
for (let i = 0; i < unitCount; i++) {
|
|
185
|
+
const datasheetId = requireId(registry, "unit", r.varint());
|
|
186
|
+
const modelCount = r.varint();
|
|
187
|
+
const flags = r.varint();
|
|
188
|
+
const enhancementId = flags & FLAG_ENH ? requireId(registry, "enhancement", r.varint()) : null;
|
|
189
|
+
const attachedToOrdinal = flags & FLAG_ATTACH ? r.varint() : null;
|
|
190
|
+
let allyFactionId = null;
|
|
191
|
+
let allyRuleId = null;
|
|
192
|
+
if (flags & FLAG_ALLY) {
|
|
193
|
+
const fRef = r.varint();
|
|
194
|
+
allyFactionId = fRef === 0 ? null : requireId(registry, "faction", fRef - 1);
|
|
195
|
+
const rRef = r.varint();
|
|
196
|
+
allyRuleId = rRef === 0 ? null : requireId(registry, "ally_rule", rRef - 1);
|
|
197
|
+
}
|
|
198
|
+
const grants = [];
|
|
199
|
+
if (flags & FLAG_GRANTS) {
|
|
200
|
+
const gCount = r.varint();
|
|
201
|
+
for (let g = 0; g < gCount; g++)
|
|
202
|
+
grants.push(r.str());
|
|
203
|
+
}
|
|
204
|
+
const loadout = [];
|
|
205
|
+
const lCount = r.varint();
|
|
206
|
+
for (let l = 0; l < lCount; l++) {
|
|
207
|
+
const wid = requireId(registry, "wargear", r.varint());
|
|
208
|
+
loadout.push([wid, r.varint()]);
|
|
209
|
+
}
|
|
210
|
+
units.push({
|
|
211
|
+
datasheetId,
|
|
212
|
+
modelCount,
|
|
213
|
+
isWarlord: Boolean(flags & FLAG_WARLORD),
|
|
214
|
+
enhancementId,
|
|
215
|
+
allyFactionId,
|
|
216
|
+
allyRuleId,
|
|
217
|
+
attachedToOrdinal,
|
|
218
|
+
grants,
|
|
219
|
+
loadout,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
return { ok: true, list: { name, factionId, detachmentIds, battleSize, disposition, units } };
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
if (e instanceof StaleRegistryError)
|
|
226
|
+
return { ok: false, reason: "stale-registry" };
|
|
227
|
+
return { ok: false, reason: "malformed" };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.js","sourceRoot":"","sources":["../../src/share/codec.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The `share-v1` compact list codec.\n *\n * A {@link ShareList} — the lossless essential subset of a roster — is packed\n * into unsigned LEB128 varints over registry indices (see {@link ShareRegistry})\n * and base64url-encoded. Entity ids become 1–3 byte integers instead of full\n * kebab-case strings, so a 2000-pt list fits in a few hundred URL-safe chars.\n *\n * The format is deliberately gzip-free: at list sizes the deflate header costs\n * more than it saves, and a raw varint buffer is trivially reproducible\n * byte-for-byte across the TS, Rust, and Python implementations (the\n * `conformance/share` corpus pins it). Free text (`name`, `grants`) rides along\n * as length-prefixed UTF-8 — the only incompressible part.\n *\n * Wire layout (all integers unsigned LEB128 unless noted):\n * ```\n * formatVersion 1 byte (= SHARE_FORMAT_VERSION)\n * registryVersion varint\n * name varint len + UTF-8 bytes\n * factionRef varint (0 = none; else faction index + 1)\n * battleSize varint (0 = incursion, 1 = strike-force)\n * dispositionRef varint (0 = none; else disposition index + 1)\n * detachments varint count, then [detachment index]...\n * units varint count, then per unit:\n * unitIdx varint\n * modelCount varint\n * flags varint bit0 warlord | bit1 enh | bit2 attach | bit3 ally | bit4 grants\n * [enhIdx] varint if enh\n * [attachOrd] varint if attach (ordinal into THIS unit list)\n * [allyFaction,allyRule] varint×2 if ally\n * [grants] varint count, then [varint len + UTF-8]... if grants\n * loadout varint count, then [wargearIdx varint, count varint]...\n * ```\n *\n * @packageDocumentation\n */\nimport { ShareRegistryIndex, type ShareKind } from \"./registry.js\";\n\n/** Current wire-format version. Bumped only if the byte layout changes. */\nexport const SHARE_FORMAT_VERSION = 1;\n\n/** Battle sizes, encoded by position. */\nconst BATTLE_SIZES = [\"incursion\", \"strike-force\"] as const;\nexport type ShareBattleSize = (typeof BATTLE_SIZES)[number];\n\n/** One weapon/wargear selection: id and how many. */\nexport type ShareLoadoutEntry = [wargearId: string, count: number];\n\n/** One unit in a {@link ShareList}. Mirrors the builder's per-row essentials. */\nexport interface ShareUnit {\n datasheetId: string;\n modelCount: number;\n isWarlord: boolean;\n enhancementId: string | null;\n /** Source faction id when this is an *allied* unit; null for own-faction. */\n allyFactionId: string | null;\n /** Allied-rule id this unit was included under; null when not an ally. */\n allyRuleId: string | null;\n /** Ordinal (into {@link ShareList.units}) of the bodyguard this leader joins. */\n attachedToOrdinal: number | null;\n /** Detachment keyword grants the player assigned to this unit. */\n grants: string[];\n loadout: ShareLoadoutEntry[];\n}\n\n/** The lossless, serializable essence of a list — what a share link carries. */\nexport interface ShareList {\n name: string;\n factionId: string | null;\n detachmentIds: string[];\n battleSize: ShareBattleSize;\n disposition: string | null;\n units: ShareUnit[];\n}\n\n/** Outcome of {@link decodeShareList}: a list, or why it couldn't be read. */\nexport type DecodeResult =\n | { ok: true; list: ShareList }\n | { ok: false; reason: \"malformed\" | \"stale-registry\" };\n\n// ── varint + buffer helpers ──────────────────────────────────────────────────\n\n/** Growable byte sink with LEB128 + length-prefixed UTF-8 writers. */\nclass ByteWriter {\n private bytes: number[] = [];\n private readonly enc = new TextEncoder();\n\n byte(b: number): void {\n this.bytes.push(b & 0xff);\n }\n\n /** Unsigned LEB128. */\n varint(value: number): void {\n if (!Number.isInteger(value) || value < 0) {\n throw new RangeError(`varint expects a non-negative integer, got ${value}`);\n }\n let v = value;\n while (v >= 0x80) {\n this.bytes.push((v & 0x7f) | 0x80);\n v = Math.floor(v / 0x80);\n }\n this.bytes.push(v);\n }\n\n /** Length-prefixed UTF-8 string. */\n str(s: string): void {\n const utf8 = this.enc.encode(s);\n this.varint(utf8.length);\n for (const b of utf8) this.bytes.push(b);\n }\n\n toBytes(): Uint8Array {\n return Uint8Array.from(this.bytes);\n }\n}\n\n/** Cursor over a byte buffer. Throws {@link MalformedError} past the end. */\nclass ByteReader {\n private pos = 0;\n private readonly dec = new TextDecoder(\"utf-8\", { fatal: false });\n constructor(private readonly bytes: Uint8Array) {}\n\n byte(): number {\n if (this.pos >= this.bytes.length) throw new MalformedError();\n return this.bytes[this.pos++];\n }\n\n varint(): number {\n let result = 0;\n let shift = 1;\n for (;;) {\n const b = this.byte();\n result += (b & 0x7f) * shift;\n if ((b & 0x80) === 0) return result;\n shift *= 0x80;\n if (shift > Number.MAX_SAFE_INTEGER) throw new MalformedError();\n }\n }\n\n str(): string {\n const len = this.varint();\n if (this.pos + len > this.bytes.length) throw new MalformedError();\n const slice = this.bytes.subarray(this.pos, this.pos + len);\n this.pos += len;\n return this.dec.decode(slice);\n }\n\n atEnd(): boolean {\n return this.pos >= this.bytes.length;\n }\n}\n\n/** Internal: thrown when the byte stream is truncated or self-inconsistent. */\nclass MalformedError extends Error {}\n/** Internal: thrown when an index isn't in the decoder's registry. */\nclass StaleRegistryError extends Error {}\n\n// ── base64url (no padding) ────────────────────────────────────────────────────\n\nfunction bytesToBase64url(bytes: Uint8Array): string {\n let bin = \"\";\n for (let i = 0; i < bytes.length; i += 0x8000) {\n bin += String.fromCharCode(...bytes.subarray(i, i + 0x8000));\n }\n return btoa(bin).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction base64urlToBytes(token: string): Uint8Array {\n const b64 = token.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padded = b64 + \"=\".repeat((4 - (b64.length % 4)) % 4);\n const bin = atob(padded);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n}\n\n// ── encode ────────────────────────────────────────────────────────────────────\n\nconst FLAG_WARLORD = 1 << 0;\nconst FLAG_ENH = 1 << 1;\nconst FLAG_ATTACH = 1 << 2;\nconst FLAG_ALLY = 1 << 3;\nconst FLAG_GRANTS = 1 << 4;\n\n/** Resolve an id to its registry slot, or fail loudly (registry is behind). */\nfunction requireIndex(idx: ShareRegistryIndex, kind: ShareKind, id: string): number {\n const i = idx.index(kind, id);\n if (i === undefined) {\n throw new Error(\n `share registry has no ${kind} id \"${id}\" — run \\`npm run registry:build\\` and commit the result`,\n );\n }\n return i;\n}\n\n/** Encode a {@link ShareList} into a URL-safe `share-v1` token. */\nexport function encodeShareList(list: ShareList, registry: ShareRegistryIndex): string {\n const w = new ByteWriter();\n w.byte(SHARE_FORMAT_VERSION);\n w.varint(registry.version);\n w.str(list.name);\n\n w.varint(list.factionId === null ? 0 : requireIndex(registry, \"faction\", list.factionId) + 1);\n w.varint(BATTLE_SIZES.indexOf(list.battleSize) < 0 ? 0 : BATTLE_SIZES.indexOf(list.battleSize));\n w.varint(\n list.disposition === null ? 0 : requireIndex(registry, \"disposition\", list.disposition) + 1,\n );\n\n w.varint(list.detachmentIds.length);\n for (const id of list.detachmentIds) w.varint(requireIndex(registry, \"detachment\", id));\n\n w.varint(list.units.length);\n for (const u of list.units) {\n w.varint(requireIndex(registry, \"unit\", u.datasheetId));\n w.varint(u.modelCount);\n const flags =\n (u.isWarlord ? FLAG_WARLORD : 0) |\n (u.enhancementId ? FLAG_ENH : 0) |\n (u.attachedToOrdinal !== null ? FLAG_ATTACH : 0) |\n (u.allyRuleId || u.allyFactionId ? FLAG_ALLY : 0) |\n (u.grants.length > 0 ? FLAG_GRANTS : 0);\n w.varint(flags);\n if (u.enhancementId) w.varint(requireIndex(registry, \"enhancement\", u.enhancementId));\n if (u.attachedToOrdinal !== null) w.varint(u.attachedToOrdinal);\n if (flags & FLAG_ALLY) {\n // An ally carries both its source faction and the rule it came in under;\n // either may be absent in odd data, so encode 0 = none with +1 offset.\n w.varint(u.allyFactionId === null ? 0 : requireIndex(registry, \"faction\", u.allyFactionId) + 1);\n w.varint(u.allyRuleId === null ? 0 : requireIndex(registry, \"ally_rule\", u.allyRuleId) + 1);\n }\n if (u.grants.length > 0) {\n w.varint(u.grants.length);\n for (const g of u.grants) w.str(g);\n }\n w.varint(u.loadout.length);\n for (const [wid, count] of u.loadout) {\n w.varint(requireIndex(registry, \"wargear\", wid));\n w.varint(count);\n }\n }\n\n return bytesToBase64url(w.toBytes());\n}\n\n// ── decode ─────────────────────────────────────────────────────────────────────\n\n/** Look up a slot's id, treating an out-of-range slot as a stale registry. */\nfunction requireId(idx: ShareRegistryIndex, kind: ShareKind, slot: number): string {\n const id = idx.id(kind, slot);\n if (id === undefined) throw new StaleRegistryError();\n return id;\n}\n\n/** Decode a `share-v1` token, or report why it can't be read. */\nexport function decodeShareList(token: string, registry: ShareRegistryIndex): DecodeResult {\n try {\n const r = new ByteReader(base64urlToBytes(token));\n if (r.byte() !== SHARE_FORMAT_VERSION) return { ok: false, reason: \"malformed\" };\n r.varint(); // registryVersion — informational; bounds checks below gate staleness.\n\n const name = r.str();\n const factionRef = r.varint();\n const factionId = factionRef === 0 ? null : requireId(registry, \"faction\", factionRef - 1);\n const battleSize = BATTLE_SIZES[r.varint()] ?? \"strike-force\";\n const dispositionRef = r.varint();\n const disposition =\n dispositionRef === 0 ? null : requireId(registry, \"disposition\", dispositionRef - 1);\n\n const detCount = r.varint();\n const detachmentIds: string[] = [];\n for (let i = 0; i < detCount; i++) {\n detachmentIds.push(requireId(registry, \"detachment\", r.varint()));\n }\n\n const unitCount = r.varint();\n const units: ShareUnit[] = [];\n for (let i = 0; i < unitCount; i++) {\n const datasheetId = requireId(registry, \"unit\", r.varint());\n const modelCount = r.varint();\n const flags = r.varint();\n const enhancementId =\n flags & FLAG_ENH ? requireId(registry, \"enhancement\", r.varint()) : null;\n const attachedToOrdinal = flags & FLAG_ATTACH ? r.varint() : null;\n let allyFactionId: string | null = null;\n let allyRuleId: string | null = null;\n if (flags & FLAG_ALLY) {\n const fRef = r.varint();\n allyFactionId = fRef === 0 ? null : requireId(registry, \"faction\", fRef - 1);\n const rRef = r.varint();\n allyRuleId = rRef === 0 ? null : requireId(registry, \"ally_rule\", rRef - 1);\n }\n const grants: string[] = [];\n if (flags & FLAG_GRANTS) {\n const gCount = r.varint();\n for (let g = 0; g < gCount; g++) grants.push(r.str());\n }\n const loadout: ShareLoadoutEntry[] = [];\n const lCount = r.varint();\n for (let l = 0; l < lCount; l++) {\n const wid = requireId(registry, \"wargear\", r.varint());\n loadout.push([wid, r.varint()]);\n }\n units.push({\n datasheetId,\n modelCount,\n isWarlord: Boolean(flags & FLAG_WARLORD),\n enhancementId,\n allyFactionId,\n allyRuleId,\n attachedToOrdinal,\n grants,\n loadout,\n });\n }\n\n return { ok: true, list: { name, factionId, detachmentIds, battleSize, disposition, units } };\n } catch (e) {\n if (e instanceof StaleRegistryError) return { ok: false, reason: \"stale-registry\" };\n return { ok: false, reason: \"malformed\" };\n }\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact, URL-safe list sharing for the 40kdc list builder.
|
|
3
|
+
*
|
|
4
|
+
* {@link encodeShareToken} packs a {@link ShareList} into a `share-v1` token
|
|
5
|
+
* (registry-indexed varints, base64url); {@link decodeShareToken} reverses it
|
|
6
|
+
* against the embedded {@link SHARE_REGISTRY}. The wire format and registry are
|
|
7
|
+
* documented in `docs/share-token.md` so other tools can interoperate.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import { encodeShareToken, decodeShareToken } from "@alpaca-software/40kdc-data";
|
|
11
|
+
*
|
|
12
|
+
* const token = encodeShareToken(myList); // → "AQ..." (URL-safe)
|
|
13
|
+
* const result = decodeShareToken(token);
|
|
14
|
+
* if (result.ok) useList(result.list);
|
|
15
|
+
*
|
|
16
|
+
* @packageDocumentation
|
|
17
|
+
*/
|
|
18
|
+
import { type DecodeResult, type ShareList } from "./codec.js";
|
|
19
|
+
export { SHARE_FORMAT_VERSION } from "./codec.js";
|
|
20
|
+
export type { DecodeResult, ShareBattleSize, ShareList, ShareLoadoutEntry, ShareUnit, } from "./codec.js";
|
|
21
|
+
export { SHARE_KINDS } from "./registry.js";
|
|
22
|
+
export type { ShareKind, ShareRegistry } from "./registry.js";
|
|
23
|
+
export { ShareRegistryIndex } from "./registry.js";
|
|
24
|
+
/** Registry version this package embeds (stamped into every token it writes). */
|
|
25
|
+
export declare const shareRegistryVersion: number;
|
|
26
|
+
/** Encode a list into a URL-safe `share-v1` token using the embedded registry. */
|
|
27
|
+
export declare function encodeShareToken(list: ShareList): string;
|
|
28
|
+
/** Decode a `share-v1` token using the embedded registry. */
|
|
29
|
+
export declare function decodeShareToken(token: string): DecodeResult;
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/share/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAoC,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAIjG,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,YAAY,EACV,YAAY,EACZ,eAAe,EACf,SAAS,EACT,iBAAiB,EACjB,SAAS,GACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAKnD,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,EAAE,MAA+B,CAAC;AAEnE,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAExD;AAED,6DAA6D;AAC7D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAE5D"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact, URL-safe list sharing for the 40kdc list builder.
|
|
3
|
+
*
|
|
4
|
+
* {@link encodeShareToken} packs a {@link ShareList} into a `share-v1` token
|
|
5
|
+
* (registry-indexed varints, base64url); {@link decodeShareToken} reverses it
|
|
6
|
+
* against the embedded {@link SHARE_REGISTRY}. The wire format and registry are
|
|
7
|
+
* documented in `docs/share-token.md` so other tools can interoperate.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import { encodeShareToken, decodeShareToken } from "@alpaca-software/40kdc-data";
|
|
11
|
+
*
|
|
12
|
+
* const token = encodeShareToken(myList); // → "AQ..." (URL-safe)
|
|
13
|
+
* const result = decodeShareToken(token);
|
|
14
|
+
* if (result.ok) useList(result.list);
|
|
15
|
+
*
|
|
16
|
+
* @packageDocumentation
|
|
17
|
+
*/
|
|
18
|
+
import { decodeShareList, encodeShareList } from "./codec.js";
|
|
19
|
+
import { SHARE_REGISTRY } from "./registry.generated.js";
|
|
20
|
+
import { ShareRegistryIndex } from "./registry.js";
|
|
21
|
+
export { SHARE_FORMAT_VERSION } from "./codec.js";
|
|
22
|
+
export { SHARE_KINDS } from "./registry.js";
|
|
23
|
+
export { ShareRegistryIndex } from "./registry.js";
|
|
24
|
+
/** The id index built once from the package's embedded registry. */
|
|
25
|
+
const embeddedIndex = new ShareRegistryIndex(SHARE_REGISTRY);
|
|
26
|
+
/** Registry version this package embeds (stamped into every token it writes). */
|
|
27
|
+
export const shareRegistryVersion = SHARE_REGISTRY.version;
|
|
28
|
+
/** Encode a list into a URL-safe `share-v1` token using the embedded registry. */
|
|
29
|
+
export function encodeShareToken(list) {
|
|
30
|
+
return encodeShareList(list, embeddedIndex);
|
|
31
|
+
}
|
|
32
|
+
/** Decode a `share-v1` token using the embedded registry. */
|
|
33
|
+
export function decodeShareToken(token) {
|
|
34
|
+
return decodeShareList(token, embeddedIndex);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/share/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAqC,MAAM,YAAY,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAQlD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,oEAAoE;AACpE,MAAM,aAAa,GAAG,IAAI,kBAAkB,CAAC,cAAc,CAAC,CAAC;AAE7D,iFAAiF;AACjF,MAAM,CAAC,MAAM,oBAAoB,GAAW,cAAc,CAAC,OAAO,CAAC;AAEnE,kFAAkF;AAClF,MAAM,UAAU,gBAAgB,CAAC,IAAe;IAC9C,OAAO,eAAe,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;AAC9C,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,eAAe,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;AAC/C,CAAC","sourcesContent":["/**\n * Compact, URL-safe list sharing for the 40kdc list builder.\n *\n * {@link encodeShareToken} packs a {@link ShareList} into a `share-v1` token\n * (registry-indexed varints, base64url); {@link decodeShareToken} reverses it\n * against the embedded {@link SHARE_REGISTRY}. The wire format and registry are\n * documented in `docs/share-token.md` so other tools can interoperate.\n *\n * @example\n * import { encodeShareToken, decodeShareToken } from \"@alpaca-software/40kdc-data\";\n *\n * const token = encodeShareToken(myList); // → \"AQ...\" (URL-safe)\n * const result = decodeShareToken(token);\n * if (result.ok) useList(result.list);\n *\n * @packageDocumentation\n */\nimport { decodeShareList, encodeShareList, type DecodeResult, type ShareList } from \"./codec.js\";\nimport { SHARE_REGISTRY } from \"./registry.generated.js\";\nimport { ShareRegistryIndex } from \"./registry.js\";\n\nexport { SHARE_FORMAT_VERSION } from \"./codec.js\";\nexport type {\n DecodeResult,\n ShareBattleSize,\n ShareList,\n ShareLoadoutEntry,\n ShareUnit,\n} from \"./codec.js\";\nexport { SHARE_KINDS } from \"./registry.js\";\nexport type { ShareKind, ShareRegistry } from \"./registry.js\";\nexport { ShareRegistryIndex } from \"./registry.js\";\n\n/** The id index built once from the package's embedded registry. */\nconst embeddedIndex = new ShareRegistryIndex(SHARE_REGISTRY);\n\n/** Registry version this package embeds (stamped into every token it writes). */\nexport const shareRegistryVersion: number = SHARE_REGISTRY.version;\n\n/** Encode a list into a URL-safe `share-v1` token using the embedded registry. */\nexport function encodeShareToken(list: ShareList): string {\n return encodeShareList(list, embeddedIndex);\n}\n\n/** Decode a `share-v1` token using the embedded registry. */\nexport function decodeShareToken(token: string): DecodeResult {\n return decodeShareList(token, embeddedIndex);\n}\n"]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The share-token id registry: the versioned, append-only dictionary that maps
|
|
3
|
+
* every shareable entity id to a stable integer index. The committed artifact
|
|
4
|
+
* lives at `data/share-registry.json`; {@link build-share-registry} regenerates
|
|
5
|
+
* it and `codegen:data` inlines it as `registry.generated.ts`.
|
|
6
|
+
*
|
|
7
|
+
* Indices never move (append-only), so a newer registry always decodes an older
|
|
8
|
+
* token. Renames are carried by {@link ShareRegistry.aliases} (old id → current
|
|
9
|
+
* id): the slot keeps the old id, the encoder also accepts the new id at that
|
|
10
|
+
* slot, and the decoder rewrites old → new on the way out. Removed ids sit in
|
|
11
|
+
* {@link ShareRegistry.tombstones} purely to document the retained slot.
|
|
12
|
+
*
|
|
13
|
+
* @packageDocumentation
|
|
14
|
+
*/
|
|
15
|
+
/** Registry kinds, in fixed order. The wire format references each by name. */
|
|
16
|
+
export declare const SHARE_KINDS: readonly ["faction", "detachment", "unit", "wargear", "enhancement", "ally_rule", "disposition"];
|
|
17
|
+
export type ShareKind = (typeof SHARE_KINDS)[number];
|
|
18
|
+
/** The committed registry artifact (mirrors `data/share-registry.json`). */
|
|
19
|
+
export interface ShareRegistry {
|
|
20
|
+
version: number;
|
|
21
|
+
kinds: Record<ShareKind, string[]>;
|
|
22
|
+
/** Rename map applied on decode: a retained (old) id → its current id. */
|
|
23
|
+
aliases: Record<string, string>;
|
|
24
|
+
/** Ids dropped from the dataset whose slots are retained for old tokens. */
|
|
25
|
+
tombstones: string[];
|
|
26
|
+
}
|
|
27
|
+
/** Bidirectional lookup over one registry, prepared once for encode/decode. */
|
|
28
|
+
export declare class ShareRegistryIndex {
|
|
29
|
+
readonly version: number;
|
|
30
|
+
/** Per-kind id → index, including reverse-alias entries (new id → old slot). */
|
|
31
|
+
private readonly toIndex;
|
|
32
|
+
/** Per-kind index → id, with aliases pre-applied (old slot → current id). */
|
|
33
|
+
private readonly fromIndex;
|
|
34
|
+
constructor(registry: ShareRegistry);
|
|
35
|
+
/** Slot for an id, or undefined if the registry doesn't know it (stale). */
|
|
36
|
+
index(kind: ShareKind, id: string): number | undefined;
|
|
37
|
+
/** Current id at a slot, or undefined if the slot is out of range (stale). */
|
|
38
|
+
id(kind: ShareKind, index: number): string | undefined;
|
|
39
|
+
/** Number of slots in a kind. */
|
|
40
|
+
size(kind: ShareKind): number;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/share/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,+EAA+E;AAC/E,eAAO,MAAM,WAAW,kGAQd,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IACnC,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,4EAA4E;IAC5E,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,+EAA+E;AAC/E,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyC;IACjE,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8B;gBAE5C,QAAQ,EAAE,aAAa;IAwBnC,4EAA4E;IAC5E,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAItD,8EAA8E;IAC9E,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAItD,iCAAiC;IACjC,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM;CAG9B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.generated.d.ts","sourceRoot":"","sources":["../../src/share/registry.generated.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAInD,qEAAqE;AACrE,eAAO,MAAM,cAAc,EAAE,aAAsD,CAAC"}
|