@carrot-foundation/schemas 0.1.21 โ†’ 0.1.23

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 CHANGED
@@ -14,15 +14,60 @@ They are versioned, publicly referenceable, and used for validation, traceabilit
14
14
 
15
15
  ## ๐Ÿ”– Versioning
16
16
 
17
- - Schemas are versioned using isolated versioning; each schema can evolve independently.
18
- - For released versions, each schemaโ€™s `$id` must point to the tagged raw URL to remain immutable.
19
- - During development on `main`, `$id` may reference `refs/heads/main`, but consumers should pin to tags in production.
20
- - Keep `$ref` paths relative; they resolve against the `$id` base (the tag) at validation time.
17
+ This project uses automated schema reference versioning with a **unified versioning approach** for both development and production environments. All schema `$id` fields always reference Git tags, ensuring consistency across all environments.
21
18
 
22
- Example `$id` pinned to a tag:
19
+ ### How It Works
20
+
21
+ - **All builds**: Schemas reference `refs/tags/{version}` where version is set via `SCHEMA_VERSION` environment variable
22
+ - **Default version**: If `SCHEMA_VERSION` is not set, defaults to `0.0.0-dev`
23
+ - **No special cases**: Development and production use the exact same versioning mechanism
24
+
25
+ ### Local Development
26
+
27
+ For local development, the default version `0.0.0-dev` is used automatically:
28
+
29
+ ```bash
30
+ # Build with default dev version (0.0.0-dev)
31
+ pnpm build
32
+ pnpm generate-ipfs-schemas
33
+ ```
34
+
35
+ ### Development Tag Management
36
+
37
+ To test a versioned build locally:
38
+
39
+ ```bash
40
+ # Build with a specific version
41
+ SCHEMA_VERSION=1.2.3 pnpm build
42
+ SCHEMA_VERSION=1.2.3 pnpm generate-ipfs-schemas
43
+ SCHEMA_VERSION=1.2.3 pnpm verify-schema-versions
44
+ ```
45
+
46
+ ### Release Process
47
+
48
+ The release process automatically:
49
+
50
+ 1. Calculates the next version based on conventional commits
51
+ 2. Builds the package with the new version embedded
52
+ 3. Generates JSON schemas with versioned `$id` references
53
+ 4. Creates a Git tag
54
+ 5. Publishes to npm
55
+ 6. Creates a GitHub release
56
+
57
+ All of this happens automatically via the GitHub Actions workflow when you push to `main`.
58
+
59
+ ### Example Schema References
60
+
61
+ **Development (default):**
62
+
63
+ ```json
64
+ "$id": "https://raw.githubusercontent.com/carrot-foundation/schemas/refs/tags/0.0.0-dev/schemas/ipfs/mass-id/mass-id.schema.json"
65
+ ```
66
+
67
+ **Production (versioned):**
23
68
 
24
69
  ```json
25
- "$id": "https://raw.githubusercontent.com/carrot-foundation/schemas/refs/tags/v0.1.0/schemas/ipfs/collection/collection.schema.json"
70
+ "$id": "https://raw.githubusercontent.com/carrot-foundation/schemas/refs/tags/1.2.3/schemas/ipfs/mass-id/mass-id.schema.json"
26
71
  ```
27
72
 
28
73
  ## ๐Ÿ“š Package Usage
@@ -51,7 +96,10 @@ const result = MassIDIpfsSchema.safeParse(data);
51
96
  ### CommonJS
52
97
 
