tigertag 1.0.6 → 1.2.0

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/CHANGELOG.md CHANGED
@@ -3,6 +3,193 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  Format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
5
 
6
+ ## [1.2.0] — 2026-09-30
7
+
8
+ ### Added
9
+ - **Unified playground**: `tools/playground.html` is now one page, byte-for-byte identical in the
10
+ JavaScript and Python SDKs. Each `tools/server.*` implements the same server contract
11
+ (`docs/playground-api.md`): `GET /api/version` (now `{ version, sdk, label, repo, install,
12
+ server, style }` — the page takes its SDK names, links and the `create()` code style from it),
13
+ `POST /api/parse` / `/api/build` (snake_case `create()` fields, `measure_available`) /
14
+ `/api/diff`, `GET /api/catalog/info` / `<id>`, `POST /api/catalog/refresh`, `GET /api/db/info`,
15
+ `POST /api/db/update`, `GET /api/db/table/<file>`, `GET /api/nfc/events` (Server-Sent Events,
16
+ chips already present replayed on connect), `POST /api/nfc/read` / `burn` / `plan`. The page
17
+ talks to the readers through SSE + HTTP on both servers (the JS server keeps its WebSocket transport for older pages; `TIGERTAG_PLAYGROUND_NO_NFC=1` starts it without readers).
18
+ `scripts/check_playground_sync.js` fails when the two copies differ (the local checkout next to
19
+ this one, else GitHub `main`); the test suite runs it and skips it when neither is reachable.
20
+ From the other SDK's page: gradient colour preview, read-only manufacturing date, refusal of an available quantity above the initial one, Studio Manager download buttons, error toasts, tables loaded from the server (`/api/db/table`).
21
+ - **Tag index / tag count** (protocol v2.2). Page 0x0D byte 3 (payload offset +39),
22
+ previously reserved padding, is now parsed and written as `tagInfo` (u8):
23
+ high nibble = tag index, which of the item's TigerTags this one is, from 1 (0 = unknown);
24
+ low nibble = tag count, how many TigerTags the item carries — a filament spool, a resin
25
+ bottle…, as given by idType (0 = unknown, 1 = single tag,
26
+ 2 = twin tag, … 15). The hex reads "index/count": `0x11` single tag, `0x12` / `0x22`
27
+ twin tag, tag 1 / 2 of 2, `0x02` twin tag with index unknown, `0x00` unknown.
28
+ - `tag.tagInfo`, and read-only getters `tag.tagCount` / `tag.tagIndex`.
29
+ - `TigerTag.create({ tagCount, tagIndex })` (default `0` = unknown); `asInit()` writes `0x00`.
30
+ Values outside 0–15 throw `RangeError`.
31
+ - `patch()` accepts `tagInfo`, `tagCount` and `tagIndex`; `patchFromRawDict()` /
32
+ `fromRawDict()` accept `tag_info`.
33
+ - `validate()` warns when tag index / count are outside 0–15, when index > count (count > 0),
34
+ and when count is 1 with index > 1.
35
+ - `toRawDict()` gains `tag_info`; `toDict()` gains `tag_count` / `tag_index`
36
+ (`null` when unknown) next to `twin_tag_pairing_id`; `pretty()` prints a `Tag` line
37
+ (`Tag 1 of 2`) after `Twin tag ID`; `describe()` adds "Tag 1 of 2 on this filament."
38
+ when known (the idType label, lowercased; "item" when the type is unknown).
39
+ - `tagInfo` is not covered by the ECDSA signature — setting it never invalidates a signed tag.
40
+
41
+ - Playground: "Tag index" / "Tag count" inputs (0–15, 0 = unknown) passed to
42
+ `TigerTag.create()` via `/api/build`; the client-side encoder / decoder handle byte +39;
43
+ the decoded view shows a `Tag` row after `Twin tag ID` ("1 of 2", "? of 2", "unknown");
44
+ the raw hex view labels page 13 byte 3 as the tag index / count (e.g. `(0x12) tag 1 of 2`);
45
+ imported `.bin` files and scanned chips fill the two inputs; presets and API loads reset them to 0.
46
+ - Playground server: `/api/parse` and the `card:detected` WebSocket event include
47
+ `validate` (the `validate()` warnings), shown in a new "Validation" card.
48
+ - Playground: an info bubble on "Tag index" and "Tag count" explains both values;
49
+ the repository list points to Tiger-Scale-V3 and adds TigerSpool-RFID, TigerPOD,
50
+ TigerSystem-Docs and TigerTag_Firebase_Integration.
51
+ - Playground ecosystem panel: photo cards for Tiger Scale V3, TigerSpool, TigerPOD Mini
52
+ and the mobile app.
53
+ - Playground: TigerTag favicon (`assets/favicon.svg`, with `assets/apple-touch-icon.png`).
54
+ - Playground SDK Input panel: `create()` | `HEX` | `Pages` tabs. `HEX` shows exactly what
55
+ Burn writes (144 bytes, pages 0x04–0x27, uppercase); `Pages` lists them page by page in
56
+ write order with the Raw Read annotations (page 0x0D byte 3 → `tag 1 of 2`). Copy copies
57
+ the active tab (`Pages` → one TSV line per page). The "→ Burn result" section is kept.
58
+ - Playground twin tag mode: when two or more readers hold a chip (sorted by name → #1, #2, …),
59
+ Tag count is locked to the number of chips and Tag index to "auto", with a note naming
60
+ each reader's tag ("#1 <reader> = tag 1 of 2"). Generate builds one payload per reader — same
61
+ data, same Timestamp (computed once in the page and passed to `create()`), tag index 1…n
62
+ (`0x12`, `0x22`) — and SDK Input shows
63
+ one section per reader. SDK Output gets `#1` / `#2` buttons to switch between the chips.
64
+ Burn asks for confirmation listing each reader, UID and tag, then writes each reader's own
65
+ payload; the "x/y written" counter and the burn result aggregate across the writes.
66
+ Loading a `.bin` or a scanned chip clears the twin plan.
67
+ - Playground server: `burn:write` accepts an optional `reader` field (a reader id / name, or an
68
+ array of them) to write to those readers only; without it every reader holding a chip is
69
+ written, as before (`tools/burn_targets.js`).
70
+ - Playground "Chips on the readers" card: for the chips currently on the readers, checks they
71
+ belong to the same item (same Timestamp), carry identical data apart from byte +39, and that
72
+ the tag numbering is complete (e.g. "Numbering complete: 1/2 + 2/2", "tag 2/2 not on a reader",
73
+ or "Tag index / count unknown" for tags written before protocol v2.2). The server replays the
74
+ last chip read on each reader to a newly connected page.
75
+
76
+ - Playground Read / Burn modes: a header switch "Read" (default) | "Burn".
77
+ Read shows only what the readers read (or an imported `.bin`), under a blue "Read result"
78
+ banner naming the reader and UID (or the file), with the "Chips on the readers" card; Burn
79
+ is disabled and twin tag locking is off. Burn shows only what Burn will write, under an
80
+ orange "Burn preview — not written yet" banner listing the target readers (and each one's
81
+ tag i of n); a chip placed on a reader never replaces the preview, and Burn writes the
82
+ stored preview, never the last payload read. Generate switches to Burn, Import .bin to Read;
83
+ each mode keeps its last view. In Read mode, removing the chip whose result is shown switches
84
+ to another chip still on a reader, or clears the view (an imported `.bin` stays). Reader
85
+ events start only after the reference database has loaded.
86
+
87
+ - Playground burn never writes a signature and never leaves a stale one: `burn:write` always
88
+ writes pages 0x04–0x27 (36 pages) — the tag data on 0x04–0x17, then `00 00 00 00` on every
89
+ signature page 0x18–0x27, whatever the payload (TigerTag, TigerTag+, or data read back from a
90
+ signed chip). Only a certified manufacturer can issue a signature, and a copied one would be
91
+ invalid since it covers the chip UID; the playgrounds only read signatures to verify them.
92
+ Pages 0–3 and 0x28+ are never touched. The page plan lives in `tools/burn_plan.js`
93
+ (unit-tested; accepts 80 or 144 bytes); `burn:result` reports `pagesWritten: 36` and
94
+ `signatureDropped` when the payload carried a signature. The playground sends the full
95
+ 144-byte image, the HEX / Pages tabs show all 36 pages with the zeroed signature, and the
96
+ caption and confirmation say so ("the signature read from the chip is not copied" when the
97
+ data came from a signed chip).
98
+
99
+ - No emoji anywhere: `SignatureResult` labels are plain words (`VALID`, `INVALID`, `NOT SIGNED`,
100
+ `NO PUBLIC KEY — …`, `NO UID — …`, `NO CRYPTO — …`), `pretty()` prints `signed (not verified)`
101
+ instead of a check mark, and the scripts print `OK` / `FAILED`. The playground uses small inline
102
+ SVG icons (read, burn, raw read, upload, download, play, check, x, warning, cloud, hourglass,
103
+ close, refresh, external link…) in buttons, banners, tabs, badges and cards, and plain words in
104
+ tooltips, confirmation dialogs and console logs. The README, llms.txt and the SVG badges no
105
+ longer use emoji either.
106
+
107
+ - **TigerTag+ from the official catalogue** (`src/catalog.js`): `TigerTag.fromCatalog(productId,
108
+ { uid, tagCount, tagIndex, timestamp, db, catalog })` builds a ready-to-burn TigerTag+ from
109
+ `id_catalog.json` (14 000+ products); `TigerTag.fromCatalogEntry(entry, options)` does it from an
110
+ entry; `catalogEntry(productId)` returns the display metadata (title, brand, SKU, barcode, image).
111
+ RFID_Data mapping: `data1` = diameter, `data2`/`data3` = nozzle min/max, `data4`/`data5` = dry
112
+ temp/time, `data6`/`data7` = bed min/max, `id_aspect2` null → 0, colours 2/3 from `color_r2…b3`
113
+ or `color_info.colors`. The catalogue (~12 MB) is not bundled: `loadCatalog({ url, cacheDir,
114
+ maxAge, force })` downloads it on first use and caches it in the user cache folder (override:
115
+ `TIGERTAG_CACHE_DIR`), checks for a new version once the copy is older than 1 day (`maxAge`), falls back to the cached copy when offline and fails with a clear error
116
+ when there is none. It changes every day: `refreshCatalog()` checks for a new version now
117
+ (conditional download with ETag / Last-Modified — an unchanged catalogue answers 304) and
118
+ `catalogInfo()` reports `downloaded`, `count`, `fetchedAt`, `checkedAt`, `url` and `etag`.
119
+ - Playground: one "Load a TigerTag+ product" block — a single Product ID field, a source toggle
120
+ "Offline · Catalogue" (bundled / cached catalogue, no internet) | "Online · API" (live
121
+ api.tigertag.io data), remembered in the browser, and one Load button; both sources fill the
122
+ form, switch to Burn and build the preview (twin tag with two readers), share one message area
123
+ and suggest the other source when a product is not found / the API is unreachable.
124
+ - Playground: decluttered — on screen only short labels, values, buttons and status words
125
+ ("Catalogue · 14 164 · 1 oct.", "Tables · 25 sept.", "Burn preview · not written · twin tag ·
126
+ 2 chips"); every explanation moved into info bubbles (TigerTag+ intro, data sources, catalogue
127
+ and tables details, Read / Burn banners, HEX / Pages captions, "Chips on the readers" checks,
128
+ field hints, twin tag readers, Init, demo presets). Bubbles are placed to stay inside their
129
+ panel and the viewport.
130
+ - Playground: "Load from catalogue" in the TigerTag+ tab — a product ID fills the whole form from
131
+ the catalogue, shows title, brand, SKU and image, switches to Burn and builds the preview (twin
132
+ tag with two readers); "Update catalogue" downloads the latest version; a status line shows
133
+ "Catalogue: 14 164 products · updated <date>" or "not downloaded yet". Server endpoints
134
+ `GET /api/catalog/<id>`, `GET /api/catalog/info`, `POST /api/catalog/refresh`.
135
+
136
+ ### Fixed
137
+ - Playground: Generate now passes an explicit Timestamp to `create()`, so the decoded view
138
+ shows the real Twin tag ID and manufacturing date instead of `null` / 2000-01-01.
139
+ - Playground: the `create()` call shown in SDK Input is the exact call sent to `/api/build`
140
+ (inactive color 2 / 3 slots are no longer sent while hidden from the displayed call), so
141
+ the text shown always reproduces the bytes.
142
+
143
+ - **Reference data always available offline, kept fresh automatically**: the product catalogue
144
+ ships in the package as `database/id_catalog.json.gz` (~1 MB, gunzipped at load; package
145
+ 1.1 MB) next to the 7 tables. `TigerTagDB` picks, per table, the newest of the downloaded copy
146
+ in the data dir (`dataDir`, `TIGERTAG_DATA_DIR`, default: the per-user cache folder shared
147
+ with the catalogue) and the bundled copy. `await TigerTagDB.open()` / the constructor's
148
+ background check (`db.ready`) look for new tables at most once a day (one request to the
149
+ TigerTag API, GitHub mirror as fallback, only the changed tables downloaded, ~5 s timeout,
150
+ never throws). `offline: true` / `TIGERTAG_OFFLINE=1` / CLI `--offline` mean zero network
151
+ calls; `autoUpdate: false` disables only the automatic check (`autoSync` is a deprecated
152
+ alias). `await db.update({ force, catalog })` forces it and returns the changed files;
153
+ `db.info()` reports where every table comes from (custom / downloaded / bundled), the last
154
+ check, the data dir, the offline flag and the catalogue. CLI: `tigertag update [--force]
155
+ [--catalog] [--data-dir PATH | --db PATH]`. The catalogue loader shares the data dir, the
156
+ offline switch and the bundled `.gz` fallback, so `TigerTag.fromCatalog()` works offline.
157
+ - Playground: "Update reference tables" button with a status line ("Reference tables: 7 (n
158
+ downloaded, n bundled) · data <date> · checked <date>"), server endpoints `GET /api/db/info`
159
+ and `POST /api/db/update`; the catalogue status line names the bundled copy.
160
+ - Playground: Read mode shows no SDK Input panel and Burn mode no SDK Output panel at all (not
161
+ even the fold rail) — three columns instead of four; switching mode opens the visible panel.
162
+ - Release pipeline: `scripts/sync_databases.js` also refreshes `database/id_catalog.json.gz`
163
+ (only when its content changed); the daily `sync-databases.yml` commits it and `publish.yml`
164
+ runs the sync (then the tests) before `npm publish`, so every release ships the day's data.
165
+
166
+ ### Changed
167
+ - Protocol version is now **TigerTag Open Source v2.2** (backward compatible: tags written
168
+ before v2.2 read `0x00` = unknown).
169
+ - `toBytes()` writes `tagInfo` at +39 instead of a hard-coded `0x00`.
170
+ - **Behaviour change**: a custom `dbPath` is used exclusively — a missing table file now throws
171
+ a clear error instead of silently falling back to the bundled copy; `new TigerTagDB()` now
172
+ checks for updates once a day in the background (into the data dir — never into the package
173
+ folder; disable with `autoUpdate: false` or `offline: true`); `db.sync()` is an alias of
174
+ `db.update()`, and `tag.syncDb()` / `tigertag --sync-only` without a folder update the data dir
175
+ instead of rewriting the bundled `database/` folder.
176
+
177
+ ## [1.1.0] — 2026-07-10
178
+
179
+ ### Changed
180
+ - **License changed from GPLv3 to Apache-2.0.** The TigerTag protocol
181
+ specification is now published as an open standard: CC-BY-4.0 for the
182
+ specification, CC0-1.0 for the reference database, Apache-2.0 for code, with an
183
+ irrevocable, worldwide, royalty-free right to implement it in any product, open
184
+ source or proprietary. Apache-2.0 carries an express patent grant.
185
+ See <https://github.com/TigerTag-Project/TigerTag-RFID-Guide/blob/main/LICENSING.md>.
186
+
187
+ ### Fixed
188
+ - `package-lock.json` was left at `1.0.1` while `package.json` declared `1.0.6`.
189
+
190
+ > Versions published to npm up to and including `1.0.6` remain under GPLv3.
191
+ > This change applies from `1.1.0` onward.
192
+
6
193
  ## [1.0.6] — 2026-05-22
7
194
 
8
195
  ### Fixed
@@ -57,7 +244,7 @@ Format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
57
244
  - Playground: **Available qty auto-link** — the Available Qty field automatically mirrors Initial
58
245
  Qty until the user manually edits it. Link is restored on preset load, API fetch, or NFC scan.
59
246
  Removes the old "(0 = same as initial)" convention.
60
- - Playground: **Raw Hex Reader** (`🔬 Raw Read` button) — reads all 144 bytes (pages 4–39) from
247
+ - Playground: **Raw Hex Reader** (`Raw Read` button) — reads all 144 bytes (pages 4–39) from
61
248
  every connected reader that holds a card and displays them in a structured table: page number,
62
249
  byte offset, four individual hex bytes (B0–B3), big-endian u32 decimal, and field label. The
63
250
  signature pages (24–39) are visually dimmed and preceded by a separator row.
@@ -66,7 +253,7 @@ Format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66
253
  displayed side-by-side (flex row). Panels use the same rail UX as SDK Input / Output.
67
254
  - **Copy hex** button per panel — copies one line per page (`0x04 B0 B1 B2 B3`) to the clipboard.
68
255
  Includes page hex prefix on each line for direct cross-reference with NFC documentation.
69
- Button shows `✓ Copied!` (green, 1.5 s) after a successful copy so the user gets clear feedback.
256
+ Button shows `Copied` (green, 1.5 s) after a successful copy so the user gets clear feedback.
70
257
  - **Annotated Field column** — each field cell now shows decoded values inline:
71
258
  `(value) field_name · (value) field_name · …`. Values are read directly from the raw bytes
72
259
  (no extra server round-trip). customMessage pages show the decoded ASCII chars `("azer")`.
@@ -105,7 +292,7 @@ Format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
105
292
  Works for TigerTag+ chips only (filament / resin types). Cache-busted with
106
293
  `v=<timestamp>` on each call. `toRawDict()` and `toDict()` now include an
107
294
  `img` field exposing all URLs.
108
- - Playground: **Burn** button (`🔥 Burn`) — writes the generated payload to every
295
+ - Playground: **Burn** button (`Burn`) — writes the generated payload to every
109
296
  connected ACR122U / PC-SC reader that currently holds a card. Writes 20 pages
110
297
  (pages 4–23, 80 bytes) sequentially via `reader.write()`. Result reported per
111
298
  reader via WS (`burn:result`) with success/error detail; `burn:done` signals
package/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2025-2026 TigerTag Corp.
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
202
+