passerelle-geo-components 0.2.0-alpha.1

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.
@@ -0,0 +1,66 @@
1
+ import { z } from "zod"
2
+
3
+ export const SceneManifestSchema = z.object({ "$schema": z.string().url().describe("Pointeur JSON Schema self-description (facultatif si absent).").optional(), "manifest_version": z.string().regex(new RegExp("^0\\.3\\.[0-9]+$")).describe("Version SemVer strict du contract. V0.3.x lit tout 0.3.y avec y ≤ x (compat descendante 2 versions mineures).").readonly(), "scene_hash": z.string().regex(new RegExp("^sha256:[0-9a-f]{64}$")).describe("Hash SHA-256 canonique du manifest_body. Calculé UNIQUEMENT côté producteur (au snapshot dans scene_store/). Le consommateur ne recalcule jamais côté main thread. Helper computeSceneHashInWorker() disponible pour vérification offline.").readonly().optional(), "manifest_id": z.string().uuid().describe("UUID4 unique du manifest. Généré au publish.").readonly().optional(), "produced_at": z.string().datetime({ offset: true }).describe("Timestamp ISO 8601 avec TZ. Généré au publish.").readonly(), "title": z.string().max(300).describe("Titre principal de la scène (affiché en header carto)."), "subtitle": z.string().max(300).describe("Sous-titre optionnel.").optional(), "description": z.string().max(2000).describe("Description longue (paragraphe intro).").optional(), "source_text": z.string().max(500).describe("Citation datée des sources de données (BD TOPO IGN 2024, ...).").optional(), "caveat": z.string().max(2000).describe("Disclaimer méthodologique (marge d'erreur, données pédagogiques, ...).").optional(), "provenance": z.any(), "projection": z.any().superRefine((x, ctx) => {
4
+ const schemas = [z.literal("mercator"), z.literal("globe"), z.literal("lambert93"), z.string().regex(new RegExp("^x_[a-z_]+$"))];
5
+ const { errors, failed } = schemas.reduce<{
6
+ errors: z.core.$ZodIssue[];
7
+ failed: number;
8
+ }>(
9
+ ({ errors, failed }, schema) =>
10
+ ((result) =>
11
+ result.error
12
+ ? {
13
+ errors: [...errors, ...result.error.issues],
14
+ failed: failed + 1,
15
+ }
16
+ : { errors, failed })(
17
+ schema.safeParse(x),
18
+ ),
19
+ { errors: [], failed: 0 },
20
+ );
21
+ const passed = schemas.length - failed;
22
+ if (passed !== 1) {
23
+ ctx.addIssue(errors.length ? {
24
+ path: [],
25
+ code: "invalid_union",
26
+ errors: [errors],
27
+ message: "Invalid input: Should pass single schema. Passed " + passed,
28
+ } : {
29
+ path: [],
30
+ code: "custom",
31
+ errors: [errors],
32
+ message: "Invalid input: Should pass single schema. Passed " + passed,
33
+ });
34
+ }
35
+ }).describe("Projection cartographique. 'mercator' (défaut), 'globe' (MapLibre v5+), 'lambert93' (via adapter proj-lambert93). Enum ouverte : accepte x_<projet>_* avec fallback graceful 'mercator'.").default("mercator"), "basemap": z.any(), "basemap_switcher": z.array(z.string()).describe("IDs de basemaps proposés en toggle runtime UI (opt.). Défaut : uniquement basemap.id actuel.").optional(), "zone": z.any(), "camera_presets": z.array(z.any()).describe("Presets caméra nommés (top, 3d, street, ...). Utilisés par API impérative flyToPreset().").optional(), "terrain": z.any().optional(), "sky": z.any().optional(), "layers": z.array(z.any()).describe("Couches cartographiques. Ordre = ordre de rendu (z_index respecté si défini)."), "custom_layers": z.array(z.any()).describe("Couches custom Three.js. Trigger dimension='3d' auto (lib dérivée). Chargé uniquement si <geo-3d-scene> composition slot présent.").optional(), "legend": z.any().optional(), "scalebar": z.object({ "position": z.any().optional(), "unit": z.enum(["metric","imperial","nautical"]).default("metric") }).strict().optional(), "north_arrow": z.object({ "position": z.any().optional() }).strict().optional(), "counters": z.array(z.any()).describe("Compteurs live bindés (nb features filtrés, %, ...).").optional(), "extensions": z.record(z.string(), z.union([z.record(z.string(), z.any()), z.never()])).superRefine((value, ctx) => {
36
+ for (const key in value) {
37
+ let evaluated = false
38
+ if (key.match(new RegExp("^[a-z][a-z_0-9]*$"))) {
39
+ evaluated = true
40
+ const result = z.record(z.string(), z.any()).safeParse(value[key])
41
+ if (!result.success) {
42
+ ctx.addIssue({
43
+ path: [key],
44
+ code: 'custom',
45
+ message: `Invalid input: Key matching regex /${key}/ must match schema`,
46
+ params: {
47
+ issues: result.error.issues
48
+ }
49
+ })
50
+ }
51
+ }
52
+ if (!evaluated) {
53
+ const result = z.never().safeParse(value[key])
54
+ if (!result.success) {
55
+ ctx.addIssue({
56
+ path: [key],
57
+ code: 'custom',
58
+ message: `Invalid input: must match catchall schema`,
59
+ params: {
60
+ issues: result.error.issues
61
+ }
62
+ })
63
+ }
64
+ }
65
+ }
66
+ }).describe("Extensions par-projet indexees par prefix (x_livrables, x_atlas, x_sig, x_zebra, ...). Contenu libre (additionalProperties: true).").optional() }).strict().describe("Contract unifie passerelle-geo-components pivot cross-projet (workspace SIG + editeur livrables + widget carto). Voir CONTRACT-V0.3.1.md pour la spec humaine complete.")