@honua/app-platform 0.1.4-beta.0 → 0.1.7-beta.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.
Files changed (38) hide show
  1. package/app/index.d.ts.map +1 -1
  2. package/app/index.js +4 -0
  3. package/contract/types.d.ts +1 -1
  4. package/contract/types.d.ts.map +1 -1
  5. package/contract/types.js +4 -0
  6. package/core/client.d.ts +6 -0
  7. package/core/client.d.ts.map +1 -1
  8. package/core/client.js +11 -9
  9. package/core/error-classifications.d.ts +17 -0
  10. package/core/error-classifications.d.ts.map +1 -1
  11. package/core/error-classifications.js +17 -0
  12. package/core/error-code-registry.d.ts +18 -1
  13. package/core/error-code-registry.d.ts.map +1 -1
  14. package/core/error-code-registry.js +17 -0
  15. package/core/errors.d.ts +1 -1
  16. package/core/errors.d.ts.map +1 -1
  17. package/core/geoparquet-sql.d.ts +1 -1
  18. package/core/geoparquet-sql.js +2 -2
  19. package/core/stac.d.ts +2 -1
  20. package/core/stac.d.ts.map +1 -1
  21. package/core/stac.js +29 -11
  22. package/core/types.d.ts +15 -2
  23. package/core/types.d.ts.map +1 -1
  24. package/interactions/declarative-contract.d.ts +149 -0
  25. package/interactions/declarative-contract.d.ts.map +1 -0
  26. package/interactions/declarative-contract.js +293 -0
  27. package/interactions/declarative.d.ts +198 -0
  28. package/interactions/declarative.d.ts.map +1 -0
  29. package/interactions/declarative.js +470 -0
  30. package/interactions/feature-state.d.ts +7 -0
  31. package/interactions/feature-state.d.ts.map +1 -1
  32. package/interactions/feature-state.js +10 -2
  33. package/map/maplibre-target.js +1 -1
  34. package/package.json +2 -2
  35. package/realtime/sse.d.ts.map +1 -1
  36. package/realtime/sse.js +7 -1
  37. package/runtime/style-compose.d.ts.map +1 -1
  38. package/runtime/style-compose.js +78 -9
