tileflow 0.1.0-alpha.13 → 0.1.0-alpha.14
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 +2 -1
- package/dist/index.js +124 -32
- package/docs/hosted-authentication-and-deploy.md +13 -2
- package/docs/local-visual-feedback.md +2 -2
- package/docs/map-authoring.md +7 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -53,7 +53,8 @@ workspaces are advanced repository orchestration, not a second application autho
|
|
|
53
53
|
|
|
54
54
|
## Map development tools
|
|
55
55
|
|
|
56
|
-
Use `describe` for bounded language discovery
|
|
56
|
+
Use `describe` for bounded language discovery (`describe themes --json` lists palette requirements
|
|
57
|
+
and optional time-of-day composition), `inspect` for configuration and data, `capture`
|
|
57
58
|
for reproducible evidence, and `compare` for saved captures, semantic edits, styles, references or
|
|
58
59
|
baselines. `preview` opens Map DevTools. These tools work in independent projects with
|
|
59
60
|
custom maps and data; official maps are optional.
|
package/dist/index.js
CHANGED
|
@@ -3922,7 +3922,7 @@ function isRecord(value) {
|
|
|
3922
3922
|
|
|
3923
3923
|
// src/describe-command.ts
|
|
3924
3924
|
function registerDescribeCommand(program2) {
|
|
3925
|
-
program2.command("describe").description("Discover domains and query the generated authoring schema without loading a map").argument("[topic]", "domain name, expressions, or schema").option(
|
|
3925
|
+
program2.command("describe").description("Discover domains and query the generated authoring schema without loading a map").argument("[topic]", "domain name, themes, expressions, or schema").option(
|
|
3926
3926
|
"--ref <pointer>",
|
|
3927
3927
|
"exact local schema reference, such as #/$defs/TileflowRoadsModuleOptions"
|
|
3928
3928
|
).option("--path <pointer>", "JSON Pointer within the selected topic or schema", "").option("--limit <count>", "maximum returned tree entries (1\u2013500)", "80").option("--depth <count>", "maximum returned tree depth (1\u201312)", "4").option("--json", "print deterministic bounded JSON").action(
|
|
@@ -3933,7 +3933,7 @@ function registerDescribeCommand(program2) {
|
|
|
3933
3933
|
const manifest = tileflowAuthoringManifest2;
|
|
3934
3934
|
let value;
|
|
3935
3935
|
let schemaRef;
|
|
3936
|
-
if (options.ref || topic && topic !== "expressions") {
|
|
3936
|
+
if (options.ref || topic && topic !== "expressions" && topic !== "themes") {
|
|
3937
3937
|
const schema = await loadPackagedSchema();
|
|
3938
3938
|
const domain = manifest.domains.find((entry) => entry.name === topic);
|
|
3939
3939
|
if (topic && topic !== "schema" && !domain)
|
|
@@ -3946,6 +3946,8 @@ function registerDescribeCommand(program2) {
|
|
|
3946
3946
|
value = selectJsonPointer(schema, schemaRef.replace(/^#/u, ""));
|
|
3947
3947
|
} else if (topic === "expressions") {
|
|
3948
3948
|
value = manifest.expressions;
|
|
3949
|
+
} else if (topic === "themes") {
|
|
3950
|
+
value = manifest.themes;
|
|
3949
3951
|
} else {
|
|
3950
3952
|
value = {
|
|
3951
3953
|
domains: manifest.domains.map(({ name, api, optionsSchemaRef }) => ({
|
|
@@ -3953,7 +3955,7 @@ function registerDescribeCommand(program2) {
|
|
|
3953
3955
|
api,
|
|
3954
3956
|
schemaRef: optionsSchemaRef
|
|
3955
3957
|
})),
|
|
3956
|
-
topics: ["expressions", "schema"],
|
|
3958
|
+
topics: ["expressions", "schema", "themes"],
|
|
3957
3959
|
commands: ["describe", "inspect", "capture", "compare", "preview"]
|
|
3958
3960
|
};
|
|
3959
3961
|
}
|
|
@@ -4126,11 +4128,14 @@ function printFeatureInspectError(error) {
|
|
|
4126
4128
|
}
|
|
4127
4129
|
|
|
4128
4130
|
// src/hosted-client.ts
|
|
4129
|
-
import {
|
|
4131
|
+
import {
|
|
4132
|
+
serializeTileflowFontBundleManifest,
|
|
4133
|
+
serializeTileflowModelBundleManifest
|
|
4134
|
+
} from "@tileflow/core";
|
|
4130
4135
|
|
|
4131
4136
|
// src/hosted-response.ts
|
|
4132
4137
|
import { z as z2 } from "zod";
|
|
4133
|
-
import { tileflowIconPackageLimits } from "@tileflow/core";
|
|
4138
|
+
import { tileflowIconPackageLimits, tileflowModelBundleLimits } from "@tileflow/core";
|
|
4134
4139
|
var maximumHostedResponseBytes = 1024 * 1024;
|
|
4135
4140
|
var safeTextSchema = z2.string().min(1).max(300).refine((value) => !/[\p{Cc}]/u.test(value));
|
|
4136
4141
|
var safeIdentifierSchema = z2.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/u);
|
|
@@ -4168,6 +4173,14 @@ var hostedFontBundleResponseSchema = z2.object({
|
|
|
4168
4173
|
id: z2.string().regex(/^fnb_[A-Za-z0-9_-]{16,64}$/u),
|
|
4169
4174
|
totalBytes: z2.number().int().positive().max(Number.MAX_SAFE_INTEGER)
|
|
4170
4175
|
});
|
|
4176
|
+
var hostedModelBundleResponseSchema = z2.object({
|
|
4177
|
+
baseUrl: publicHttpUrlSchema,
|
|
4178
|
+
changed: z2.boolean().optional(),
|
|
4179
|
+
contentHash: z2.string().regex(/^[a-f0-9]{64}$/u),
|
|
4180
|
+
id: z2.string().regex(/^mdb_[A-Za-z0-9_-]{16,64}$/u),
|
|
4181
|
+
modelCount: z2.number().int().positive().max(tileflowModelBundleLimits.maxModelCount),
|
|
4182
|
+
totalBytes: z2.number().int().positive().max(tileflowModelBundleLimits.maxPackageBytes)
|
|
4183
|
+
});
|
|
4171
4184
|
var hostedStatusStyleSchema = z2.object({
|
|
4172
4185
|
environment: safeTextSchema,
|
|
4173
4186
|
key: safeStorageKeySchema,
|
|
@@ -4520,6 +4533,42 @@ async function uploadHostedFontBundle(api, bundle, options = {}) {
|
|
|
4520
4533
|
}
|
|
4521
4534
|
return { ok: true, value };
|
|
4522
4535
|
}
|
|
4536
|
+
async function uploadHostedModelBundle(api, bundle, options = {}) {
|
|
4537
|
+
const formData = new FormData();
|
|
4538
|
+
formData.append(
|
|
4539
|
+
"manifest",
|
|
4540
|
+
new Blob([serializeTileflowModelBundleManifest(bundle.manifest)], {
|
|
4541
|
+
type: "application/json"
|
|
4542
|
+
}),
|
|
4543
|
+
"manifest.json"
|
|
4544
|
+
);
|
|
4545
|
+
for (const file of bundle.files) {
|
|
4546
|
+
const bytes = new Uint8Array(file.source.byteLength);
|
|
4547
|
+
bytes.set(file.source);
|
|
4548
|
+
formData.append(
|
|
4549
|
+
`file:${encodeURIComponent(file.fileName)}`,
|
|
4550
|
+
new Blob([bytes.buffer], { type: file.contentType }),
|
|
4551
|
+
file.fileName
|
|
4552
|
+
);
|
|
4553
|
+
}
|
|
4554
|
+
const response = await requestHostedJson(
|
|
4555
|
+
api.apiUrl,
|
|
4556
|
+
`/v1/model-bundles/${encodeURIComponent(bundle.contentHash)}`,
|
|
4557
|
+
{ body: formData, headers: authorizationHeaders(api.apiKey, api.mapId), method: "PUT" },
|
|
4558
|
+
options
|
|
4559
|
+
);
|
|
4560
|
+
if (!response.ok) return { ok: false, status: response.status };
|
|
4561
|
+
const value = parseResponse(
|
|
4562
|
+
response,
|
|
4563
|
+
hostedModelBundleResponseSchema,
|
|
4564
|
+
"Model bundle upload response"
|
|
4565
|
+
);
|
|
4566
|
+
const publicBaseUrl = new URL(value.baseUrl);
|
|
4567
|
+
if (value.contentHash !== bundle.contentHash || value.modelCount !== bundle.manifest.models.length || value.totalBytes !== bundle.manifest.models.reduce((total, model) => total + model.byteLength, 0) || publicBaseUrl.pathname !== `/model-bundles/${value.id}` || publicBaseUrl.search !== "") {
|
|
4568
|
+
throw new Error("Model bundle upload response did not confirm the submitted bundle.");
|
|
4569
|
+
}
|
|
4570
|
+
return { ok: true, value };
|
|
4571
|
+
}
|
|
4523
4572
|
async function fetchHostedMapStatus(api, options = {}) {
|
|
4524
4573
|
const response = await requestHostedJson(
|
|
4525
4574
|
api.apiUrl,
|
|
@@ -4773,22 +4822,6 @@ function inspectTileflowHostedCompatibility(project, styles) {
|
|
|
4773
4822
|
});
|
|
4774
4823
|
}
|
|
4775
4824
|
}
|
|
4776
|
-
const own = /* @__PURE__ */ new Set();
|
|
4777
|
-
for (const style of Object.values(themes)) {
|
|
4778
|
-
const models = style?.metadata?.["tileflow:buildings"]?.models;
|
|
4779
|
-
if (!Array.isArray(models)) continue;
|
|
4780
|
-
for (const entry of models) {
|
|
4781
|
-
const { model, url } = entry ?? {};
|
|
4782
|
-
if (typeof url === "string" && url.includes(`models/${mapName}/`) && typeof model === "string")
|
|
4783
|
-
own.add(model);
|
|
4784
|
-
}
|
|
4785
|
-
}
|
|
4786
|
-
if (own.size > 0)
|
|
4787
|
-
issues.push({
|
|
4788
|
-
map: mapName,
|
|
4789
|
-
message: `Hosted deploy does not take a map's own 3D building models yet. Map ${mapName} places ${[...own].sort(compareCodeUnits5).join(", ")}; keep the map local or remove them from buildings.threeDimensional.models.`,
|
|
4790
|
-
path: `maps.${mapName}.modules.buildings.threeDimensional.models`
|
|
4791
|
-
});
|
|
4792
4825
|
}
|
|
4793
4826
|
return issues;
|
|
4794
4827
|
}
|
|
@@ -4797,17 +4830,36 @@ function cloneStyle(style) {
|
|
|
4797
4830
|
}
|
|
4798
4831
|
|
|
4799
4832
|
// src/model-assets.ts
|
|
4800
|
-
import {
|
|
4833
|
+
import {
|
|
4834
|
+
createTileflowModelBundle,
|
|
4835
|
+
prepareTileflowMapTreeModels
|
|
4836
|
+
} from "@tileflow/dev/icons";
|
|
4801
4837
|
async function prepareModelAssets(project, options) {
|
|
4802
|
-
const
|
|
4838
|
+
const prepared = { assets: {}, bundles: {}, identities: {} };
|
|
4803
4839
|
for (const [name, map] of Object.entries(project.maps)) {
|
|
4804
4840
|
if (!map.models?.length) continue;
|
|
4805
|
-
const
|
|
4841
|
+
const models = await prepareTileflowMapTreeModels(map.models, {
|
|
4806
4842
|
...options,
|
|
4807
4843
|
cwd: process.cwd(),
|
|
4808
4844
|
configPath: `maps.${name}.models`
|
|
4809
4845
|
});
|
|
4810
|
-
assets[name] = { models:
|
|
4846
|
+
prepared.assets[name] = { models: models.models };
|
|
4847
|
+
const bundle = await createTileflowModelBundle(models.buildings);
|
|
4848
|
+
if (bundle) prepared.bundles[name] = bundle;
|
|
4849
|
+
if (models.identities.length > 0) prepared.identities[name] = models.identities;
|
|
4850
|
+
}
|
|
4851
|
+
return prepared;
|
|
4852
|
+
}
|
|
4853
|
+
function mapAssetsWithBuildingModels(prepared, baseOf) {
|
|
4854
|
+
const assets = { ...prepared.assets };
|
|
4855
|
+
for (const [mapName, bundle] of Object.entries(prepared.bundles)) {
|
|
4856
|
+
const base = baseOf(mapName, bundle).replace(/\/+$/u, "");
|
|
4857
|
+
assets[mapName] = {
|
|
4858
|
+
...assets[mapName],
|
|
4859
|
+
buildingModels: Object.fromEntries(
|
|
4860
|
+
bundle.manifest.models.map((model) => [model.id, `${base}/${model.file}`])
|
|
4861
|
+
)
|
|
4862
|
+
};
|
|
4811
4863
|
}
|
|
4812
4864
|
return assets;
|
|
4813
4865
|
}
|
|
@@ -4850,13 +4902,17 @@ async function prepareNativeHostedDeploy(options) {
|
|
|
4850
4902
|
const iconPackage = binding ? compiledIcons.packages.find((entry) => entry.contentHash === binding.packageHash) : void 0;
|
|
4851
4903
|
const sprite = binding ? `${apiOrigin}/sprites/preflight/${binding.packageHash}/sprite` : void 0;
|
|
4852
4904
|
const modelAssets = await prepareModelAssets(project, { baseDirectory, target: "hosted" });
|
|
4905
|
+
if (modelAssets.bundles[mapName])
|
|
4906
|
+
throw failure(
|
|
4907
|
+
"A deployment with --with-native does not take a map's own 3D building models yet. Deploy without --with-native, or remove them from the map's models directories."
|
|
4908
|
+
);
|
|
4853
4909
|
const mapAssets = {
|
|
4854
|
-
...modelAssets,
|
|
4910
|
+
...modelAssets.assets,
|
|
4855
4911
|
...Object.fromEntries(
|
|
4856
4912
|
compiledIcons.bindings.map((entry) => [
|
|
4857
4913
|
entry.mapName,
|
|
4858
4914
|
{
|
|
4859
|
-
...modelAssets[entry.mapName],
|
|
4915
|
+
...modelAssets.assets[entry.mapName],
|
|
4860
4916
|
icons: {
|
|
4861
4917
|
ids: entry.iconIds,
|
|
4862
4918
|
sprite: `${apiOrigin}/sprites/preflight/${entry.packageHash}/sprite`
|
|
@@ -9348,7 +9404,10 @@ program.command("validate").description("Validate a Tileflow config").option("-c
|
|
|
9348
9404
|
const mapAssets = createCompiledMapAssets(
|
|
9349
9405
|
compiledIcons,
|
|
9350
9406
|
(binding) => options.target === "hosted" ? `${normalizeUrl(options.apiBaseUrl)}/sprites/preflight/${binding.packageHash}/sprite` : `/tileflow/icons/${binding.mapName}/sprite`,
|
|
9351
|
-
|
|
9407
|
+
mapAssetsWithBuildingModels(
|
|
9408
|
+
modelAssets,
|
|
9409
|
+
(mapName) => options.target === "hosted" ? `${normalizeUrl(options.apiBaseUrl)}/model-bundles/preflight` : `/tileflow/models/${mapName}`
|
|
9410
|
+
)
|
|
9352
9411
|
);
|
|
9353
9412
|
failureDefaults = { code: "STYLE_INVALID", phase: "style-validation" };
|
|
9354
9413
|
const compiledStyles = createTileflowStyles2(project, {
|
|
@@ -9824,7 +9883,7 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
|
|
|
9824
9883
|
const preflightMapAssets = createCompiledMapAssets(
|
|
9825
9884
|
compiledIcons,
|
|
9826
9885
|
(binding) => `${apiUrl}/sprites/preflight/${binding.packageHash}/sprite`,
|
|
9827
|
-
modelAssets
|
|
9886
|
+
mapAssetsWithBuildingModels(modelAssets, () => `${apiUrl}/model-bundles/preflight`)
|
|
9828
9887
|
);
|
|
9829
9888
|
const compiledPreflightStyles = createTileflowStyles2(deploymentProject, {
|
|
9830
9889
|
apiBaseUrl: apiUrl,
|
|
@@ -9898,6 +9957,25 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
|
|
|
9898
9957
|
);
|
|
9899
9958
|
hostedSpriteByPackageHash.set(iconPackage.contentHash, uploaded.value.spriteUrl);
|
|
9900
9959
|
}
|
|
9960
|
+
const hostedModelBaseByHash = /* @__PURE__ */ new Map();
|
|
9961
|
+
const uniqueModelBundles = [
|
|
9962
|
+
...new Map(
|
|
9963
|
+
Object.values(modelAssets.bundles).map((bundle) => [bundle.contentHash, bundle])
|
|
9964
|
+
).values()
|
|
9965
|
+
].sort((left, right) => left.contentHash.localeCompare(right.contentHash));
|
|
9966
|
+
for (const bundle of uniqueModelBundles) {
|
|
9967
|
+
const uploaded = await uploadHostedModelBundle(api, bundle);
|
|
9968
|
+
if (!uploaded.ok) {
|
|
9969
|
+
logError(`Model bundle upload failed: ${uploaded.status}.`);
|
|
9970
|
+
process.exitCode = 1;
|
|
9971
|
+
return;
|
|
9972
|
+
}
|
|
9973
|
+
const action = uploaded.value.changed === false ? "Reused" : "Uploaded";
|
|
9974
|
+
logSuccess(
|
|
9975
|
+
`${action} 3D model bundle (${plural(bundle.manifest.models.length, "model")}, ${formatBytes3(uploaded.value.totalBytes)}, ${bundle.contentHash.slice(0, 12)}).`
|
|
9976
|
+
);
|
|
9977
|
+
hostedModelBaseByHash.set(bundle.contentHash, uploaded.value.baseUrl);
|
|
9978
|
+
}
|
|
9901
9979
|
const hostedMapAssets = createCompiledMapAssets(
|
|
9902
9980
|
compiledIcons,
|
|
9903
9981
|
(binding) => {
|
|
@@ -9905,7 +9983,11 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
|
|
|
9905
9983
|
if (!sprite) throw new Error(`Missing hosted sprite URL for map ${binding.mapName}`);
|
|
9906
9984
|
return sprite;
|
|
9907
9985
|
},
|
|
9908
|
-
modelAssets
|
|
9986
|
+
mapAssetsWithBuildingModels(modelAssets, (mapName, bundle) => {
|
|
9987
|
+
const base = hostedModelBaseByHash.get(bundle.contentHash);
|
|
9988
|
+
if (!base) throw new Error(`Missing hosted model bundle URL for map ${mapName}.`);
|
|
9989
|
+
return base;
|
|
9990
|
+
})
|
|
9909
9991
|
);
|
|
9910
9992
|
const compiledHostedStyles = createTileflowStyles2(deploymentProject, {
|
|
9911
9993
|
apiBaseUrl: api.apiUrl,
|
|
@@ -9996,7 +10078,12 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
|
|
|
9996
10078
|
fileName: `icons/${mapName}/${file.fileName}`,
|
|
9997
10079
|
source: file.source
|
|
9998
10080
|
})),
|
|
9999
|
-
...hostedFonts.bundles[mapName]?.files ?? []
|
|
10081
|
+
...hostedFonts.bundles[mapName]?.files ?? [],
|
|
10082
|
+
...(modelAssets.bundles[mapName]?.files ?? []).map((file) => ({
|
|
10083
|
+
contentType: file.contentType,
|
|
10084
|
+
fileName: `models/${mapName}/${file.fileName}`,
|
|
10085
|
+
source: file.source
|
|
10086
|
+
}))
|
|
10000
10087
|
],
|
|
10001
10088
|
lineage: deploymentProject.mapMetadata?.[mapName]?.lineage ?? [
|
|
10002
10089
|
{ id: map.id, mapVersion: map.version }
|
|
@@ -10005,7 +10092,8 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
|
|
|
10005
10092
|
sourceAssets: {
|
|
10006
10093
|
fonts: hostedFonts.sourceIdentities[mapName] ?? [],
|
|
10007
10094
|
icons: compiledIcons.sourceIdentities[mapName] ?? [],
|
|
10008
|
-
...compiledIcons.compositions[mapName] ? { iconComposition: compiledIcons.compositions[mapName] } : {}
|
|
10095
|
+
...compiledIcons.compositions[mapName] ? { iconComposition: compiledIcons.compositions[mapName] } : {},
|
|
10096
|
+
...modelAssets.identities[mapName] ? { models: modelAssets.identities[mapName] } : {}
|
|
10009
10097
|
},
|
|
10010
10098
|
styles: styles[mapName]
|
|
10011
10099
|
}
|
|
@@ -10018,11 +10106,13 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
|
|
|
10018
10106
|
const iconBinding = bindingsByMap.get(mapName);
|
|
10019
10107
|
const iconPackage = iconBinding ? packagesByHash.get(iconBinding.packageHash) : void 0;
|
|
10020
10108
|
const fontBundle = hostedFonts.bundles[mapName];
|
|
10109
|
+
const modelBundle = modelAssets.bundles[mapName];
|
|
10021
10110
|
return {
|
|
10022
10111
|
iconBinding,
|
|
10023
10112
|
iconComposition: compiledIcons.compositions[mapName],
|
|
10024
10113
|
iconPackage,
|
|
10025
10114
|
fontBundle,
|
|
10115
|
+
modelBundle,
|
|
10026
10116
|
mapName,
|
|
10027
10117
|
teamSources: hostedThemeFamilies[mapName].teamSources,
|
|
10028
10118
|
buildManifest: {
|
|
@@ -10042,6 +10132,7 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
|
|
|
10042
10132
|
iconComposition,
|
|
10043
10133
|
iconPackage,
|
|
10044
10134
|
mapName,
|
|
10135
|
+
modelBundle,
|
|
10045
10136
|
teamSources,
|
|
10046
10137
|
styles: themeStyles
|
|
10047
10138
|
} = deployment;
|
|
@@ -10076,6 +10167,7 @@ program.command("deploy").description("Deploy maps to Tileflow and write the fro
|
|
|
10076
10167
|
} : {},
|
|
10077
10168
|
...iconComposition ? { iconComposition } : {},
|
|
10078
10169
|
...fontBundle ? { fontBundle: { contentHash: fontBundle.contentHash } } : {},
|
|
10170
|
+
...modelBundle ? { modelBundle: { contentHash: modelBundle.contentHash } } : {},
|
|
10079
10171
|
source
|
|
10080
10172
|
},
|
|
10081
10173
|
`Deploy response for ${mapName}`
|
|
@@ -184,8 +184,19 @@ Hosted deploy currently rejects local or package-owned web fonts before authenti
|
|
|
184
184
|
writes. Use self-hosted delivery or an explicit public glyph provider. Enabling managed font bundles
|
|
185
185
|
requires a separately deployed ownership, quota, retention, and immutable-delivery contract.
|
|
186
186
|
|
|
187
|
-
|
|
187
|
+
A map's own 3D building models (the building models of its `models` directories) deploy as one
|
|
188
|
+
model bundle per map. Deploy prepares them as `build` does, uploads the bundle with
|
|
189
|
+
`PUT /v1/model-bundles/:contentHash` after authentication and before the first style write, compiles
|
|
190
|
+
each placement's `url` at the bundle's hosted address and publishes the style with the bundle's
|
|
191
|
+
content hash. Byte-identical bundles are reused by content hash. A bundle holds at most 64 models,
|
|
192
|
+
16 MiB a prepared file and 32 MiB together, and shares the organization's hosted-storage pool like
|
|
193
|
+
icon packages. Upload success must confirm the submitted `contentHash`, model count and byte total,
|
|
194
|
+
one opaque `mdb_<id>` and the query-free `/model-bundles/<id>` base URL. A deploy with
|
|
195
|
+
`--with-native` rejects a map's own building models, because MapLibre Native draws no models.
|
|
196
|
+
|
|
197
|
+
For every managed resource kind, `assetSetSha256` remains a separate per-map output identity. It
|
|
188
198
|
hashes each generated file's exact portable name, media type, byte count, and SHA-256 using Core's
|
|
189
199
|
asset-set v1 contract; it does not hash only the icon-package manifest. Hosted must
|
|
190
200
|
confirm the immutable resource bytes, reconstruct those same per-file identities (including the
|
|
191
|
-
`icons/<mapId>/`
|
|
201
|
+
`icons/<mapId>/` and `models/<mapId>/` prefixes), and reject the Style when the submitted
|
|
202
|
+
build-manifest hash diverges.
|
|
@@ -62,8 +62,8 @@ authoring:
|
|
|
62
62
|
```sh
|
|
63
63
|
# Two maps or themes from one config.
|
|
64
64
|
npx tileflow preview \
|
|
65
|
-
--map
|
|
66
|
-
--against-map
|
|
65
|
+
--map baedeker --theme light \
|
|
66
|
+
--against-map map-1777 --against-theme light
|
|
67
67
|
|
|
68
68
|
# Or compare against another config.
|
|
69
69
|
npx tileflow preview \
|
package/docs/map-authoring.md
CHANGED
|
@@ -25,6 +25,7 @@ Start with bounded queries, then request more detail at the returned JSON Pointe
|
|
|
25
25
|
```sh
|
|
26
26
|
npx tileflow describe --json
|
|
27
27
|
npx tileflow describe roads --path /properties/classes --json
|
|
28
|
+
npx tileflow describe themes --json
|
|
28
29
|
npx tileflow inspect --domain roads --limit 80 --json
|
|
29
30
|
npx tileflow inspect compiled --domain roads --theme dark --json
|
|
30
31
|
npx tileflow compare configs --from-config before/tileflow.config.ts --to-config after/tileflow.config.ts --json
|
|
@@ -54,6 +55,12 @@ Inspection includes resolved inheritance, merge provenance, and the theme contra
|
|
|
54
55
|
returns compilation diagnostics and a report, not the complete Style JSON. Add `--inspection` only
|
|
55
56
|
when physical output provenance is needed; those layer IDs are not stable authoring targets.
|
|
56
57
|
|
|
58
|
+
`describe themes` lists the minimal palette roles and optional time-of-day behavior. Compiled
|
|
59
|
+
inspection includes `report.decisions` for resolved color roles and selected module defaults,
|
|
60
|
+
with the effective value, source, reason and override guidance. Query it with
|
|
61
|
+
`inspect compiled --path /report/decisions --limit 80 --json`. It describes semantic decisions,
|
|
62
|
+
not every final paint property; config inspection retains inheritance provenance separately.
|
|
63
|
+
|
|
57
64
|
For a normal config, map selection is implicit. Paired `--from-config` and `--to-config` compare
|
|
58
65
|
two ordinary configs. Workspace comparisons use their documented explicit map selectors; do not
|
|
59
66
|
mix the paired-config and same-workspace forms.
|
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.14",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "CLI-only tools for Tileflow config, capture, visual testing, and deploy.",
|
|
6
6
|
"type": "module",
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
"@tileflow/capture": ">=0.1.0-alpha.31 <0.1.0-beta.0",
|
|
40
40
|
"@tileflow/coordinates": ">=0.1.0-alpha.2 <0.1.0-beta.0",
|
|
41
41
|
"@tileflow/coordinates-runtime": ">=0.1.0-alpha.5 <0.1.0-beta.0",
|
|
42
|
-
"@tileflow/core": ">=0.1.0-alpha.
|
|
43
|
-
"@tileflow/dev": ">=0.1.0-alpha.
|
|
44
|
-
"@tileflow/maps": ">=0.1.0-alpha.
|
|
42
|
+
"@tileflow/core": ">=0.1.0-alpha.36 <0.1.0-beta.0",
|
|
43
|
+
"@tileflow/dev": ">=0.1.0-alpha.33 <0.1.0-beta.0",
|
|
44
|
+
"@tileflow/maps": ">=0.1.0-alpha.10 <0.1.0-beta.0",
|
|
45
45
|
"commander": "^15.0.0",
|
|
46
46
|
"hono": "4.13.7",
|
|
47
47
|
"picocolors": "^1.1.1",
|