53
98
  ```javascript
54
- const { MassIDIpfsSchema, MassIDDataSchema } = require('@carrot-foundation/schemas');
99
+ const {
100
+ MassIDIpfsSchema,
101
+ MassIDDataSchema,
102
+ } = require('@carrot-foundation/schemas');
55
103
 
56
104
  // Use the schemas for validation
57
105
  const result = MassIDIpfsSchema.safeParse(data);
package/dist/index.cjs CHANGED
@@ -35,6 +35,11 @@ var IsoDateSchema = zod.z.iso.date("Must be a valid ISO 8601 date (YYYY-MM-DD)")
35
35
  description: "ISO 8601 formatted date in YYYY-MM-DD format",
36
36
  examples: ["2024-12-05", "2025-02-22", "2024-02-10"]
37
37
  });
38
+ var UnixTimestampSchema = zod.z.number().int().positive().meta({
39
+ title: "Unix Timestamp",
40
+ description: "Unix timestamp in milliseconds since epoch (January 1, 1970 00:00:00 UTC)",
41
+ examples: [17040672e5, 17356896e5, 1762371245149]
42
+ });
38
43
  var IsoCountryCodeSchema = zod.z.string().regex(/^[A-Z]{2}$/, "Must be a valid ISO 3166-1 alpha-2 country code").meta({
39
44
  title: "ISO Country Code",
40
45
  description: "Two-letter country code following ISO 3166-1 alpha-2 standard",
@@ -48,14 +53,14 @@ var IsoAdministrativeDivisionCodeSchema = zod.z.string().regex(
48
53
  description: "Administrative division code following ISO 3166-2 standard",
49
54
  examples: ["BR-AP", "BR-ES", "US-CA"]
50
55
  });
51
- var LatitudeSchema = zod.z.number().min(-90).max(90).meta({
56
+ var LatitudeSchema = zod.z.number().min(-90).max(90).multipleOf(1e-3).meta({
52
57
  title: "Latitude",
53
- description: "Geographic latitude coordinate in decimal degrees",
54
- examples: [-0.02, -20.38, 40.7128]
58
+ description: "Geographic latitude coordinate in decimal degrees with maximum 3 decimal places precision (~100m-1km accuracy for city-level, non-PII compliance)",
59
+ examples: [-0.02, -20.38, 40.713]
55
60
  });
56
- var LongitudeSchema = zod.z.number().min(-180).max(180).meta({
61
+ var LongitudeSchema = zod.z.number().min(-180).max(180).multipleOf(1e-3).meta({
57
62
  title: "Longitude",
58
- description: "Geographic longitude coordinate in decimal degrees",
63
+ description: "Geographic longitude coordinate in decimal degrees with maximum 3 decimal places precision (~100m-1km accuracy for city-level, non-PII compliance)",
59
64
  examples: [-51.06, -40.34, -74.006]
60
65
  });
61
66
  var WeightKgSchema = zod.z.number().min(0).meta({
@@ -97,19 +102,18 @@ var ParticipantNameSchema = NonEmptyStringSchema.max(100).meta({
97
102
  examples: ["Enlatados Produ\xE7\xE3o", "Eco Reciclagem", "Green Tech Corp"]
98
103
  });
99
104
  var FacilityTypeSchema = zod.z.enum([
100
- "Waste Generation",
101
105
  "Collection Point",
102
- "Transfer Station",
103
- "Sorting Facility",
104
- "Composting Facility",
105
106
  "Recycling Facility",
106
- "Processing Facility",
107
- "Disposal Facility",
108
- "Administrative Office"
107
+ "Administrative Office",
108
+ "Other"
109
109
  ]).meta({
110
110
  title: "Facility Type",
111
- description: "Type of facility in the waste management infrastructure",
112
- examples: ["Waste Generation", "Recycling Facility", "Collection Point"]
111
+ description: "Type of facility in the waste management chain",
112
+ examples: [
113
+ "Collection Point",
114
+ "Recycling Facility",
115
+ "Administrative Office"
116
+ ]
113
117
  });
114
118
  var BlockchainChainIdSchema = zod.z.number().int().min(1).meta({
115
119
  title: "Chain ID",
@@ -267,9 +271,9 @@ var CoordinatesSchema = zod.z.strictObject({
267
271
  description: "GPS coordinates of the location"
268
272
  });
269
273
  var LocationSchema = zod.z.strictObject({
270
- id: UuidSchema.meta({
271
- title: "Location ID",
272
- description: "Unique identifier for the location"
274
+ id_hash: Sha256HashSchema.meta({
275
+ title: "Location ID Hash",
276
+ description: "Anonymized identifier for the location"
273
277
  }),
274
278
  municipality: NonEmptyStringSchema.max(50).meta({
275
279
  title: "Municipality",
@@ -294,9 +298,9 @@ var LocationSchema = zod.z.strictObject({
294
298
  title: "Country Code",
295
299
  description: "ISO 3166-1 alpha-2 country code"
296
300
  }),
297
- responsible_participant_id: UuidSchema.meta({
298
- title: "Responsible Participant ID",
299
- description: "ID of the participant responsible for this location"
301
+ responsible_participant_id_hash: Sha256HashSchema.meta({
302
+ title: "Responsible Participant ID Hash",
303
+ description: "Anonymized ID of the participant responsible for this location"
300
304
  }),
301
305
  coordinates: CoordinatesSchema,
302
306
  facility_type: FacilityTypeSchema.optional().meta({
@@ -326,9 +330,9 @@ function uniqueBy(schema, selector, errorMessage = "Items must be unique") {
326
330
 
327
331
  // src/shared/entities/participant.schema.ts
328
332
  var ParticipantSchema = zod.z.strictObject({
329
- id: UuidSchema.meta({
330
- title: "Participant ID",
331
- description: "Unique identifier for the participant"
333
+ id_hash: Sha256HashSchema.meta({
334
+ title: "Participant ID Hash",
335
+ description: "Anonymized identifier for the participant"
332
336
  }),
333
337
  name: ParticipantNameSchema.meta({
334
338
  title: "Participant Name",
@@ -363,15 +367,10 @@ var MassIDLocalClassificationSchema = zod.z.strictObject({
363
367
  "Municipal solid waste - organic fraction"
364
368
  ]
365
369
  }),
366
- system: NonEmptyStringSchema.max(50).meta({
370
+ system: zod.z.enum(["IBAMA"]).meta({
367
371
  title: "Classification System",
368
- description: 'Classification system name (e.g., "Ibama Waste Code", "European Waste Catalogue", "US EPA Codes")',
369
- examples: [
370
- "European Waste Catalogue",
371
- "US EPA Codes",
372
- "Ibama Waste Code",
373
- "Brazilian ABNT Classification"
374
- ]
372
+ description: "Classification system name - currently supports IBAMA (Instituto Brasileiro do Meio Ambiente e dos Recursos Naturais Renov\xE1veis)",
373
+ examples: ["IBAMA"]
375
374
  })
376
375
  }).meta({
377
376
  title: "Local Classification",
@@ -387,10 +386,10 @@ var ContaminationLevelSchema = zod.z.enum(["None", "Low", "Medium", "High"]).met
387
386
  description: "Level of contamination in the waste batch",
388
387
  examples: ["Low", "Medium", "None"]
389
388
  });
390
- var MassIDWasteClassificationSchema = zod.z.strictObject({
391
- primary_type: WasteTypeSchema.meta({
392
- title: "Primary Waste Type",
393
- description: "Primary waste material category"
389
+ var MassIDWastePropertiesSchema = zod.z.strictObject({
390
+ type: WasteTypeSchema.meta({
391
+ title: "Waste Type",
392
+ description: "Waste material category"
394
393
  }),
395
394
  subtype: WasteSubtypeSchema.meta({
396
395
  title: "Waste Subtype",
@@ -404,8 +403,8 @@ var MassIDWasteClassificationSchema = zod.z.strictObject({
404
403
  }),
405
404
  contamination_level: ContaminationLevelSchema.optional()
406
405
  }).meta({
407
- title: "Waste Classification",
408
- description: "Standardized waste material classification and regulatory information"
406
+ title: "Waste Properties",
407
+ description: "Standardized waste material properties and regulatory information"
409
408
  });
410
409
  var EventAttributeFormatSchema = zod.z.enum(["KILOGRAM", "DATE", "CURRENCY", "PERCENTAGE", "COORDINATE"]).meta({
411
410
  title: "Event Attribute Format",
@@ -427,7 +426,7 @@ var EventAttributeSchema = zod.z.strictObject({
427
426
  "processing_cost"
428
427
  ]
429
428
  }),
430
- value: zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean()]).meta({
429
+ value: zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean()]).optional().meta({
431
430
  title: "Attribute Value",
432
431
  description: "Event attribute value",
433
432
  examples: [
@@ -441,6 +440,10 @@ var EventAttributeSchema = zod.z.strictObject({
441
440
  "OP-456"
442
441
  ]
443
442
  }),
443
+ preserved_sensitivity: zod.z.boolean().optional().meta({
444
+ title: "Preserved Sensitivity",
445
+ description: "Indicates if the attribute contains sensitive information that was preserved"
446
+ }),
444
447
  format: EventAttributeFormatSchema.optional()
445
448
  }).meta({
446
449
  title: "Event Attribute",
@@ -529,12 +532,12 @@ var MassIDChainOfCustodyEventSchema = zod.z.strictObject({
529
532
  title: "Event Timestamp",
530
533
  description: "ISO 8601 timestamp when the event occurred"
531
534
  }),
532
- participant_id: UuidSchema.meta({
533
- title: "Participant ID",
535
+ participant_id_hash: Sha256HashSchema.meta({
536
+ title: "Participant ID Hash",
534
537
  description: "Reference to participant in the participants array"
535
538
  }),
536
- location_id: UuidSchema.meta({
537
- title: "Location ID",
539
+ location_id_hash: Sha256HashSchema.meta({
540
+ title: "Location ID Hash",
538
541
  description: "Reference to location in the locations array"
539
542
  }),
540
543
  weight: NonNegativeFloatSchema.optional().meta({
@@ -575,12 +578,12 @@ var MassIDChainOfCustodySchema = zod.z.strictObject({
575
578
  description: "Complete chain of custody tracking from waste generation to final processing"
576
579
  });
577
580
  var MassIDTransportRouteSchema = zod.z.strictObject({
578
- from_location_id: UuidSchema.meta({
579
- title: "From Location ID",
581
+ from_location_id_hash: Sha256HashSchema.meta({
582
+ title: "From Location ID Hash",
580
583
  description: "Reference to the origin location in the locations array"
581
584
  }),
582
- to_location_id: UuidSchema.meta({
583
- title: "To Location ID",
585
+ to_location_id_hash: Sha256HashSchema.meta({
586
+ title: "To Location ID Hash",
584
587
  description: "Reference to the destination location in the locations array"
585
588
  }),
586
589
  distance_km: NonNegativeFloatSchema.meta({
@@ -610,16 +613,16 @@ var MassIDTransportRouteSchema = zod.z.strictObject({
610
613
  description: "Transport route segment information"
611
614
  });
612
615
  var MassIDGeographicDataSchema = zod.z.strictObject({
613
- origin_location_id: UuidSchema.meta({
614
- title: "Origin Location ID",
616
+ origin_location_id_hash: Sha256HashSchema.meta({
617
+ title: "Origin Location ID Hash",
615
618
  description: "Reference to origin location in the locations array"
616
619
  }),
617
- processing_location_ids: zod.z.array(UuidSchema).optional().meta({
618
- title: "Processing Location IDs",
620
+ processing_location_id_hashes: zod.z.array(Sha256HashSchema).optional().meta({
621
+ title: "Processing Location ID Hashes",
619
622
  description: "Locations where the waste was processed or handled"
620
623
  }),
621
- final_destination_id: UuidSchema.meta({
622
- title: "Final Destination ID",
624
+ final_destination_id_hash: Sha256HashSchema.meta({
625
+ title: "Final Destination ID Hash",
623
626
  description: "Reference to final destination in the locations array"
624
627
  }),
625
628
  transport_routes: zod.z.array(MassIDTransportRouteSchema).meta({
@@ -631,19 +634,19 @@ var MassIDGeographicDataSchema = zod.z.strictObject({
631
634
  description: "Geographic information about waste origin and processing locations"
632
635
  });
633
636
  var MassIDDataSchema = zod.z.strictObject({
634
- waste_classification: MassIDWasteClassificationSchema,
637
+ waste_properties: MassIDWastePropertiesSchema,
635
638
  locations: uniqueBy(
636
639
  LocationSchema,
637
- (loc) => loc.id,
638
- "Location IDs must be unique"
640
+ (loc) => loc.id_hash,
641
+ "Location ID hashes must be unique"
639
642
  ).min(1).meta({
640
643
  title: "Locations",
641
644
  description: "All locations referenced in this MassID, indexed by ID"
642
645
  }),
643
646
  participants: uniqueBy(
644
647
  ParticipantSchema,
645
- (p) => p.id,
646
- "Participant IDs must be unique"
648
+ (p) => p.id_hash,
649
+ "Participant ID hashes must be unique"
647
650
  ).min(1).meta({
648
651
  title: "Participants",
649
652
  description: "All participants referenced in this MassID, indexed by ID"
@@ -652,27 +655,27 @@ var MassIDDataSchema = zod.z.strictObject({
652
655
  geographic_data: MassIDGeographicDataSchema
653
656
  }).refine((data) => {
654
657
  const participantIdSet = new Set(
655
- data.participants.map((participant) => participant.id)
658
+ data.participants.map((participant) => participant.id_hash)
656
659
  );
657
660
  const eventParticipantIds = data.chain_of_custody.events.map(
658
- (event) => event.participant_id
661
+ (event) => event.participant_id_hash
659
662
  );
660
663
  const allEventParticipantsExist = eventParticipantIds.every(
661
664
  (participantId) => participantIdSet.has(participantId)
662
665
  );
663
666
  return allEventParticipantsExist;
664
- }, "All participant IDs in chain of custody events must exist in participants array").refine((data) => {
667
+ }, "All participant ID hashes in chain of custody events must exist in participants array").refine((data) => {
665
668
  const locationIdSet = new Set(
666
- data.locations.map((location) => location.id)
669
+ data.locations.map((location) => location.id_hash)
667
670
  );
668
671
  const eventLocationIds = data.chain_of_custody.events.map(
669
- (event) => event.location_id
672
+ (event) => event.location_id_hash
670
673
  );
671
674
  const allEventLocationsExist = eventLocationIds.every(
672
675
  (locationId) => locationIdSet.has(locationId)
673
676
  );
674
677
  return allEventLocationsExist;
675
- }, "All location IDs in chain of custody events must exist in locations array").meta({
678
+ }, "All location ID hashes in chain of custody events must exist in locations array").meta({
676
679
  title: "MassID Data",
677
680
  description: "MassID data containing waste tracking and chain of custody information"
678
681
  });
@@ -696,7 +699,7 @@ var RecordCreatorSchema = zod.z.strictObject({
696
699
  name: zod.z.string().meta({
697
700
  title: "Creator Name",
698
701
  description: "Company or individual name that created this record",
699
- examples: ["Carrot Foundation", "Alice", "Bob"]
702
+ examples: ["Carrot Foundation"]
700
703
  }),
701
704
  id: UuidSchema.meta({
702
705
  title: "Creator ID",
@@ -964,6 +967,18 @@ var NftIpfsSchema = BaseIpfsSchema.safeExtend({
964
967
  description: "NFT-specific fields for Carrot IPFS records"
965
968
  });
966
969
 
970
+ // src/shared/schema-version.ts
971
+ function getSchemaBaseUrl() {
972
+ return `https://raw.githubusercontent.com/carrot-foundation/schemas/refs/tags/${getSchemaVersionOrDefault()}/schemas/ipfs`;
973
+ }
974
+ function buildSchemaUrl(schemaPath) {
975
+ const cleanPath = schemaPath.startsWith("/") ? schemaPath.slice(1) : schemaPath;
976
+ return `${getSchemaBaseUrl()}/${cleanPath}`;
977
+ }
978
+ function getSchemaVersionOrDefault() {
979
+ return "0.0.0-dev";
980
+ }
981
+
967
982
  // src/mass-id/mass-id.schema.ts
