tileflow 0.1.0-alpha.1 → 0.1.0-alpha.3
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/README.md +40 -0
- package/dist/index.js +370 -14
- package/package.json +9 -10
- package/GENERATED_OUTPUT_LICENSE.md +0 -21
- package/NOTICE +0 -9
- package/TRADEMARKS.md +0 -10
package/README.md
CHANGED
|
@@ -131,6 +131,41 @@ Each manifest command entry declares its `outputKind`, `outputVersion`, and stab
|
|
|
131
131
|
`outputSchemaRef`. `raw-authoring-manifest` and `raw-config-reference` identify the two bootstrap
|
|
132
132
|
documents above; `command-envelope` identifies the ordinary version-1 CLI contract.
|
|
133
133
|
|
|
134
|
+
## Coordinates
|
|
135
|
+
|
|
136
|
+
These local commands require an explicitly provided compatible runtime distribution. No public
|
|
137
|
+
native runtime download is currently offered; package installation or the default setup URL does
|
|
138
|
+
not supply one. The verified production profile is Linux x64 on Debian 12, glibc 2.36 and Node 24.
|
|
139
|
+
The examples below use development artifacts and do not establish public platform support.
|
|
140
|
+
|
|
141
|
+
Coordinates commands use a locally provisioned execution release. They do not use Map
|
|
142
|
+
configuration, account state, or a Hosted fallback. Provision first from a distribution source, then
|
|
143
|
+
run the local commands against the active cache entry or an explicit runtime directory.
|
|
144
|
+
|
|
145
|
+
```sh
|
|
146
|
+
tileflow setup coordinates --source /path/to/distribution.json --cache-dir /path/to/coordinates-cache --development --json
|
|
147
|
+
tileflow coordinates search --query ETRS89 --runtime-dir /path/to/installed-release --development --json
|
|
148
|
+
tileflow coordinates describe --id EPSG:25830 --runtime-dir /path/to/installed-release --development --json
|
|
149
|
+
tileflow coordinates operations --from EPSG:4258 --to EPSG:25832 --runtime-dir /path/to/installed-release --development --json
|
|
150
|
+
tileflow coordinates transform --from EPSG:4258 --to EPSG:25832 --positions '[[12,55]]' --runtime-dir /path/to/installed-release --development --json
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`setup coordinates --source` accepts a distribution URL, a local `distribution.json`, or a directory
|
|
154
|
+
that contains one. `--archive` is a local archive alias and rejects URLs. Setup verifies every asset
|
|
155
|
+
declared by the selected distribution before it records the active release. `--development` is
|
|
156
|
+
required for a development distribution.
|
|
157
|
+
|
|
158
|
+
Every execution command accepts `--runtime-dir`, `--release`, and `--development`. With no
|
|
159
|
+
`--runtime-dir`, it opens the cache entry selected by setup. `search` accepts `--query`, `describe`
|
|
160
|
+
accepts `--id`, `operations` accepts `--from` and `--to`, and `transform` also accepts JSON
|
|
161
|
+
`--positions`.
|
|
162
|
+
|
|
163
|
+
For a complete request, use exactly one of `--request '<json>'` or `--input <file|->`. These forms
|
|
164
|
+
cannot be combined with the command's convenience request flags. Input is limited to 32 KiB, must be
|
|
165
|
+
valid UTF-8 JSON, and `-` reads standard input. Success writes one JSON document to stdout. A
|
|
166
|
+
structured failure writes one JSON document to stderr and exits with status 1. Plain `--help` remains
|
|
167
|
+
human-readable; `--help --json` is a structured failure.
|
|
168
|
+
|
|
134
169
|
`tileflow explain` compiles exactly one map/theme selection through `createStyleResult()` and emits
|
|
135
170
|
its structured `report` and safe compiler `diagnostics`; it deliberately omits the usually large
|
|
136
171
|
MapLibre `style`. In a singular config, `--map` is optional; a multi-map workbench requires it.
|
|
@@ -490,6 +525,11 @@ npx tileflow status --map-id map_AbCdEfGhIjKlMnOp
|
|
|
490
525
|
npx tileflow logout
|
|
491
526
|
```
|
|
492
527
|
|
|
528
|
+
Changing or resetting the hosted account password revokes saved personal CLI sessions. The next
|
|
529
|
+
account or hosted command stops with a clear message and asks you to run `tileflow login` again.
|
|
530
|
+
Project API and CI keys are independent project credentials and are not removed by a personal
|
|
531
|
+
password change.
|
|
532
|
+
|
|
493
533
|
The dashboard creates the Map and browser-access policy before repository setup. Keep that exact
|
|
494
534
|
Map ID in the deploy action. If the config contains several maps, select the authored map explicitly:
|
|
495
535
|
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { hostname } from "os";
|
|
|
10
10
|
import { dirname as dirname10, isAbsolute as isAbsolute4, relative as relative5, resolve as resolve7, sep as sep5 } from "path";
|
|
11
11
|
import { createInterface } from "readline/promises";
|
|
12
12
|
import pc3 from "picocolors";
|
|
13
|
+
import { createLocalCoordinates, setupCoordinates } from "@tileflow/coordinates-runtime";
|
|
13
14
|
import {
|
|
14
15
|
auditTileflowMapThemeValues,
|
|
15
16
|
getTileflowStyleFontFaces,
|
|
@@ -64,6 +65,7 @@ import { chmod, mkdir, open, readFile, rename, unlink } from "fs/promises";
|
|
|
64
65
|
import { homedir } from "os";
|
|
65
66
|
import { dirname, resolve } from "path";
|
|
66
67
|
import { z } from "zod";
|
|
68
|
+
var rejectedAccountSessionMessage = "The saved Tileflow account session is no longer valid. Run tileflow login to authorize this machine again.";
|
|
67
69
|
var maximumAuthFileBytes = 1024 * 1024;
|
|
68
70
|
var maximumOrigins = 16;
|
|
69
71
|
var accountSchema = z.object({
|
|
@@ -1588,6 +1590,353 @@ function printHumanConfigInspection(inspection) {
|
|
|
1588
1590
|
console.log("Run tileflow inspect --json for the complete machine-readable result.");
|
|
1589
1591
|
}
|
|
1590
1592
|
|
|
1593
|
+
// src/coordinates-command.ts
|
|
1594
|
+
import { lstat as lstat2, open as open3 } from "fs/promises";
|
|
1595
|
+
import { CoordinatesSetupError } from "@tileflow/coordinates-runtime";
|
|
1596
|
+
import {
|
|
1597
|
+
CoordinatesContractError,
|
|
1598
|
+
coordinatesFailureSchema,
|
|
1599
|
+
coordinatesLimits,
|
|
1600
|
+
parseCoordinatesJsonRequest,
|
|
1601
|
+
parseCoordinatesRequest
|
|
1602
|
+
} from "@tileflow/coordinates/contract";
|
|
1603
|
+
var CoordinatesInputError = class extends Error {
|
|
1604
|
+
constructor(reason) {
|
|
1605
|
+
super("Coordinates input could not be read.");
|
|
1606
|
+
this.reason = reason;
|
|
1607
|
+
}
|
|
1608
|
+
reason;
|
|
1609
|
+
};
|
|
1610
|
+
function registerCoordinatesCommands(program2, dependencies) {
|
|
1611
|
+
const setup = program2.commands.find((command2) => command2.name() === "setup") ?? program2.command("setup").description("Provision local Tileflow tooling");
|
|
1612
|
+
configureCommand(
|
|
1613
|
+
setup.command("coordinates").description("Provision an offline Coordinates execution release").option("--source <url-or-path>", "distribution manifest URL or offline path").option("--archive <path>", "offline archive path alias").option("--cache-dir <path>", "Coordinates cache directory").option("--release <release-id>", "required Coordinates release identity").option("--development", "allow a development distribution").option("--json", "print one JSON receipt")
|
|
1614
|
+
).action(async (options, command2) => {
|
|
1615
|
+
if (options.help) {
|
|
1616
|
+
printHelp(command2, options.json, () => printSetupFailure());
|
|
1617
|
+
return;
|
|
1618
|
+
}
|
|
1619
|
+
if (command2.args.length > 0 || options.source && options.archive || options.archive && isRemoteLocation(options.archive)) {
|
|
1620
|
+
printSetupFailure(new CoordinatesSetupError("COORDINATES_SETUP_INVALID_REQUEST"));
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
const controller = (dependencies.createSignalController ?? installSignalAbortController)();
|
|
1624
|
+
try {
|
|
1625
|
+
const receipt = await dependencies.setupCoordinates({
|
|
1626
|
+
source: options.archive ?? options.source,
|
|
1627
|
+
cacheDirectory: options.cacheDir,
|
|
1628
|
+
requiredReleaseId: options.release,
|
|
1629
|
+
allowDevelopment: options.development,
|
|
1630
|
+
signal: controller.signal
|
|
1631
|
+
});
|
|
1632
|
+
printDocument(receipt);
|
|
1633
|
+
} catch (error) {
|
|
1634
|
+
printSetupFailure(error);
|
|
1635
|
+
} finally {
|
|
1636
|
+
controller.close();
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
const coordinates = program2.command("coordinates").description("Inspect and execute Coordinates requests using a provisioned local release");
|
|
1640
|
+
configureCommand(coordinates).option("--json", "print one JSON response");
|
|
1641
|
+
coordinates.action((options, command2) => {
|
|
1642
|
+
if (options.help) {
|
|
1643
|
+
printHelp(
|
|
1644
|
+
command2,
|
|
1645
|
+
options.json,
|
|
1646
|
+
() => printFailure(invalidRequest(null, "INVALID_VALUE", { path: ["command"] }))
|
|
1647
|
+
);
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
const reason = command2.args.length === 0 ? "INVALID_VALUE" : "UNKNOWN_FIELD";
|
|
1651
|
+
printFailure(invalidRequest(null, reason, { path: ["command"] }));
|
|
1652
|
+
});
|
|
1653
|
+
registerOperation(
|
|
1654
|
+
coordinates,
|
|
1655
|
+
"search",
|
|
1656
|
+
dependencies,
|
|
1657
|
+
(options) => parseRequest("search", options, { query: options.query })
|
|
1658
|
+
);
|
|
1659
|
+
registerOperation(
|
|
1660
|
+
coordinates,
|
|
1661
|
+
"describe",
|
|
1662
|
+
dependencies,
|
|
1663
|
+
(options) => parseRequest("describe", options, { id: options.id })
|
|
1664
|
+
);
|
|
1665
|
+
registerOperation(
|
|
1666
|
+
coordinates,
|
|
1667
|
+
"operations",
|
|
1668
|
+
dependencies,
|
|
1669
|
+
(options) => parseRequest("operations", options, { from: options.from, to: options.to })
|
|
1670
|
+
);
|
|
1671
|
+
registerOperation(
|
|
1672
|
+
coordinates,
|
|
1673
|
+
"transform",
|
|
1674
|
+
dependencies,
|
|
1675
|
+
(options) => parseTransformRequest(options)
|
|
1676
|
+
);
|
|
1677
|
+
}
|
|
1678
|
+
function registerOperation(coordinates, name, dependencies, parse3) {
|
|
1679
|
+
const command2 = configureCommand(
|
|
1680
|
+
coordinates.command(name).description(`Run the local Coordinates ${name} operation`).option("--request <json>", "complete JSON request").option("--input <path-or-dash>", "complete JSON request file, or - for standard input").option("--runtime-dir <path>", "prepared Coordinates runtime directory").option("--release <release-id>", "required Coordinates release identity").option("--development", "allow a development runtime").option("--json", "print one JSON response")
|
|
1681
|
+
);
|
|
1682
|
+
if (name === "search") command2.option("--query <text>", "search query");
|
|
1683
|
+
if (name === "describe") command2.option("--id <crs-id>", "CRS identifier");
|
|
1684
|
+
if (name === "operations" || name === "transform") {
|
|
1685
|
+
command2.option("--from <crs-id>", "source CRS identifier").option("--to <crs-id>", "target CRS identifier");
|
|
1686
|
+
}
|
|
1687
|
+
if (name === "transform") command2.option("--positions <json>", "JSON coordinate positions");
|
|
1688
|
+
command2.action(async (options, invoked) => {
|
|
1689
|
+
const parent = invoked.parent?.opts();
|
|
1690
|
+
const wantsHelp = options.help ?? parent?.help;
|
|
1691
|
+
const wantsJson = options.json ?? parent?.json;
|
|
1692
|
+
if (wantsHelp) {
|
|
1693
|
+
printHelp(
|
|
1694
|
+
invoked,
|
|
1695
|
+
wantsJson,
|
|
1696
|
+
() => printFailure(invalidRequest(name, "INVALID_VALUE", { path: ["command"] }))
|
|
1697
|
+
);
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
if (invoked.args.length > 0) {
|
|
1701
|
+
printFailure(invalidRequest(name, "UNKNOWN_FIELD", { path: ["options"] }));
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
const controller = (dependencies.createSignalController ?? installSignalAbortController)();
|
|
1705
|
+
let local;
|
|
1706
|
+
try {
|
|
1707
|
+
const request = await resolveRequest(
|
|
1708
|
+
name,
|
|
1709
|
+
options,
|
|
1710
|
+
parse3,
|
|
1711
|
+
dependencies.readInput,
|
|
1712
|
+
controller.signal
|
|
1713
|
+
);
|
|
1714
|
+
local = await dependencies.createLocalCoordinates({
|
|
1715
|
+
directory: options.runtimeDir,
|
|
1716
|
+
requiredReleaseId: options.release,
|
|
1717
|
+
allowDevelopment: options.development,
|
|
1718
|
+
signal: controller.signal
|
|
1719
|
+
});
|
|
1720
|
+
const response = await local[name](request, { signal: controller.signal });
|
|
1721
|
+
await local.close();
|
|
1722
|
+
local = void 0;
|
|
1723
|
+
printDocument(response);
|
|
1724
|
+
} catch (error) {
|
|
1725
|
+
printFailure(error, name);
|
|
1726
|
+
} finally {
|
|
1727
|
+
try {
|
|
1728
|
+
await local?.close();
|
|
1729
|
+
} catch {
|
|
1730
|
+
} finally {
|
|
1731
|
+
controller.close();
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
});
|
|
1735
|
+
}
|
|
1736
|
+
function configureCommand(command2) {
|
|
1737
|
+
return command2.allowUnknownOption(true).allowExcessArguments(true).helpOption(false).option("-h, --help", "display help");
|
|
1738
|
+
}
|
|
1739
|
+
async function resolveRequest(command2, options, parse3, readInput = readCoordinatesInput, signal) {
|
|
1740
|
+
const rawRequest = options.request !== void 0 || options.input !== void 0;
|
|
1741
|
+
if (options.request !== void 0 && options.input !== void 0 || rawRequest && hasConvenienceOptions(command2, options)) {
|
|
1742
|
+
throw invalidRequest(command2, "INVALID_VALUE", { path: ["request"] });
|
|
1743
|
+
}
|
|
1744
|
+
if (options.request !== void 0) return parseCoordinatesJsonRequest(command2, options.request);
|
|
1745
|
+
if (options.input !== void 0) {
|
|
1746
|
+
try {
|
|
1747
|
+
const input = await readInput(options.input, signal ?? new AbortController().signal);
|
|
1748
|
+
if (signal?.aborted) throw cancelledRequest(command2);
|
|
1749
|
+
return parseCoordinatesJsonRequest(command2, input);
|
|
1750
|
+
} catch (error) {
|
|
1751
|
+
if (signal?.aborted) throw cancelledRequest(command2);
|
|
1752
|
+
if (error instanceof CoordinatesInputError) throw invalidRequest(command2, error.reason);
|
|
1753
|
+
throw error;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
return parse3(options);
|
|
1757
|
+
}
|
|
1758
|
+
function hasConvenienceOptions(command2, options) {
|
|
1759
|
+
if (command2 === "search") return options.query !== void 0;
|
|
1760
|
+
if (command2 === "describe") return options.id !== void 0;
|
|
1761
|
+
if (command2 === "operations") {
|
|
1762
|
+
const pair = options;
|
|
1763
|
+
return pair.from !== void 0 || pair.to !== void 0;
|
|
1764
|
+
}
|
|
1765
|
+
const transform = options;
|
|
1766
|
+
return transform.from !== void 0 || transform.to !== void 0 || transform.positions !== void 0;
|
|
1767
|
+
}
|
|
1768
|
+
function parseRequest(command2, options, request) {
|
|
1769
|
+
return parseCoordinatesRequest(command2, request);
|
|
1770
|
+
}
|
|
1771
|
+
function parseTransformRequest(options) {
|
|
1772
|
+
let positions;
|
|
1773
|
+
try {
|
|
1774
|
+
positions = options.positions === void 0 ? void 0 : JSON.parse(options.positions);
|
|
1775
|
+
} catch {
|
|
1776
|
+
throw invalidRequest("transform", "INVALID_JSON");
|
|
1777
|
+
}
|
|
1778
|
+
return parseCoordinatesRequest("transform", { from: options.from, to: options.to, positions });
|
|
1779
|
+
}
|
|
1780
|
+
async function readCoordinatesInput(input, signal) {
|
|
1781
|
+
if (input === "-") return readStandardInput(signal);
|
|
1782
|
+
assertNotAborted(signal);
|
|
1783
|
+
let info;
|
|
1784
|
+
try {
|
|
1785
|
+
info = await lstat2(input);
|
|
1786
|
+
} catch {
|
|
1787
|
+
throw new CoordinatesInputError("INVALID_VALUE");
|
|
1788
|
+
}
|
|
1789
|
+
if (!info.isFile() || info.isSymbolicLink()) throw new CoordinatesInputError("INVALID_VALUE");
|
|
1790
|
+
if (info.size > coordinatesLimits.maximumRequestBytes) {
|
|
1791
|
+
throw new CoordinatesInputError("REQUEST_TOO_LARGE");
|
|
1792
|
+
}
|
|
1793
|
+
const handle = await open3(input, "r");
|
|
1794
|
+
try {
|
|
1795
|
+
let length = 0;
|
|
1796
|
+
const bytes = Buffer.alloc(coordinatesLimits.maximumRequestBytes + 1);
|
|
1797
|
+
while (length < bytes.length) {
|
|
1798
|
+
assertNotAborted(signal);
|
|
1799
|
+
const { bytesRead } = await handle.read(bytes, length, bytes.length - length, length);
|
|
1800
|
+
if (bytesRead === 0) break;
|
|
1801
|
+
length += bytesRead;
|
|
1802
|
+
}
|
|
1803
|
+
assertNotAborted(signal);
|
|
1804
|
+
if (length > coordinatesLimits.maximumRequestBytes) {
|
|
1805
|
+
throw new CoordinatesInputError("REQUEST_TOO_LARGE");
|
|
1806
|
+
}
|
|
1807
|
+
return decodeInput(bytes.subarray(0, length));
|
|
1808
|
+
} finally {
|
|
1809
|
+
await handle.close();
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
async function readStandardInput(signal) {
|
|
1813
|
+
const chunks = [];
|
|
1814
|
+
let length = 0;
|
|
1815
|
+
return new Promise((resolve8, reject) => {
|
|
1816
|
+
const finish = (result) => {
|
|
1817
|
+
process.stdin.removeListener("data", onData);
|
|
1818
|
+
process.stdin.removeListener("end", onEnd);
|
|
1819
|
+
process.stdin.removeListener("error", onError);
|
|
1820
|
+
signal.removeEventListener("abort", onAbort);
|
|
1821
|
+
result();
|
|
1822
|
+
};
|
|
1823
|
+
const onAbort = () => {
|
|
1824
|
+
process.stdin.pause();
|
|
1825
|
+
finish(() => reject(new CoordinatesInputError("INVALID_VALUE")));
|
|
1826
|
+
};
|
|
1827
|
+
const onData = (chunk) => {
|
|
1828
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
1829
|
+
length += bytes.length;
|
|
1830
|
+
if (length > coordinatesLimits.maximumRequestBytes) {
|
|
1831
|
+
process.stdin.pause();
|
|
1832
|
+
finish(() => reject(new CoordinatesInputError("REQUEST_TOO_LARGE")));
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
chunks.push(bytes);
|
|
1836
|
+
};
|
|
1837
|
+
const onEnd = () => finish(() => resolve8(decodeInput(Buffer.concat(chunks))));
|
|
1838
|
+
const onError = () => finish(() => reject(new CoordinatesInputError("INVALID_VALUE")));
|
|
1839
|
+
if (signal.aborted) {
|
|
1840
|
+
onAbort();
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
process.stdin.on("data", onData);
|
|
1844
|
+
process.stdin.once("end", onEnd);
|
|
1845
|
+
process.stdin.once("error", onError);
|
|
1846
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1847
|
+
process.stdin.resume();
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
function invalidRequest(command2, reason, details = {}) {
|
|
1851
|
+
return new CoordinatesContractError({
|
|
1852
|
+
schemaVersion: 1,
|
|
1853
|
+
ok: false,
|
|
1854
|
+
command: command2,
|
|
1855
|
+
releaseId: null,
|
|
1856
|
+
provenance: null,
|
|
1857
|
+
warnings: [],
|
|
1858
|
+
usage: null,
|
|
1859
|
+
error: { code: "COORDINATES_INVALID_REQUEST", reason, details, phase: "input" }
|
|
1860
|
+
});
|
|
1861
|
+
}
|
|
1862
|
+
function cancelledRequest(command2) {
|
|
1863
|
+
return new CoordinatesContractError({
|
|
1864
|
+
schemaVersion: 1,
|
|
1865
|
+
ok: false,
|
|
1866
|
+
command: command2,
|
|
1867
|
+
releaseId: null,
|
|
1868
|
+
provenance: null,
|
|
1869
|
+
warnings: [],
|
|
1870
|
+
usage: null,
|
|
1871
|
+
error: { code: "COORDINATES_CANCELLED", reason: "CANCELLED", details: {}, phase: "input" }
|
|
1872
|
+
});
|
|
1873
|
+
}
|
|
1874
|
+
function printDocument(document) {
|
|
1875
|
+
process.stdout.write(`${JSON.stringify(document)}
|
|
1876
|
+
`);
|
|
1877
|
+
}
|
|
1878
|
+
function printFailure(error, command2) {
|
|
1879
|
+
const document = serializeCoordinatesFailure(error, command2) ?? (command2 ? invalidRequest(command2, "INVALID_VALUE").toJSON() : setupFailureDocument());
|
|
1880
|
+
process.stderr.write(`${JSON.stringify(document)}
|
|
1881
|
+
`);
|
|
1882
|
+
process.exitCode = 1;
|
|
1883
|
+
}
|
|
1884
|
+
function printSetupFailure(error) {
|
|
1885
|
+
process.stderr.write(
|
|
1886
|
+
`${JSON.stringify(serializeSetupFailure(error) ?? setupFailureDocument())}
|
|
1887
|
+
`
|
|
1888
|
+
);
|
|
1889
|
+
process.exitCode = 1;
|
|
1890
|
+
}
|
|
1891
|
+
function serializeCoordinatesFailure(error, command2) {
|
|
1892
|
+
if (!(error instanceof CoordinatesContractError)) return void 0;
|
|
1893
|
+
const parsed = coordinatesFailureSchema.safeParse(error.toJSON());
|
|
1894
|
+
if (!parsed.success) return void 0;
|
|
1895
|
+
const failure = parsed.data;
|
|
1896
|
+
return new CoordinatesContractError({
|
|
1897
|
+
...failure,
|
|
1898
|
+
command: failure.command ?? command2 ?? null
|
|
1899
|
+
}).toJSON();
|
|
1900
|
+
}
|
|
1901
|
+
function serializeSetupFailure(error) {
|
|
1902
|
+
if (!(error instanceof CoordinatesSetupError)) return void 0;
|
|
1903
|
+
const codes = /* @__PURE__ */ new Set([
|
|
1904
|
+
"COORDINATES_SETUP_INVALID_REQUEST",
|
|
1905
|
+
"COORDINATES_SETUP_RELEASE_UNAVAILABLE",
|
|
1906
|
+
"COORDINATES_SETUP_MANIFEST_INVALID",
|
|
1907
|
+
"COORDINATES_SETUP_ARTIFACT_UNAVAILABLE",
|
|
1908
|
+
"COORDINATES_SETUP_INTEGRITY_FAILED",
|
|
1909
|
+
"COORDINATES_SETUP_INSTALLATION_FAILED",
|
|
1910
|
+
"COORDINATES_SETUP_CANCELLED",
|
|
1911
|
+
"COORDINATES_SETUP_TIMEOUT"
|
|
1912
|
+
]);
|
|
1913
|
+
if (!codes.has(error.code)) return void 0;
|
|
1914
|
+
return new CoordinatesSetupError(error.code, error.releaseId, error.assetId).toJSON();
|
|
1915
|
+
}
|
|
1916
|
+
function setupFailureDocument() {
|
|
1917
|
+
return new CoordinatesSetupError("COORDINATES_SETUP_INVALID_REQUEST").toJSON();
|
|
1918
|
+
}
|
|
1919
|
+
function printHelp(command2, json, printJson) {
|
|
1920
|
+
if (json) {
|
|
1921
|
+
printJson();
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
command2.outputHelp();
|
|
1925
|
+
}
|
|
1926
|
+
function isRemoteLocation(input) {
|
|
1927
|
+
return /^[A-Za-z][A-Za-z0-9+.-]*:\/\//u.test(input);
|
|
1928
|
+
}
|
|
1929
|
+
function assertNotAborted(signal) {
|
|
1930
|
+
if (signal.aborted) throw new CoordinatesInputError("INVALID_VALUE");
|
|
1931
|
+
}
|
|
1932
|
+
function decodeInput(bytes) {
|
|
1933
|
+
try {
|
|
1934
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
1935
|
+
} catch {
|
|
1936
|
+
throw new CoordinatesInputError("INVALID_VALUE");
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1591
1940
|
// src/deploy-source.ts
|
|
1592
1941
|
var overrideFields = [
|
|
1593
1942
|
["TILEFLOW_DEPLOY_REPOSITORY", "repository", 255],
|
|
@@ -2018,7 +2367,10 @@ async function validateAccountSession(session, options = {}) {
|
|
|
2018
2367
|
options
|
|
2019
2368
|
);
|
|
2020
2369
|
if (!response.ok) {
|
|
2021
|
-
return {
|
|
2370
|
+
return {
|
|
2371
|
+
error: response.status === 401 ? rejectedAccountSessionMessage : `Account session validation failed (${response.status}).`,
|
|
2372
|
+
ok: false
|
|
2373
|
+
};
|
|
2022
2374
|
}
|
|
2023
2375
|
const body = asRecord(response.body);
|
|
2024
2376
|
const account = asRecord(body.account);
|
|
@@ -2110,7 +2462,10 @@ async function requestProjectCapability(session, project, scopes, options = {})
|
|
|
2110
2462
|
};
|
|
2111
2463
|
}
|
|
2112
2464
|
if (!response.ok) {
|
|
2113
|
-
return {
|
|
2465
|
+
return {
|
|
2466
|
+
error: response.status === 401 ? rejectedAccountSessionMessage : `Project capability request failed (${response.status}).`,
|
|
2467
|
+
ok: false
|
|
2468
|
+
};
|
|
2114
2469
|
}
|
|
2115
2470
|
const body = asRecord(response.body);
|
|
2116
2471
|
if (!response.json || typeof body.capability !== "string" || !body.capability.startsWith("tf_cap_") || body.capability.length > 8192 || body.reference !== project || !validIsoDate(body.expiresAt) || !Array.isArray(body.scopes) || body.scopes.join("\0") !== [...scopes].sort().join("\0")) {
|
|
@@ -3661,7 +4016,7 @@ async function accountRequest(session, path, init) {
|
|
|
3661
4016
|
}
|
|
3662
4017
|
return response.ok ? { body: response.body, ok: true, status: response.status } : {
|
|
3663
4018
|
body: response.body,
|
|
3664
|
-
error: `Request failed (${response.status}).`,
|
|
4019
|
+
error: response.status === 401 ? rejectedAccountSessionMessage : `Request failed (${response.status}).`,
|
|
3665
4020
|
ok: false,
|
|
3666
4021
|
status: response.status
|
|
3667
4022
|
};
|
|
@@ -3745,7 +4100,7 @@ import { z as z6 } from "zod";
|
|
|
3745
4100
|
|
|
3746
4101
|
// src/tileset-upload.ts
|
|
3747
4102
|
import { createHash } from "crypto";
|
|
3748
|
-
import { open as
|
|
4103
|
+
import { open as open4, stat } from "fs/promises";
|
|
3749
4104
|
import { z as z5 } from "zod";
|
|
3750
4105
|
import { serializeCanonicalJson as serializeCanonicalJson2 } from "@tileflow/core";
|
|
3751
4106
|
var teamTilesetPartSize = 16777216;
|
|
@@ -3809,7 +4164,7 @@ async function inspectNodeTeamTilesetArchive(path) {
|
|
|
3809
4164
|
return checkedIdentity(value.dev, value.ino, value.mtimeNs, value.size);
|
|
3810
4165
|
}
|
|
3811
4166
|
async function openNodeTeamTilesetArchive(path, expectedIdentity) {
|
|
3812
|
-
const handle = await
|
|
4167
|
+
const handle = await open4(path, "r");
|
|
3813
4168
|
const identity = async () => {
|
|
3814
4169
|
let pathStat;
|
|
3815
4170
|
let handleStat;
|
|
@@ -4824,7 +5179,7 @@ var tilesetPurgeResponseSchema = z6.object({
|
|
|
4824
5179
|
}).strict();
|
|
4825
5180
|
|
|
4826
5181
|
// src/visual-command.ts
|
|
4827
|
-
import { lstat as
|
|
5182
|
+
import { lstat as lstat4, readFile as readFile6 } from "fs/promises";
|
|
4828
5183
|
import { dirname as dirname9, isAbsolute as isAbsolute3, parse as parse2, relative as relative4, resolve as resolve6, sep as sep4 } from "path";
|
|
4829
5184
|
import {
|
|
4830
5185
|
analyzeTileflowCaptureReference,
|
|
@@ -4843,7 +5198,7 @@ import { compareCodeUnits as compareCodeUnits8 } from "@tileflow/core";
|
|
|
4843
5198
|
|
|
4844
5199
|
// src/visual-compare-command.ts
|
|
4845
5200
|
import { createHash as createHash3 } from "crypto";
|
|
4846
|
-
import { lstat as
|
|
5201
|
+
import { lstat as lstat3, readFile as readFile5, realpath } from "fs/promises";
|
|
4847
5202
|
import { basename as basename3, dirname as dirname8, extname, isAbsolute as isAbsolute2, parse, relative as relative3, resolve as resolve5, sep as sep3 } from "path";
|
|
4848
5203
|
import {
|
|
4849
5204
|
compareTileflowCapturesForReview,
|
|
@@ -5793,7 +6148,7 @@ async function readVisualCompareInventory(assetDirectory, inventoryPath, boundar
|
|
|
5793
6148
|
await assertNoSymlinkComponents(dirname8(inventoryPath), boundary);
|
|
5794
6149
|
let info;
|
|
5795
6150
|
try {
|
|
5796
|
-
info = await
|
|
6151
|
+
info = await lstat3(inventoryPath);
|
|
5797
6152
|
} catch (error) {
|
|
5798
6153
|
if (isMissingFile4(error)) return [];
|
|
5799
6154
|
throw error;
|
|
@@ -5817,7 +6172,7 @@ async function readVisualCompareInventory(assetDirectory, inventoryPath, boundar
|
|
|
5817
6172
|
await assertNoSymlinkComponents(dirname8(path), boundary);
|
|
5818
6173
|
let assetInfo;
|
|
5819
6174
|
try {
|
|
5820
|
-
assetInfo = await
|
|
6175
|
+
assetInfo = await lstat3(path);
|
|
5821
6176
|
} catch (error) {
|
|
5822
6177
|
if (isMissingFile4(error)) continue;
|
|
5823
6178
|
throw error;
|
|
@@ -5979,7 +6334,7 @@ function pathsOverlap(left, right) {
|
|
|
5979
6334
|
}
|
|
5980
6335
|
async function assertDirectoryIfPresent(path) {
|
|
5981
6336
|
try {
|
|
5982
|
-
const info = await
|
|
6337
|
+
const info = await lstat3(path);
|
|
5983
6338
|
if (info.isSymbolicLink() || !info.isDirectory()) {
|
|
5984
6339
|
throw new Error("Visual compare asset path must be a non-symlink directory.");
|
|
5985
6340
|
}
|
|
@@ -5989,7 +6344,7 @@ async function assertDirectoryIfPresent(path) {
|
|
|
5989
6344
|
}
|
|
5990
6345
|
async function pathExists(path) {
|
|
5991
6346
|
try {
|
|
5992
|
-
await
|
|
6347
|
+
await lstat3(path);
|
|
5993
6348
|
return true;
|
|
5994
6349
|
} catch (error) {
|
|
5995
6350
|
if (isMissingFile4(error)) return false;
|
|
@@ -6343,7 +6698,7 @@ async function readOptionalArtifact(path, boundary, maximumBytes) {
|
|
|
6343
6698
|
await assertNoSymlinkComponents(dirname9(path), boundary);
|
|
6344
6699
|
let info;
|
|
6345
6700
|
try {
|
|
6346
|
-
info = await
|
|
6701
|
+
info = await lstat4(path);
|
|
6347
6702
|
} catch (error) {
|
|
6348
6703
|
if (isMissingFile5(error)) return void 0;
|
|
6349
6704
|
throw error;
|
|
@@ -6363,7 +6718,7 @@ async function readOptionalArtifact(path, boundary, maximumBytes) {
|
|
|
6363
6718
|
async function readVisualReference(path) {
|
|
6364
6719
|
let info;
|
|
6365
6720
|
try {
|
|
6366
|
-
info = await
|
|
6721
|
+
info = await lstat4(path);
|
|
6367
6722
|
} catch (error) {
|
|
6368
6723
|
throw new TileflowCaptureError2("BASELINE_INVALID", "The visual reference PNG cannot be read.", {
|
|
6369
6724
|
cause: error
|
|
@@ -6399,7 +6754,7 @@ async function resolveVisualDirectory(cwd, value, kind) {
|
|
|
6399
6754
|
const boundary = isInside2(cwd, path) ? cwd : path;
|
|
6400
6755
|
await assertNoSymlinkComponents(path, boundary);
|
|
6401
6756
|
try {
|
|
6402
|
-
const info = await
|
|
6757
|
+
const info = await lstat4(path);
|
|
6403
6758
|
if (info.isSymbolicLink() || !info.isDirectory()) {
|
|
6404
6759
|
throw new Error(`Visual ${kind} path must be a non-symlink directory.`);
|
|
6405
6760
|
}
|
|
@@ -7341,6 +7696,7 @@ registerConfigInspectCommand(inspectCommand, { defaultConfigPath });
|
|
|
7341
7696
|
registerFeatureInspectCommand(inspectCommand, { defaultConfigPath });
|
|
7342
7697
|
registerAiCommands(program, { defaultApiUrl, defaultConfigPath });
|
|
7343
7698
|
registerCaptureCommands(program, { defaultConfigPath });
|
|
7699
|
+
registerCoordinatesCommands(program, { createLocalCoordinates, setupCoordinates });
|
|
7344
7700
|
registerVisualCommands(program, {
|
|
7345
7701
|
defaultConfigPath,
|
|
7346
7702
|
openReport: (path) => openBrowser(path, true)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tileflow",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "CLI-only tools for Tileflow config, capture, visual testing, and deploy.",
|
|
6
6
|
"type": "module",
|
|
@@ -25,10 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"dist",
|
|
28
|
-
"LICENSE"
|
|
29
|
-
"NOTICE",
|
|
30
|
-
"GENERATED_OUTPUT_LICENSE.md",
|
|
31
|
-
"TRADEMARKS.md"
|
|
28
|
+
"LICENSE"
|
|
32
29
|
],
|
|
33
30
|
"bin": {
|
|
34
31
|
"tileflow": "./dist/index.js"
|
|
@@ -38,12 +35,14 @@
|
|
|
38
35
|
},
|
|
39
36
|
"dependencies": {
|
|
40
37
|
"@hono/node-server": "2.0.10",
|
|
41
|
-
"@tileflow/capture": ">=0.1.0-alpha.
|
|
42
|
-
"@tileflow/
|
|
43
|
-
"@tileflow/
|
|
44
|
-
"@tileflow/
|
|
45
|
-
"
|
|
38
|
+
"@tileflow/capture": ">=0.1.0-alpha.21 <0.1.0-beta.0",
|
|
39
|
+
"@tileflow/coordinates": ">=0.1.0-alpha.0 <0.1.0-beta.0",
|
|
40
|
+
"@tileflow/coordinates-runtime": ">=0.1.0-alpha.0 <0.1.0-beta.0",
|
|
41
|
+
"@tileflow/core": ">=0.1.0-alpha.24 <0.1.0-beta.0",
|
|
42
|
+
"@tileflow/dev": ">=0.1.0-alpha.22 <0.1.0-beta.0",
|
|
43
|
+
"@tileflow/maps": ">=0.1.0-alpha.2 <0.1.0-beta.0",
|
|
46
44
|
"commander": "^15.0.0",
|
|
45
|
+
"hono": "4.13.5",
|
|
47
46
|
"picocolors": "^1.1.1",
|
|
48
47
|
"zod": "^4.4.3"
|
|
49
48
|
},
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# Tileflow generated-output grant
|
|
2
|
-
|
|
3
|
-
In addition to the rights granted by the Apache License, Version 2.0, Tileflow.dev contributors
|
|
4
|
-
grant recipients a perpetual, worldwide, non-exclusive, no-charge, royalty-free right to use,
|
|
5
|
-
reproduce, modify, display, perform, distribute, sublicense, and commercialize the Tileflow-owned
|
|
6
|
-
parts of output generated by this SDK.
|
|
7
|
-
|
|
8
|
-
Covered output includes compiled map styles, manifests, receipts, sprites, glyph artifacts, static
|
|
9
|
-
render descriptions, and other build artifacts produced from Tileflow maps or application maps.
|
|
10
|
-
Original Tileflow map definitions, icons, and patterns distributed by `@tileflow/maps` may likewise
|
|
11
|
-
be used, modified, embedded, deployed, and redistributed as part of personal or commercial maps and
|
|
12
|
-
applications.
|
|
13
|
-
|
|
14
|
-
This grant does not relicense third-party software, fonts, icons, map data, datasets, imagery, or
|
|
15
|
-
other input material. Their licenses, attribution requirements, notices, and use restrictions
|
|
16
|
-
continue to apply to the relevant source and generated output. You are responsible for having the
|
|
17
|
-
rights necessary to process and distribute your own inputs.
|
|
18
|
-
|
|
19
|
-
This grant does not provide access to Tileflow hosted services or grant rights in Tileflow names,
|
|
20
|
-
logos, or marks. The SDK and generated output are provided without warranties or conditions beyond
|
|
21
|
-
those required by applicable law.
|
package/NOTICE
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
Tileflow SDK
|
|
2
|
-
Copyright 2026 Tileflow.dev contributors
|
|
3
|
-
|
|
4
|
-
This product includes software and original map artwork developed by Tileflow.dev contributors.
|
|
5
|
-
|
|
6
|
-
Third-party materials distributed with a package remain subject to their own license and notice
|
|
7
|
-
files. See THIRD_PARTY_NOTICES.md and asset-adjacent license files where present.
|
|
8
|
-
|
|
9
|
-
The Tileflow names and marks are not licensed under the Apache License 2.0. See TRADEMARKS.md.
|
package/TRADEMARKS.md
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
# Tileflow trademarks
|
|
2
|
-
|
|
3
|
-
The Apache License, Version 2.0, and the Tileflow generated-output grant do not grant permission to
|
|
4
|
-
use the Tileflow name, `tileflow.dev`, logos, service marks, product names, or other Tileflow brand
|
|
5
|
-
features except for reasonable and customary use that truthfully describes the origin of the SDK or
|
|
6
|
-
compatibility with Tileflow, and for reproducing required notices.
|
|
7
|
-
|
|
8
|
-
You may not use Tileflow brand features in a way that suggests sponsorship, endorsement, or an
|
|
9
|
-
official Tileflow product when none exists. Third-party names and marks remain the property of their
|
|
10
|
-
respective owners.
|