@@ -20,7 +20,8 @@ export async function composeStyle(pkg, mapSpecWithSources, options) {
20
20
  try {
21
21
  const withStyleRefs = await applyStyleRefs(mapSpecWithSources, pkg, options.resolveStyleRef);
22
22
  const theme = await resolveTheme(pkg, options.resolveTheme);
23
- return theme ? applyTheme(withStyleRefs, theme) : withStyleRefs;
23
+ const composed = theme ? applyTheme(withStyleRefs, theme) : withStyleRefs;
24
+ return pruneUndefined(composed);
24
25
  }
25
26
  catch (cause) {
26
27
  if (cause instanceof HonuaMapPackageError)
@@ -97,26 +98,94 @@ function mergeLayerOverride(layer, override) {
97
98
  ...(override.filter !== undefined ? { filter: override.filter } : {}),
98
99
  ...(override.minzoom !== undefined ? { minzoom: override.minzoom } : {}),
99
100
  ...(override.maxzoom !== undefined ? { maxzoom: override.maxzoom } : {}),
100
- paint: { ...(layer.paint ?? {}), ...(override.paint ?? {}) },
101
- layout: { ...(layer.layout ?? {}), ...(override.layout ?? {}) },
102
- metadata: { ...(layer.metadata ?? {}), ...(override.metadata ?? {}) },
101
+ ...optionalRecord("paint", { ...(layer.paint ?? {}), ...(override.paint ?? {}) }),
102
+ ...optionalRecord("layout", { ...(layer.layout ?? {}), ...(override.layout ?? {}) }),
103
+ ...optionalRecord("metadata", { ...(layer.metadata ?? {}), ...(override.metadata ?? {}) }),
103
104
  };
104
105
  }
105
106
  function cloneLayer(layer) {
106
107
  return {
107
108
  ...layer,
108
- paint: layer.paint ? { ...layer.paint } : undefined,
109
- layout: layer.layout ? { ...layer.layout } : undefined,
110
- metadata: layer.metadata ? { ...layer.metadata } : undefined,
109
+ ...optionalRecord("paint", layer.paint ? { ...layer.paint } : undefined),
110
+ ...optionalRecord("layout", layer.layout ? { ...layer.layout } : undefined),
111
+ ...optionalRecord("metadata", layer.metadata ? { ...layer.metadata } : undefined),
111
112
  };
112
113
  }
113
114
  function substituteLayerTokens(layer, tokens) {
114
115
  return {
115
116
  ...layer,
116
- paint: layer.paint ? substituteTokens(layer.paint, tokens) : layer.paint,
117
- layout: layer.layout ? substituteTokens(layer.layout, tokens) : layer.layout,
117
+ ...optionalRecord("paint", layer.paint ? substituteTokens(layer.paint, tokens) : undefined),
118
+ ...optionalRecord("layout", layer.layout ? substituteTokens(layer.layout, tokens) : undefined),
118
119
  };
119
120
  }
121
+ /**
122
+ * Spread helper for optional MapLibre layer properties: contributes the key
123
+ * only when there is something to contribute.
124
+ *
125
+ * `{ layout: undefined }` and "no `layout` key" are indistinguishable to
126
+ * `JSON.stringify`, but not to MapLibre's style validator — it reads the own
127
+ * property and rejects the layer with `layers[n].layout: object expected,
128
+ * undefined found`. A rejected style makes `setStyle(style, { diff: true })`
129
+ * a silent no-op, so the map stops updating with nothing thrown and nothing
130
+ * logged. An empty object is dropped for the same reason in spirit: it is
131
+ * meaningless in the document and only adds diff noise.
132
+ */
133
+ function optionalRecord(key, value) {
134
+ if (!value || Object.keys(value).length === 0)
135
+ return {};
136
+ return { [key]: value };
137
+ }
138
+ /**
139
+ * Drop present-but-`undefined` properties from the composed document.
140
+ *
141
+ * The composition helpers above never introduce them, but the input
142
+ * `mapSpec` can arrive with them already (hand-built styles, projections
143
+ * from another shape, `JSON`-roundtripped packages that were assembled with
144
+ * explicit `undefined`s). Composition is the last hop before `setStyle`, so
145
+ * it is the right place to guarantee the invariant for every consumer rather
146
+ * than have each one re-implement the pruning.
147
+ *
148
+ * Structure-sharing: a subtree with no `undefined` values is returned by
149
+ * reference, so a clean style allocates nothing. Only plain objects and
150
+ * arrays are traversed; anything else (class instances, typed arrays) is
151
+ * passed through untouched.
152
+ */
153
+ function pruneUndefined(value) {
154
+ return pruneUnknown(value);
155
+ }
156
+ function pruneUnknown(value) {
157
+ if (Array.isArray(value)) {
158
+ let changed = false;
159
+ const next = value.map((item) => {
160
+ const pruned = pruneUnknown(item);
161
+ if (pruned !== item)
162
+ changed = true;
163
+ return pruned;
164
+ });
165
+ return changed ? next : value;
166
+ }
167
+ if (!isPlainObject(value))
168
+ return value;
169
+ let changed = false;
170
+ const next = {};
171
+ for (const [key, entry] of Object.entries(value)) {
172
+ if (entry === undefined) {
173
+ changed = true;
174
+ continue;
175
+ }
176
+ const pruned = pruneUnknown(entry);
177
+ if (pruned !== entry)
178
+ changed = true;
179
+ next[key] = pruned;
180
+ }
181
+ return changed ? next : value;
182
+ }
183
+ function isPlainObject(value) {
184
+ if (typeof value !== "object" || value === null)
185
+ return false;
186
+ const proto = Object.getPrototypeOf(value);
187
+ return proto === Object.prototype || proto === null;
188
+ }
120
189
  function substituteTokens(obj, tokens) {
121
190
  const out = {};
122
191
  for (const [key, value] of Object.entries(obj)) {