968
983
  var AttributeWasteTypeSchema = zod.z.strictObject({
969
984
  trait_type: zod.z.literal("Waste Type"),
@@ -1039,14 +1054,14 @@ var AttributeIntegratorSchema = zod.z.strictObject({
1039
1054
  });
1040
1055
  var AttributePickUpDateSchema = zod.z.strictObject({
1041
1056
  trait_type: zod.z.literal("Pick-up Date"),
1042
- value: zod.z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Must be a valid date in YYYY-MM-DD format").meta({
1057
+ value: UnixTimestampSchema.meta({
1043
1058
  title: "Pick-up Date Value",
1044
- description: "Date when the waste was picked up from the source"
1059
+ description: "Unix timestamp in milliseconds when the waste was picked up from the source"
1045
1060
  }),
1046
1061
  display_type: zod.z.literal("date")
1047
1062
  }).meta({
1048
1063
  title: "Pick-up Date Attribute",
1049
- description: "Pick-up date attribute"
1064
+ description: "Pick-up date attribute with Unix timestamp"
1050
1065
  });
1051
1066
  var AttributeRecyclingDateSchema = zod.z.strictObject({
1052
1067
  trait_type: zod.z.literal("Recycling Date"),
@@ -1077,8 +1092,8 @@ var MassIDAttributesSchema = zod.z.tuple([
1077
1092
  var MassIDIpfsSchemaMeta = {
1078
1093
  title: "MassID NFT IPFS Record",
1079
1094
  description: "Complete MassID NFT IPFS record including fixed attributes and detailed waste tracking data",
1080
- $id: "https://raw.githubusercontent.com/carrot-foundation/schemas/refs/heads/main/schemas/ipfs/mass-id/mass-id.schema.json",
1081
- version: "1.0.1"
1095
+ $id: buildSchemaUrl("mass-id/mass-id.schema.json"),
1096
+ version: getSchemaVersionOrDefault()
1082
1097
  };
1083
1098
  var MassIDIpfsSchema = NftIpfsSchema.safeExtend({
1084
1099
  schema: NftIpfsSchema.shape.schema.safeExtend({