@carrot-foundation/schemas 0.1.25 → 0.1.27

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/dist/index.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  var zod = require('zod');
4
4
 
5
- // src/mass-id/mass-id.data.schema.ts
5
+ // src/mass-id/mass-id.attributes.ts
6
6
  var UuidSchema = zod.z.uuidv4("Must be a valid UUID v4 string").meta({
7
7
  title: "UUID",
8
8
  description: "A universally unique identifier version 4",
@@ -30,7 +30,7 @@ var IsoTimestampSchema = zod.z.iso.datetime({
30
30
  description: "ISO 8601 formatted timestamp with timezone information",
31
31
  examples: ["2024-12-05T11:02:47.000Z", "2025-02-22T10:35:12.000Z"]
32
32
  });
33
- var IsoDateSchema = zod.z.iso.date("Must be a valid ISO 8601 date (YYYY-MM-DD)").meta({
33
+ zod.z.iso.date("Must be a valid ISO 8601 date (YYYY-MM-DD)").meta({
34
34
  title: "ISO Date",
35
35
  description: "ISO 8601 formatted date in YYYY-MM-DD format",
36
36
  examples: ["2024-12-05", "2025-02-22", "2024-02-10"]
@@ -81,7 +81,7 @@ NonEmptyStringSchema.regex(
81
81
  description: "URL-friendly identifier with lowercase letters, numbers, and hyphens",
82
82
  examples: ["mass-id-123", "recycled-plastic", "organic-waste"]
83
83
  });
84
- var WasteTypeSchema = NonEmptyStringSchema.meta({
84
+ var WasteTypeSchema = NonEmptyStringSchema.max(100).meta({
85
85
  title: "Waste Type",
86
86
  description: "Category or type of waste material",
87
87
  examples: ["Organic", "Plastic", "Metal"]
@@ -145,6 +145,11 @@ var HoursSchema = zod.z.number().min(0).multipleOf(0.1).meta({
145
145
  description: "Time duration in hours with 0.1 hour precision",
146
146
  examples: [72.5, 24, 168.5]
147
147
  });
148
+ var MinutesSchema = zod.z.number().int().min(0).meta({
149
+ title: "Minutes",
150
+ description: "Time duration in minutes",
151
+ examples: [4350, 1440, 10110]
152
+ });
148
153
  var IpfsUriSchema = NonEmptyStringSchema.regex(
149
154
  /^ipfs:\/\/[a-zA-Z0-9]+(\/.*)?$/,
150
155
  "Must be a valid IPFS URI with CID"
@@ -251,65 +256,117 @@ var RecordRelationshipTypeSchema = zod.z.enum([
251
256
  description: "Type of relationship between different entities in the system",
252
257
  examples: ["mass-id", "audit", "collection"]
253
258
  });
254
- var PrecisionLevelSchema = zod.z.enum(["exact", "neighborhood", "city", "region", "country"]).meta({
255
- title: "Precision Level",
256
- description: "Level of coordinate precision",
257
- examples: ["city", "exact", "neighborhood"]
259
+ var SchemaInfoSchema = zod.z.strictObject({
260
+ hash: Keccak256HashSchema.meta({
261
+ title: "Schema Hash",
262
+ description: "Keccak256 hash of the JSON Schema this record was validated against"
263
+ }),
264
+ type: RecordSchemaTypeSchema.meta({
265
+ title: "Schema Type",
266
+ description: "Type/category of this schema"
267
+ }),
268
+ version: SemanticVersionSchema.meta({
269
+ title: "Schema Version",
270
+ description: "Version of the schema, using semantic versioning"
271
+ })
272
+ }).meta({
273
+ title: "Schema Information"
258
274
  });
259
- var CoordinatesSchema = zod.z.strictObject({
260
- latitude: LatitudeSchema.meta({
261
- title: "Latitude",
262
- description: "GPS latitude coordinate"
275
+ var RecordCreatorSchema = zod.z.strictObject({
276
+ name: zod.z.string().meta({
277
+ title: "Creator Name",
278
+ description: "Company or individual name that created this record",
279
+ examples: ["Carrot Foundation"]
263
280
  }),
264
- longitude: LongitudeSchema.meta({
265
- title: "Longitude",
266
- description: "GPS longitude coordinate"
281
+ id: UuidSchema.meta({
282
+ title: "Creator ID",
283
+ description: "Unique identifier for the creator"
284
+ })
285
+ }).meta({
286
+ title: "Creator",
287
+ description: "Entity that created this record"
288
+ });
289
+ var RecordRelationshipSchema = zod.z.strictObject({
290
+ target_uri: IpfsUriSchema.meta({
291
+ title: "Target IPFS URI",
292
+ description: "Target IPFS URI of the referenced record"
267
293
  }),
268
- precision_level: PrecisionLevelSchema
294
+ type: RecordRelationshipTypeSchema.meta({
295
+ title: "Relationship Type",
296
+ description: "Type of relationship to the referenced record"
297
+ }),
298
+ description: zod.z.string().optional().meta({
299
+ title: "Relationship Description",
300
+ description: "Human-readable description of the relationship",
301
+ examples: [
302
+ "This record supersedes the previous version",
303
+ "Related carbon credit batch",
304
+ "Source document for this verification",
305
+ "Child record derived from this parent",
306
+ "Updated version of original record"
307
+ ]
308
+ })
269
309
  }).meta({
270
- title: "Coordinates",
271
- description: "GPS coordinates of the location"
310
+ title: "Relationship",
311
+ description: "Relationship to another IPFS record"
272
312
  });
273
- var LocationSchema = zod.z.strictObject({
274
- id_hash: Sha256HashSchema.meta({
275
- title: "Location ID Hash",
276
- description: "Anonymized identifier for the location"
313
+ var RecordEnvironmentSchema = zod.z.strictObject({
314
+ blockchain_network: zod.z.enum(["mainnet", "testnet"]).meta({
315
+ title: "Blockchain Network",
316
+ description: "Blockchain Network Environment"
277
317
  }),
278
- municipality: NonEmptyStringSchema.max(50).meta({
279
- title: "Municipality",
280
- description: "Municipality or city name",
281
- examples: ["New York", "S\xE3o Paulo", "London", "Tokyo"]
318
+ deployment: zod.z.enum(["production", "development", "testing"]).meta({
319
+ title: "Deployment Environment",
320
+ description: "System environment where this record was generated"
282
321
  }),
283
- administrative_division: NonEmptyStringSchema.max(50).meta({
284
- title: "Administrative Division",
285
- description: "State, province, or administrative region",
286
- examples: ["California", "Ontario", "Bavaria", "Queensland"]
322
+ data_set_name: zod.z.enum(["TEST", "PROD"]).meta({
323
+ title: "Data Set Name",
324
+ description: "Name of the data set for this record"
325
+ })
326
+ }).meta({
327
+ title: "Environment",
328
+ description: "Environment information"
329
+ });
330
+ var BaseIpfsSchema = zod.z.strictObject({
331
+ $schema: zod.z.url("Must be a valid URI").meta({
332
+ title: "JSON Schema URI",
333
+ description: "URI of the JSON Schema used to validate this record",
334
+ example: "https://raw.githubusercontent.com/carrot-foundation/schemas/refs/heads/main/schemas/ipfs/shared/base/base.schema.json"
287
335
  }),
288
- administrative_division_code: IsoAdministrativeDivisionCodeSchema.optional().meta({
289
- title: "Administrative Division Code",
290
- description: "ISO 3166-2 administrative division code"
336
+ schema: SchemaInfoSchema,
337
+ created_at: IsoTimestampSchema.meta({
338
+ title: "Created At",
339
+ description: "ISO 8601 creation timestamp for this record"
291
340
  }),
292
- country: NonEmptyStringSchema.max(50).meta({
293
- title: "Country",
294
- description: "Full country name in English",
295
- examples: ["United States", "Canada", "Germany", "Australia"]
341
+ external_id: ExternalIdSchema.meta({
342
+ title: "External ID",
343
+ description: "Off-chain reference ID (UUID from Carrot backend)"
296
344
  }),
297
- country_code: IsoCountryCodeSchema.meta({
298
- title: "Country Code",
299
- description: "ISO 3166-1 alpha-2 country code"
345
+ external_url: ExternalUrlSchema.meta({
346
+ title: "External URL",
347
+ description: "External URL of the content"
300
348
  }),
301
- responsible_participant_id_hash: Sha256HashSchema.meta({
302
- title: "Responsible Participant ID Hash",
303
- description: "Anonymized ID of the participant responsible for this location"
349
+ original_content_hash: Sha256HashSchema.meta({
350
+ title: "Original Content Hash",
351
+ description: "SHA-256 hash of the original JSON content including private data before schema validation"
304
352
  }),
305
- coordinates: CoordinatesSchema,
306
- facility_type: FacilityTypeSchema.optional().meta({
307
- title: "Facility Type",
308
- description: "Type of facility at this location"
353
+ content_hash: Sha256HashSchema.meta({
354
+ title: "Content Hash",
355
+ description: "SHA-256 hash of RFC 8785 canonicalized JSON after schema validation"
356
+ }),
357
+ creator: RecordCreatorSchema.optional(),
358
+ relationships: zod.z.array(RecordRelationshipSchema).optional().meta({
359
+ title: "Relationships",
360
+ description: "References to other IPFS records this record relates to"
361
+ }),
362
+ environment: RecordEnvironmentSchema.optional(),
363
+ data: zod.z.record(zod.z.string(), zod.z.unknown()).optional().meta({
364
+ title: "Custom Data",
365
+ description: "Custom data block that includes the record's data"
309
366
  })
310
367
  }).meta({
311
- title: "Location",
312
- description: "Geographic location with address and coordinate information"
368
+ title: "Base IPFS Record",
369
+ description: "Base fields for all Carrot IPFS records, providing common structure for any JSON content stored in IPFS"
313
370
  });
314
371
  function uniqueArrayItems(schema, errorMessage = "Array items must be unique") {
315
372
  return zod.z.array(schema).refine((items) => new Set(items).size === items.length, {
@@ -328,86 +385,527 @@ function uniqueBy(schema, selector, errorMessage = "Items must be unique") {
328
385
  );
329
386
  }
330
387
 
331
- // src/shared/entities/participant.schema.ts
332
- var ParticipantSchema = zod.z.strictObject({
333
- id_hash: Sha256HashSchema.meta({
334
- title: "Participant ID Hash",
335
- description: "Anonymized identifier for the participant"
388
+ // src/shared/nft.schema.ts
389
+ var NftSchemaTypeSchema = RecordSchemaTypeSchema.extract([
390
+ "MassID",
391
+ "RecycledID",
392
+ "GasID",
393
+ "PurchaseID"
394
+ ]).meta({
395
+ title: "NFT Schema Type",
396
+ description: "Type of schema for NFT records"
397
+ });
398
+ var BlockchainReferenceSchema = zod.z.strictObject({
399
+ smart_contract_address: EthereumAddressSchema.meta({
400
+ title: "Smart Contract Address"
336
401
  }),
337
- name: ParticipantNameSchema.meta({
338
- title: "Participant Name",
339
- description: "Name of the participant"
402
+ chain_id: BlockchainChainIdSchema.meta({
403
+ title: "Chain ID",
404
+ description: "Blockchain chain ID"
340
405
  }),
341
- roles: uniqueArrayItems(
342
- ParticipantRoleSchema,
343
- "Participant roles must be unique"
344
- ).min(1).meta({
345
- title: "Participant Roles",
346
- description: "Roles of the participant in the waste management supply chain"
406
+ network_name: zod.z.string().min(5).max(100).meta({
407
+ title: "Network Name",
408
+ description: "Name of the blockchain network"
409
+ }),
410
+ token_id: TokenIdSchema.meta({
411
+ title: "Token ID",
412
+ description: "NFT token ID"
347
413
  })
348
414
  }).meta({
349
- title: "Participant",
350
- description: "A participant in the waste management supply chain"
415
+ title: "Blockchain Information",
416
+ description: "Blockchain-specific information for the NFT"
351
417
  });
352
-
353
- // src/mass-id/mass-id.data.schema.ts
354
- var MassIDLocalClassificationSchema = zod.z.strictObject({
355
- code: NonEmptyStringSchema.max(20).meta({
356
- title: "Classification Code",
357
- description: "Local waste classification code",
358
- examples: ["20 01 01", "D001", "EWC-150101", "IBAMA-A001"]
418
+ var ExternalLinkSchema = zod.z.strictObject({
419
+ label: zod.z.string().min(1).max(50).meta({
420
+ title: "Link Label",
421
+ description: "Display name for the external link"
359
422
  }),
360
- description: NonEmptyStringSchema.max(200).meta({
361
- title: "Classification Description",
362
- description: "Local waste classification description",
363
- examples: [
364
- "Paper and cardboard packaging",
365
- "Ignitable waste",
366
- "Paper and cardboard packaging waste",
367
- "Municipal solid waste - organic fraction"
368
- ]
423
+ url: zod.z.url("Must be a valid URI").meta({
424
+ title: "Link URL",
425
+ description: "Direct URI to the linked resource"
369
426
  }),
370
- system: zod.z.enum(["IBAMA"]).meta({
371
- title: "Classification System",
372
- description: "Classification system name - currently supports IBAMA (Instituto Brasileiro do Meio Ambiente e dos Recursos Naturais Renov\xE1veis)",
373
- examples: ["IBAMA"]
427
+ description: zod.z.string().min(10).max(100).optional().meta({
428
+ title: "Link Description",
429
+ description: "Optional context about what the link provides"
374
430
  })
375
431
  }).meta({
376
- title: "Local Classification",
377
- description: "Local or regional waste classification codes and descriptions"
378
- });
379
- var MassIDMeasurementUnitSchema = zod.z.enum(["kg", "ton"]).meta({
380
- title: "Measurement Unit",
381
- description: "Unit of measurement for the waste quantity",
382
- examples: ["kg", "ton"]
383
- });
384
- var ContaminationLevelSchema = zod.z.enum(["None", "Low", "Medium", "High"]).meta({
385
- title: "Contamination Level",
386
- description: "Level of contamination in the waste batch",
387
- examples: ["Low", "Medium", "None"]
432
+ title: "External Link",
433
+ description: "External link with label and description"
388
434
  });
389
- var MassIDWastePropertiesSchema = zod.z.strictObject({
390
- type: WasteTypeSchema.meta({
391
- title: "Waste Type",
392
- description: "Waste material category"
435
+ var NftAttributeSchema = zod.z.strictObject({
436
+ trait_type: zod.z.string().max(50).meta({
437
+ title: "Trait Type",
438
+ description: "Name of the trait or attribute"
393
439
  }),
394
- subtype: WasteSubtypeSchema.meta({
395
- title: "Waste Subtype",
396
- description: "Specific subcategory of waste material"
440
+ value: zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean()]).meta({
441
+ title: "Trait Value",
442
+ description: "Value of the trait - can be string, number, or boolean"
397
443
  }),
398
- local_classification: MassIDLocalClassificationSchema.optional(),
399
- measurement_unit: MassIDMeasurementUnitSchema,
400
- net_weight: NonNegativeFloatSchema.meta({
401
- title: "Net Weight",
402
- description: "Net weight of the waste batch in the specified measurement unit"
444
+ display_type: zod.z.enum(["number", "date", "boost_number", "boost_percentage"]).optional().meta({
445
+ title: "Display Type",
446
+ description: "How the trait should be displayed in marketplace UIs"
403
447
  }),
404
- contamination_level: ContaminationLevelSchema.optional()
448
+ max_value: NonNegativeFloatSchema.optional().meta({
449
+ title: "Max Value",
450
+ description: "Maximum possible value for numeric traits"
451
+ })
405
452
  }).meta({
406
- title: "Waste Properties",
407
- description: "Standardized waste material properties and regulatory information"
453
+ title: "NFT Attribute",
454
+ description: "NFT attribute or trait with type and value"
408
455
  });
409
- var EventAttributeFormatSchema = zod.z.enum(["KILOGRAM", "DATE", "CURRENCY", "PERCENTAGE", "COORDINATE"]).meta({
410
- title: "Event Attribute Format",
456
+ var NftIpfsSchema = BaseIpfsSchema.safeExtend({
457
+ schema: BaseIpfsSchema.shape.schema.safeExtend({
458
+ type: NftSchemaTypeSchema.meta({
459
+ title: "NFT Schema Type",
460
+ description: "Type/category of this NFT schema"
461
+ })
462
+ }),
463
+ blockchain: BlockchainReferenceSchema,
464
+ name: zod.z.string().min(1).max(100).meta({
465
+ title: "NFT Name",
466
+ description: "Full display name for this NFT, including extra context",
467
+ examples: [
468
+ "MassID #123 \u2022 Organic \u2022 3.0t",
469
+ "RecycledID #456 \u2022 Plastic \u2022 2.5t",
470
+ "GasID #789 \u2022 Methane \u2022 1000 m\xB3"
471
+ ]
472
+ }),
473
+ short_name: zod.z.string().min(1).max(50).meta({
474
+ title: "Short Name",
475
+ description: "Compact name for UI summaries, tables, or tooltips",
476
+ examples: ["MassID #123", "RecycledID #456", "GasID #789"]
477
+ }),
478
+ description: zod.z.string().min(10).max(500).meta({
479
+ title: "Description",
480
+ description: "Human-readable summary of the NFT's role and context. Ideally, maximum 300 characters.",
481
+ examples: [
482
+ "This MassID represents 3 metric tons of organic food waste from Enlatados Produ\xE7\xE3o, tracked through complete chain of custody from generation to composting.",
483
+ "This RecycledID represents 2.5 metric tons of recycled plastic bottles processed by Green Solutions Ltd."
484
+ ]
485
+ }),
486
+ image: IpfsUriSchema.meta({
487
+ title: "Image URI",
488
+ description: "IPFS URI pointing to the preview image"
489
+ }),
490
+ background_color: HexColorSchema.optional().meta({
491
+ title: "Background Color",
492
+ description: "Hex color code for marketplace background display"
493
+ }),
494
+ animation_url: IpfsUriSchema.optional().meta({
495
+ title: "Animation URL",
496
+ description: "IPFS URI pointing to an animated or interactive media file",
497
+ examples: [
498
+ "ipfs://QmAnimation123/mass-id-animation.mp4",
499
+ "ipfs://QmInteractive456/recycled-visualization.webm"
500
+ ]
501
+ }),
502
+ external_links: uniqueBy(
503
+ ExternalLinkSchema,
504
+ (link) => link.url,
505
+ "External link URLs must be unique"
506
+ ).max(10).optional().meta({
507
+ title: "External Links",
508
+ description: "Optional list of public resource links with labels",
509
+ examples: [
510
+ [
511
+ {
512
+ label: "Carrot Explorer",
513
+ url: "https://explore.carrot.eco/document/ad44dd3f-f176-4b98-bf78-5ee6e77d0530",
514
+ description: "Complete chain of custody and audit trail"
515
+ },
516
+ {
517
+ label: "Carrot White Paper",
518
+ url: "https://carrot.eco/whitepaper.pdf",
519
+ description: "Carrot Foundation technical and impact white paper"
520
+ }
521
+ ]
522
+ ]
523
+ }),
524
+ attributes: uniqueBy(
525
+ NftAttributeSchema,
526
+ (attr) => attr.trait_type,
527
+ "Attribute trait_type values must be unique"
528
+ ).meta({
529
+ title: "NFT Attributes",
530
+ description: "List of visual traits and filterable attributes compatible with NFT marketplaces",
531
+ examples: [
532
+ [
533
+ {
534
+ trait_type: "Waste Type",
535
+ value: "Organic"
536
+ },
537
+ {
538
+ trait_type: "Waste Subtype",
539
+ value: "Food, Food Waste and Beverages"
540
+ },
541
+ {
542
+ trait_type: "Weight (kg)",
543
+ value: 3e3,
544
+ display_type: "number"
545
+ },
546
+ {
547
+ trait_type: "Origin Country",
548
+ value: "Brazil"
549
+ },
550
+ {
551
+ trait_type: "Pick-up Date",
552
+ value: "2024-12-05",
553
+ display_type: "date"
554
+ }
555
+ ]
556
+ ]
557
+ })
558
+ }).meta({
559
+ title: "NFT IPFS Record",
560
+ description: "NFT-specific fields for Carrot IPFS records"
561
+ });
562
+
563
+ // src/mass-id/mass-id.attributes.ts
564
+ var MassIDAttributeWasteTypeSchema = NftAttributeSchema.extend({
565
+ trait_type: zod.z.literal("Waste Type"),
566
+ value: WasteTypeSchema
567
+ }).meta({
568
+ title: "Waste Type Attribute",
569
+ description: "Waste type attribute"
570
+ });
571
+ var MassIDAttributeWasteSubtypeSchema = NftAttributeSchema.extend({
572
+ trait_type: zod.z.literal("Waste Subtype"),
573
+ value: WasteSubtypeSchema
574
+ }).meta({
575
+ title: "Waste Subtype Attribute",
576
+ description: "Waste subtype attribute"
577
+ });
578
+ var MassIDAttributeWeightSchema = NftAttributeSchema.extend({
579
+ trait_type: zod.z.literal("Weight (kg)"),
580
+ value: WeightKgSchema,
581
+ display_type: zod.z.literal("number")
582
+ }).meta({
583
+ title: "Weight Attribute",
584
+ description: "Weight attribute with numeric display"
585
+ });
586
+ var MassIDAttributeOriginCountrySchema = NftAttributeSchema.extend({
587
+ trait_type: zod.z.literal("Origin Country"),
588
+ value: NonEmptyStringSchema.max(100).meta({
589
+ title: "Origin Country Value",
590
+ description: "Country where the waste was generated",
591
+ examples: ["Brazil", "United States", "Germany", "Japan"]
592
+ })
593
+ }).meta({
594
+ title: "Origin Country Attribute",
595
+ description: "Origin country attribute"
596
+ });
597
+ var MassIDAttributeOriginMunicipalitySchema = NftAttributeSchema.extend({
598
+ trait_type: zod.z.literal("Origin Municipality"),
599
+ value: NonEmptyStringSchema.max(100).meta({
600
+ title: "Origin Municipality Value",
601
+ description: "Municipality where the waste was generated",
602
+ examples: ["S\xE3o Paulo", "New York", "Berlin", "Tokyo"]
603
+ })
604
+ }).meta({
605
+ title: "Origin Municipality Attribute",
606
+ description: "Origin municipality attribute"
607
+ });
608
+ var MassIDAttributeOriginDivisionSchema = NftAttributeSchema.extend({
609
+ trait_type: zod.z.literal("Origin Administrative Division"),
610
+ value: NonEmptyStringSchema.max(100).meta({
611
+ title: "Origin Division Value",
612
+ description: "Administrative division (state/province) where the waste was generated",
613
+ examples: ["S\xE3o Paulo", "California", "Bavaria"]
614
+ })
615
+ }).meta({
616
+ title: "Origin Administrative Division Attribute",
617
+ description: "Origin administrative division attribute"
618
+ });
619
+ var MassIDAttributeVehicleTypeSchema = NftAttributeSchema.extend({
620
+ trait_type: zod.z.literal("Vehicle Type"),
621
+ value: NonEmptyStringSchema.max(100).meta({
622
+ title: "Vehicle Type Value",
623
+ description: "Type of vehicle used for waste transportation",
624
+ examples: ["Garbage Truck", "Box Truck", "Flatbed Truck", "Roll-off Truck"]
625
+ })
626
+ }).meta({
627
+ title: "Vehicle Type Attribute",
628
+ description: "Vehicle type attribute"
629
+ });
630
+ var MassIDAttributeRecyclingMethodSchema = NftAttributeSchema.extend({
631
+ trait_type: zod.z.literal("Recycling Method"),
632
+ value: NonEmptyStringSchema.max(100).meta({
633
+ title: "Recycling Method Value",
634
+ description: "Method used for recycling or processing the waste",
635
+ examples: [
636
+ "Composting",
637
+ "Mechanical Recycling",
638
+ "Incineration with Energy Recovery"
639
+ ]
640
+ })
641
+ }).meta({
642
+ title: "Recycling Method Attribute",
643
+ description: "Recycling method attribute"
644
+ });
645
+ var MassIDAttributeProcessingTimeSchema = NftAttributeSchema.extend({
646
+ trait_type: zod.z.literal("Processing Time (hours)"),
647
+ value: HoursSchema,
648
+ trait_description: NonEmptyStringSchema.max(200).optional().meta({
649
+ title: "Processing Time Description",
650
+ description: "Custom description for the processing time"
651
+ })
652
+ }).meta({
653
+ title: "Processing Time Attribute",
654
+ description: "Processing time attribute with optional trait description"
655
+ });
656
+ var MassIDAttributeLocalWasteClassificationIdSchema = NftAttributeSchema.extend({
657
+ trait_type: zod.z.literal("Local Waste Classification ID"),
658
+ value: NonEmptyStringSchema.max(100).meta({
659
+ title: "Local Waste Classification ID Value",
660
+ description: "Local or regional waste classification identifier",
661
+ examples: ["04 02 20", "IBAMA-A001", "EWC-150101"]
662
+ })
663
+ }).meta({
664
+ title: "Local Waste Classification ID Attribute",
665
+ description: "Local waste classification ID attribute"
666
+ });
667
+ var MassIDAttributeRecyclingManifestCodeSchema = NftAttributeSchema.extend({
668
+ trait_type: zod.z.literal("Recycling Manifest Code"),
669
+ value: NonEmptyStringSchema.max(100).meta({
670
+ title: "Recycling Manifest Code Value",
671
+ description: "Concatenated recycling manifest code (Document Type + Document Number)",
672
+ examples: ["CDF-2353", "RC-12345", "REC-MANIFEST-789"]
673
+ })
674
+ }).meta({
675
+ title: "Recycling Manifest Code Attribute",
676
+ description: "Recycling manifest code attribute (optional)"
677
+ });
678
+ var MassIDAttributeTransportManifestCodeSchema = NftAttributeSchema.extend({
679
+ trait_type: zod.z.literal("Transport Manifest Code"),
680
+ value: NonEmptyStringSchema.max(100).meta({
681
+ title: "Transport Manifest Code Value",
682
+ description: "Concatenated transport manifest code (Document Type + Document Number)",
683
+ examples: ["MTR-4126", "TRN-67890", "TRANS-MANIFEST-456"]
684
+ })
685
+ }).meta({
686
+ title: "Transport Manifest Code Attribute",
687
+ description: "Transport manifest code attribute (optional)"
688
+ });
689
+ var MassIDAttributeWeighingCaptureMethodSchema = NftAttributeSchema.extend({
690
+ trait_type: zod.z.literal("Weighing Capture Method"),
691
+ value: NonEmptyStringSchema.max(100).meta({
692
+ title: "Weighing Capture Method Value",
693
+ description: "Method used to capture weight data",
694
+ examples: ["Digital", "Manual", "Automated", "Electronic Scale"]
695
+ })
696
+ }).meta({
697
+ title: "Weighing Capture Method Attribute",
698
+ description: "Weighing capture method attribute (optional)"
699
+ });
700
+ var MassIDAttributeScaleTypeSchema = NftAttributeSchema.extend({
701
+ trait_type: zod.z.literal("Scale Type"),
702
+ value: NonEmptyStringSchema.max(100).meta({
703
+ title: "Scale Type Value",
704
+ description: "Type of scale used for weighing",
705
+ examples: [
706
+ "Weighbridge (Truck Scale)",
707
+ "Floor Scale",
708
+ "Bench Scale",
709
+ "Crane Scale"
710
+ ]
711
+ })
712
+ }).meta({
713
+ title: "Scale Type Attribute",
714
+ description: "Scale type attribute (optional)"
715
+ });
716
+ var MassIDAttributeContainerTypeSchema = NftAttributeSchema.extend({
717
+ trait_type: zod.z.literal("Container Type"),
718
+ value: NonEmptyStringSchema.max(100).meta({
719
+ title: "Container Type Value",
720
+ description: "Type of container used for waste storage or transport",
721
+ examples: ["Truck", "Dumpster", "Roll-off Container", "Compactor", "Bin"]
722
+ })
723
+ }).meta({
724
+ title: "Container Type Attribute",
725
+ description: "Container type attribute (optional)"
726
+ });
727
+ var MassIDAttributePickUpDateSchema = NftAttributeSchema.extend({
728
+ trait_type: zod.z.literal("Pick-up Date"),
729
+ value: UnixTimestampSchema.meta({
730
+ title: "Pick-up Date Value",
731
+ description: "Unix timestamp in milliseconds when the waste was picked up from the source",
732
+ examples: [17105184e5, 17040672e5, 17152704e5]
733
+ }),
734
+ display_type: zod.z.literal("date")
735
+ }).meta({
736
+ title: "Pick-up Date Attribute",
737
+ description: "Pick-up date attribute with Unix timestamp"
738
+ });
739
+ var MassIDAttributeRecyclingDateSchema = NftAttributeSchema.extend({
740
+ trait_type: zod.z.literal("Recycling Date"),
741
+ value: UnixTimestampSchema.meta({
742
+ title: "Recycling Date Value",
743
+ description: "Unix timestamp in milliseconds when the waste was recycled/processed",
744
+ examples: [17106048e5, 17041536e5, 17153568e5]
745
+ }),
746
+ display_type: zod.z.literal("date")
747
+ }).meta({
748
+ title: "Recycling Date Attribute",
749
+ description: "Recycling date attribute with Unix timestamp"
750
+ });
751
+ var MassIDAttributesSchema = uniqueBy(
752
+ zod.z.union([
753
+ MassIDAttributeWasteTypeSchema,
754
+ MassIDAttributeWasteSubtypeSchema,
755
+ MassIDAttributeWeightSchema,
756
+ MassIDAttributeOriginCountrySchema,
757
+ MassIDAttributeOriginMunicipalitySchema,
758
+ MassIDAttributeOriginDivisionSchema,
759
+ MassIDAttributeVehicleTypeSchema,
760
+ MassIDAttributeRecyclingMethodSchema,
761
+ MassIDAttributeProcessingTimeSchema,
762
+ MassIDAttributeLocalWasteClassificationIdSchema,
763
+ MassIDAttributeRecyclingManifestCodeSchema,
764
+ MassIDAttributeTransportManifestCodeSchema,
765
+ MassIDAttributeWeighingCaptureMethodSchema,
766
+ MassIDAttributeScaleTypeSchema,
767
+ MassIDAttributeContainerTypeSchema,
768
+ MassIDAttributePickUpDateSchema,
769
+ MassIDAttributeRecyclingDateSchema
770
+ ]),
771
+ (attr) => attr.trait_type
772
+ ).min(12).max(17).meta({
773
+ title: "MassID Attributes",
774
+ description: "MassID NFT attributes array containing attributes selected from the available attribute types. The schema validates array length but does not enforce which specific attributes must be present."
775
+ });
776
+ var PrecisionLevelSchema = zod.z.enum(["exact", "neighborhood", "city", "region", "country"]).meta({
777
+ title: "Precision Level",
778
+ description: "Level of coordinate precision",
779
+ examples: ["city", "exact", "neighborhood"]
780
+ });
781
+ var CoordinatesSchema = zod.z.strictObject({
782
+ latitude: LatitudeSchema.meta({
783
+ title: "Latitude",
784
+ description: "GPS latitude coordinate"
785
+ }),
786
+ longitude: LongitudeSchema.meta({
787
+ title: "Longitude",
788
+ description: "GPS longitude coordinate"
789
+ }),
790
+ precision_level: PrecisionLevelSchema
791
+ }).meta({
792
+ title: "Coordinates",
793
+ description: "GPS coordinates of the location"
794
+ });
795
+ var LocationSchema = zod.z.strictObject({
796
+ id_hash: Sha256HashSchema.meta({
797
+ title: "Location ID Hash",
798
+ description: "Anonymized identifier for the location"
799
+ }),
800
+ municipality: NonEmptyStringSchema.max(50).meta({
801
+ title: "Municipality",
802
+ description: "Municipality or city name",
803
+ examples: ["New York", "S\xE3o Paulo", "London", "Tokyo"]
804
+ }),
805
+ administrative_division: NonEmptyStringSchema.max(50).meta({
806
+ title: "Administrative Division",
807
+ description: "State, province, or administrative region",
808
+ examples: ["California", "Ontario", "Bavaria", "Queensland"]
809
+ }),
810
+ administrative_division_code: IsoAdministrativeDivisionCodeSchema.optional().meta({
811
+ title: "Administrative Division Code",
812
+ description: "ISO 3166-2 administrative division code"
813
+ }),
814
+ country: NonEmptyStringSchema.max(50).meta({
815
+ title: "Country",
816
+ description: "Full country name in English",
817
+ examples: ["United States", "Canada", "Germany", "Australia"]
818
+ }),
819
+ country_code: IsoCountryCodeSchema.meta({
820
+ title: "Country Code",
821
+ description: "ISO 3166-1 alpha-2 country code"
822
+ }),
823
+ responsible_participant_id_hash: Sha256HashSchema.meta({
824
+ title: "Responsible Participant ID Hash",
825
+ description: "Anonymized ID of the participant responsible for this location"
826
+ }),
827
+ coordinates: CoordinatesSchema,
828
+ facility_type: FacilityTypeSchema.optional().meta({
829
+ title: "Facility Type",
830
+ description: "Type of facility at this location"
831
+ })
832
+ }).meta({
833
+ title: "Location",
834
+ description: "Geographic location with address and coordinate information"
835
+ });
836
+ var ParticipantSchema = zod.z.strictObject({
837
+ id_hash: Sha256HashSchema.meta({
838
+ title: "Participant ID Hash",
839
+ description: "Anonymized identifier for the participant"
840
+ }),
841
+ name: ParticipantNameSchema.meta({
842
+ title: "Participant Name",
843
+ description: "Name of the participant"
844
+ }),
845
+ roles: uniqueArrayItems(
846
+ ParticipantRoleSchema,
847
+ "Participant roles must be unique"
848
+ ).min(1).meta({
849
+ title: "Participant Roles",
850
+ description: "Roles of the participant in the waste management supply chain"
851
+ })
852
+ }).meta({
853
+ title: "Participant",
854
+ description: "A participant in the waste management supply chain"
855
+ });
856
+
857
+ // src/mass-id/mass-id.data.schema.ts
858
+ var MassIDLocalClassificationSchema = zod.z.strictObject({
859
+ code: NonEmptyStringSchema.max(20).meta({
860
+ title: "Classification Code",
861
+ description: "Local waste classification code",
862
+ examples: ["20 01 01", "D001", "EWC-150101", "IBAMA-A001"]
863
+ }),
864
+ description: NonEmptyStringSchema.max(200).meta({
865
+ title: "Classification Description",
866
+ description: "Local waste classification description",
867
+ examples: [
868
+ "Paper and cardboard packaging",
869
+ "Ignitable waste",
870
+ "Paper and cardboard packaging waste",
871
+ "Municipal solid waste - organic fraction"
872
+ ]
873
+ }),
874
+ system: zod.z.enum(["IBAMA"]).meta({
875
+ title: "Classification System",
876
+ description: "Classification system name - currently supports IBAMA (Instituto Brasileiro do Meio Ambiente e dos Recursos Naturais Renov\xE1veis)",
877
+ examples: ["IBAMA"]
878
+ })
879
+ }).meta({
880
+ title: "Local Classification",
881
+ description: "Local or regional waste classification codes and descriptions"
882
+ });
883
+ var MassIDMeasurementUnitSchema = zod.z.enum(["kg", "ton"]).meta({
884
+ title: "Measurement Unit",
885
+ description: "Unit of measurement for the waste quantity",
886
+ examples: ["kg", "ton"]
887
+ });
888
+ var MassIDWastePropertiesSchema = zod.z.strictObject({
889
+ type: WasteTypeSchema.meta({
890
+ title: "Waste Type",
891
+ description: "Waste material category"
892
+ }),
893
+ subtype: WasteSubtypeSchema.meta({
894
+ title: "Waste Subtype",
895
+ description: "Specific subcategory of waste material"
896
+ }),
897
+ local_classification: MassIDLocalClassificationSchema.optional(),
898
+ measurement_unit: MassIDMeasurementUnitSchema,
899
+ net_weight: NonNegativeFloatSchema.meta({
900
+ title: "Net Weight",
901
+ description: "Net weight of the waste batch in the specified measurement unit"
902
+ })
903
+ }).meta({
904
+ title: "Waste Properties",
905
+ description: "Standardized waste material properties and regulatory information"
906
+ });
907
+ var EventAttributeFormatSchema = zod.z.enum(["KILOGRAM", "DATE", "CURRENCY", "PERCENTAGE", "COORDINATE"]).meta({
908
+ title: "Event Attribute Format",
411
909
  description: "Data format hint for proper display",
412
910
  examples: ["KILOGRAM", "DATE", "PERCENTAGE"]
413
911
  });
@@ -486,9 +984,10 @@ var EventAttachmentSchema = zod.z.strictObject({
486
984
  "processing_receipt_20240315.jpg"
487
985
  ]
488
986
  }),
489
- issue_date: IsoDateSchema.optional().meta({
490
- title: "Issue Date",
491
- description: "Date the attachment was issued"
987
+ issue_timestamp: UnixTimestampSchema.optional().meta({
988
+ title: "Issue Timestamp",
989
+ description: "Unix timestamp in milliseconds when the attachment was issued",
990
+ examples: [17105184e5, 17040672e5, 17152704e5]
492
991
  }),
493
992
  issuer: NonEmptyStringSchema.max(100).optional().meta({
494
993
  title: "Attachment Issuer",
@@ -528,9 +1027,10 @@ var MassIDChainOfCustodyEventSchema = zod.z.strictObject({
528
1027
  "Quality inspection and contamination assessment completed"
529
1028
  ]
530
1029
  }),
531
- timestamp: IsoTimestampSchema.meta({
1030
+ timestamp: UnixTimestampSchema.meta({
532
1031
  title: "Event Timestamp",
533
- description: "ISO 8601 timestamp when the event occurred"
1032
+ description: "Unix timestamp in milliseconds when the event occurred",
1033
+ examples: [17105184e5, 17040672e5, 17152704e5]
534
1034
  }),
535
1035
  participant_id_hash: Sha256HashSchema.meta({
536
1036
  title: "Participant ID Hash",
@@ -546,386 +1046,99 @@ var MassIDChainOfCustodyEventSchema = zod.z.strictObject({
546
1046
  }),
547
1047
  attributes: zod.z.array(EventAttributeSchema).optional().meta({
548
1048
  title: "Event Attributes",
549
- description: "Additional attributes specific to this event"
550
- }),
551
- attachments: zod.z.array(EventAttachmentSchema).optional().meta({
552
- title: "Event Attachments",
553
- description: "Associated attachments for this event"
554
- })
555
- }).meta({
556
- title: "Chain of Custody Event",
557
- description: "Chain of custody event"
558
- });
559
- var MassIDChainOfCustodySchema = zod.z.strictObject({
560
- events: zod.z.array(MassIDChainOfCustodyEventSchema).min(1).meta({
561
- title: "Custody Events",
562
- description: "Chronological sequence of custody transfer and processing events"
563
- }),
564
- total_duration_hours: HoursSchema.meta({
565
- title: "Total Duration (hours)",
566
- description: "Total time from first to last event in hours"
567
- })
568
- }).meta({
569
- title: "Chain of Custody",
570
- description: "Complete chain of custody tracking from waste generation to final processing"
571
- });
572
- var MassIDGeographicDataSchema = zod.z.strictObject({
573
- from_location_id_hash: Sha256HashSchema.meta({
574
- title: "From Location ID Hash",
575
- description: "Reference hash of the location where the waste started movement"
576
- }),
577
- to_location_id_hash: Sha256HashSchema.meta({
578
- title: "To Location ID Hash",
579
- description: "Reference hash of the location where the waste ended movement"
580
- }),
581
- first_reported_timestamp: IsoTimestampSchema.meta({
582
- title: "First Reported Timestamp",
583
- description: "ISO 8601 timestamp when the waste was first reported/collected at the origin location"
584
- }),
585
- last_reported_timestamp: IsoTimestampSchema.meta({
586
- title: "Last Reported Timestamp",
587
- description: "ISO 8601 timestamp when the waste was last reported/processed at the destination location"
588
- })
589
- }).refine((data) => {
590
- const first = new Date(data.first_reported_timestamp);
591
- const last = new Date(data.last_reported_timestamp);
592
- return first <= last;
593
- }, "first_reported_timestamp must be before or equal to last_reported_timestamp").meta({
594
- title: "Geographic Data",
595
- description: "Simplified geographic information tracking waste movement from origin to destination with temporal bounds"
596
- });
597
- var MassIDDataSchema = zod.z.strictObject({
598
- waste_properties: MassIDWastePropertiesSchema,
599
- locations: uniqueBy(
600
- LocationSchema,
601
- (loc) => loc.id_hash,
602
- "Location ID hashes must be unique"
603
- ).min(1).meta({
604
- title: "Locations",
605
- description: "All locations referenced in this MassID, indexed by ID"
606
- }),
607
- participants: uniqueBy(
608
- ParticipantSchema,
609
- (p) => p.id_hash,
610
- "Participant ID hashes must be unique"
611
- ).min(1).meta({
612
- title: "Participants",
613
- description: "All participants referenced in this MassID, indexed by ID"
614
- }),
615
- chain_of_custody: MassIDChainOfCustodySchema,
616
- geographic_data: MassIDGeographicDataSchema
617
- }).refine((data) => {
618
- const participantIdSet = new Set(
619
- data.participants.map((participant) => participant.id_hash)
620
- );
621
- const eventParticipantIds = data.chain_of_custody.events.map(
622
- (event) => event.participant_id_hash
623
- );
624
- const allEventParticipantsExist = eventParticipantIds.every(
625
- (participantId) => participantIdSet.has(participantId)
626
- );
627
- return allEventParticipantsExist;
628
- }, "All participant ID hashes in chain of custody events must exist in participants array").refine((data) => {
629
- const locationIdSet = new Set(
630
- data.locations.map((location) => location.id_hash)
631
- );
632
- const eventLocationIds = data.chain_of_custody.events.map(
633
- (event) => event.location_id_hash
634
- );
635
- const allEventLocationsExist = eventLocationIds.every(
636
- (locationId) => locationIdSet.has(locationId)
637
- );
638
- return allEventLocationsExist;
639
- }, "All location ID hashes in chain of custody events must exist in locations array").meta({
640
- title: "MassID Data",
641
- description: "MassID data containing waste tracking and chain of custody information"
642
- });
643
- var SchemaInfoSchema = zod.z.strictObject({
644
- hash: Keccak256HashSchema.meta({
645
- title: "Schema Hash",
646
- description: "Keccak256 hash of the JSON Schema this record was validated against"
647
- }),
648
- type: RecordSchemaTypeSchema.meta({
649
- title: "Schema Type",
650
- description: "Type/category of this schema"
651
- }),
652
- version: SemanticVersionSchema.meta({
653
- title: "Schema Version",
654
- description: "Version of the schema, using semantic versioning"
655
- })
656
- }).meta({
657
- title: "Schema Information"
658
- });
659
- var RecordCreatorSchema = zod.z.strictObject({
660
- name: zod.z.string().meta({
661
- title: "Creator Name",
662
- description: "Company or individual name that created this record",
663
- examples: ["Carrot Foundation"]
664
- }),
665
- id: UuidSchema.meta({
666
- title: "Creator ID",
667
- description: "Unique identifier for the creator"
668
- })
669
- }).meta({
670
- title: "Creator",
671
- description: "Entity that created this record"
672
- });
673
- var RecordRelationshipSchema = zod.z.strictObject({
674
- target_uri: IpfsUriSchema.meta({
675
- title: "Target IPFS URI",
676
- description: "Target IPFS URI of the referenced record"
677
- }),
678
- type: RecordRelationshipTypeSchema.meta({
679
- title: "Relationship Type",
680
- description: "Type of relationship to the referenced record"
681
- }),
682
- description: zod.z.string().optional().meta({
683
- title: "Relationship Description",
684
- description: "Human-readable description of the relationship",
685
- examples: [
686
- "This record supersedes the previous version",
687
- "Related carbon credit batch",
688
- "Source document for this verification",
689
- "Child record derived from this parent",
690
- "Updated version of original record"
691
- ]
692
- })
693
- }).meta({
694
- title: "Relationship",
695
- description: "Relationship to another IPFS record"
696
- });
697
- var RecordEnvironmentSchema = zod.z.strictObject({
698
- blockchain_network: zod.z.enum(["mainnet", "testnet"]).meta({
699
- title: "Blockchain Network",
700
- description: "Blockchain Network Environment"
701
- }),
702
- deployment: zod.z.enum(["production", "development", "testing"]).meta({
703
- title: "Deployment Environment",
704
- description: "System environment where this record was generated"
705
- }),
706
- data_set_name: zod.z.enum(["TEST", "PROD"]).meta({
707
- title: "Data Set Name",
708
- description: "Name of the data set for this record"
709
- })
710
- }).meta({
711
- title: "Environment",
712
- description: "Environment information"
713
- });
714
- var BaseIpfsSchema = zod.z.strictObject({
715
- $schema: zod.z.url("Must be a valid URI").meta({
716
- title: "JSON Schema URI",
717
- description: "URI of the JSON Schema used to validate this record",
718
- example: "https://raw.githubusercontent.com/carrot-foundation/schemas/refs/heads/main/schemas/ipfs/shared/base/base.schema.json"
719
- }),
720
- schema: SchemaInfoSchema,
721
- created_at: IsoTimestampSchema.meta({
722
- title: "Created At",
723
- description: "ISO 8601 creation timestamp for this record"
724
- }),
725
- external_id: ExternalIdSchema.meta({
726
- title: "External ID",
727
- description: "Off-chain reference ID (UUID from Carrot backend)"
728
- }),
729
- external_url: ExternalUrlSchema.meta({
730
- title: "External URL",
731
- description: "External URL of the content"
732
- }),
733
- original_content_hash: Sha256HashSchema.meta({
734
- title: "Original Content Hash",
735
- description: "SHA-256 hash of the original JSON content including private data before schema validation"
736
- }),
737
- content_hash: Sha256HashSchema.meta({
738
- title: "Content Hash",
739
- description: "SHA-256 hash of RFC 8785 canonicalized JSON after schema validation"
740
- }),
741
- creator: RecordCreatorSchema.optional(),
742
- relationships: zod.z.array(RecordRelationshipSchema).optional().meta({
743
- title: "Relationships",
744
- description: "References to other IPFS records this record relates to"
745
- }),
746
- environment: RecordEnvironmentSchema.optional(),
747
- data: zod.z.record(zod.z.string(), zod.z.unknown()).optional().meta({
748
- title: "Custom Data",
749
- description: "Custom data block that includes the record's data"
750
- })
751
- }).meta({
752
- title: "Base IPFS Record",
753
- description: "Base fields for all Carrot IPFS records, providing common structure for any JSON content stored in IPFS"
754
- });
755
-
756
- // src/shared/nft.schema.ts
757
- var NftSchemaTypeSchema = RecordSchemaTypeSchema.extract([
758
- "MassID",
759
- "RecycledID",
760
- "GasID",
761
- "PurchaseID"
762
- ]).meta({
763
- title: "NFT Schema Type",
764
- description: "Type of schema for NFT records"
765
- });
766
- var BlockchainReferenceSchema = zod.z.strictObject({
767
- smart_contract_address: EthereumAddressSchema.meta({
768
- title: "Smart Contract Address"
769
- }),
770
- chain_id: BlockchainChainIdSchema.meta({
771
- title: "Chain ID",
772
- description: "Blockchain chain ID"
773
- }),
774
- network_name: zod.z.string().min(5).max(100).meta({
775
- title: "Network Name",
776
- description: "Name of the blockchain network"
777
- }),
778
- token_id: TokenIdSchema.meta({
779
- title: "Token ID",
780
- description: "NFT token ID"
781
- })
782
- }).meta({
783
- title: "Blockchain Information",
784
- description: "Blockchain-specific information for the NFT"
785
- });
786
- var ExternalLinkSchema = zod.z.strictObject({
787
- label: zod.z.string().min(1).max(50).meta({
788
- title: "Link Label",
789
- description: "Display name for the external link"
790
- }),
791
- url: zod.z.url("Must be a valid URI").meta({
792
- title: "Link URL",
793
- description: "Direct URI to the linked resource"
1049
+ description: "Additional attributes specific to this event"
794
1050
  }),
795
- description: zod.z.string().min(10).max(100).optional().meta({
796
- title: "Link Description",
797
- description: "Optional context about what the link provides"
1051
+ attachments: zod.z.array(EventAttachmentSchema).optional().meta({
1052
+ title: "Event Attachments",
1053
+ description: "Associated attachments for this event"
798
1054
  })
799
1055
  }).meta({
800
- title: "External Link",
801
- description: "External link with label and description"
1056
+ title: "Chain of Custody Event",
1057
+ description: "Chain of custody event"
802
1058
  });
803
- var NftAttributeSchema = zod.z.strictObject({
804
- trait_type: zod.z.string().max(50).meta({
805
- title: "Trait Type",
806
- description: "Name of the trait or attribute"
807
- }),
808
- value: zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean()]).meta({
809
- title: "Trait Value",
810
- description: "Value of the trait - can be string, number, or boolean"
811
- }),
812
- display_type: zod.z.enum(["number", "date", "boost_number", "boost_percentage"]).optional().meta({
813
- title: "Display Type",
814
- description: "How the trait should be displayed in marketplace UIs"
1059
+ var MassIDChainOfCustodySchema = zod.z.strictObject({
1060
+ events: zod.z.array(MassIDChainOfCustodyEventSchema).min(1).meta({
1061
+ title: "Custody Events",
1062
+ description: "Chronological sequence of custody transfer and processing events"
815
1063
  }),
816
- max_value: NonNegativeFloatSchema.optional().meta({
817
- title: "Max Value",
818
- description: "Maximum possible value for numeric traits"
1064
+ total_duration_minutes: MinutesSchema.meta({
1065
+ title: "Total Duration (minutes)",
1066
+ description: "Total time from first to last event in minutes"
819
1067
  })
820
1068
  }).meta({
821
- title: "NFT Attribute",
822
- description: "NFT attribute or trait with type and value"
1069
+ title: "Chain of Custody",
1070
+ description: "Complete chain of custody tracking from waste generation to final processing"
823
1071
  });
824
- var NftIpfsSchema = BaseIpfsSchema.safeExtend({
825
- schema: BaseIpfsSchema.shape.schema.safeExtend({
826
- type: NftSchemaTypeSchema.meta({
827
- title: "NFT Schema Type",
828
- description: "Type/category of this NFT schema"
829
- })
830
- }),
831
- blockchain: BlockchainReferenceSchema,
832
- name: zod.z.string().min(1).max(100).meta({
833
- title: "NFT Name",
834
- description: "Full display name for this NFT, including extra context",
835
- examples: [
836
- "MassID #123 \u2022 Organic \u2022 3.0t",
837
- "RecycledID #456 \u2022 Plastic \u2022 2.5t",
838
- "GasID #789 \u2022 Methane \u2022 1000 m\xB3"
839
- ]
840
- }),
841
- short_name: zod.z.string().min(1).max(50).meta({
842
- title: "Short Name",
843
- description: "Compact name for UI summaries, tables, or tooltips",
844
- examples: ["MassID #123", "RecycledID #456", "GasID #789"]
845
- }),
846
- description: zod.z.string().min(10).max(500).meta({
847
- title: "Description",
848
- description: "Human-readable summary of the NFT's role and context. Ideally, maximum 300 characters.",
849
- examples: [
850
- "This MassID represents 3 metric tons of organic food waste from Enlatados Produ\xE7\xE3o, tracked through complete chain of custody from generation to composting.",
851
- "This RecycledID represents 2.5 metric tons of recycled plastic bottles processed by Green Solutions Ltd."
852
- ]
1072
+ var MassIDGeographicDataSchema = zod.z.strictObject({
1073
+ from_location_id_hash: Sha256HashSchema.meta({
1074
+ title: "From Location ID Hash",
1075
+ description: "Reference hash of the location where the waste started movement"
853
1076
  }),
854
- image: IpfsUriSchema.meta({
855
- title: "Image URI",
856
- description: "IPFS URI pointing to the preview image"
1077
+ to_location_id_hash: Sha256HashSchema.meta({
1078
+ title: "To Location ID Hash",
1079
+ description: "Reference hash of the location where the waste ended movement"
857
1080
  }),
858
- background_color: HexColorSchema.optional().meta({
859
- title: "Background Color",
860
- description: "Hex color code for marketplace background display"
1081
+ first_reported_timestamp: UnixTimestampSchema.meta({
1082
+ title: "First Reported Timestamp",
1083
+ description: "Unix timestamp in milliseconds when the waste was first reported/collected at the origin location",
1084
+ examples: [17105184e5, 17040672e5, 17152704e5]
861
1085
  }),
862
- animation_url: IpfsUriSchema.optional().meta({
863
- title: "Animation URL",
864
- description: "IPFS URI pointing to an animated or interactive media file",
865
- examples: [
866
- "ipfs://QmAnimation123/mass-id-animation.mp4",
867
- "ipfs://QmInteractive456/recycled-visualization.webm"
868
- ]
1086
+ last_reported_timestamp: UnixTimestampSchema.meta({
1087
+ title: "Last Reported Timestamp",
1088
+ description: "Unix timestamp in milliseconds when the waste was last reported/processed at the destination location",
1089
+ examples: [17106048e5, 17041536e5, 17153568e5]
1090
+ })
1091
+ }).refine((data) => {
1092
+ return data.first_reported_timestamp <= data.last_reported_timestamp;
1093
+ }, "first_reported_timestamp must be before or equal to last_reported_timestamp").meta({
1094
+ title: "Geographic Data",
1095
+ description: "Simplified geographic information tracking waste movement from origin to destination with temporal bounds"
1096
+ });
1097
+ var MassIDDataSchema = zod.z.strictObject({
1098
+ waste_properties: MassIDWastePropertiesSchema,
1099
+ locations: uniqueBy(
1100
+ LocationSchema,
1101
+ (loc) => loc.id_hash,
1102
+ "Location ID hashes must be unique"
1103
+ ).min(1).meta({
1104
+ title: "Locations",
1105
+ description: "All locations referenced in this MassID, indexed by ID"
869
1106
  }),
870
- external_links: uniqueBy(
871
- ExternalLinkSchema,
872
- (link) => link.url,
873
- "External link URLs must be unique"
874
- ).max(10).optional().meta({
875
- title: "External Links",
876
- description: "Optional list of public resource links with labels",
877
- examples: [
878
- [
879
- {
880
- label: "Carrot Explorer",
881
- url: "https://explore.carrot.eco/document/ad44dd3f-f176-4b98-bf78-5ee6e77d0530",
882
- description: "Complete chain of custody and audit trail"
883
- },
884
- {
885
- label: "Carrot White Paper",
886
- url: "https://carrot.eco/whitepaper.pdf",
887
- description: "Carrot Foundation technical and impact white paper"
888
- }
889
- ]
890
- ]
1107
+ participants: uniqueBy(
1108
+ ParticipantSchema,
1109
+ (p) => p.id_hash,
1110
+ "Participant ID hashes must be unique"
1111
+ ).min(1).meta({
1112
+ title: "Participants",
1113
+ description: "All participants referenced in this MassID, indexed by ID"
891
1114
  }),
892
- attributes: uniqueBy(
893
- NftAttributeSchema,
894
- (attr) => attr.trait_type,
895
- "Attribute trait_type values must be unique"
896
- ).meta({
897
- title: "NFT Attributes",
898
- description: "List of visual traits and filterable attributes compatible with NFT marketplaces",
899
- examples: [
900
- [
901
- {
902
- trait_type: "Waste Type",
903
- value: "Organic"
904
- },
905
- {
906
- trait_type: "Waste Subtype",
907
- value: "Food, Food Waste and Beverages"
908
- },
909
- {
910
- trait_type: "Weight (kg)",
911
- value: 3e3,
912
- display_type: "number"
913
- },
914
- {
915
- trait_type: "Origin Country",
916
- value: "Brazil"
917
- },
918
- {
919
- trait_type: "Pick-up Date",
920
- value: "2024-12-05",
921
- display_type: "date"
922
- }
923
- ]
924
- ]
925
- })
926
- }).meta({
927
- title: "NFT IPFS Record",
928
- description: "NFT-specific fields for Carrot IPFS records"
1115
+ chain_of_custody: MassIDChainOfCustodySchema,
1116
+ geographic_data: MassIDGeographicDataSchema
1117
+ }).refine((data) => {
1118
+ const participantIdSet = new Set(
1119
+ data.participants.map((participant) => participant.id_hash)
1120
+ );
1121
+ const eventParticipantIds = data.chain_of_custody.events.map(
1122
+ (event) => event.participant_id_hash
1123
+ );
1124
+ const allEventParticipantsExist = eventParticipantIds.every(
1125
+ (participantId) => participantIdSet.has(participantId)
1126
+ );
1127
+ return allEventParticipantsExist;
1128
+ }, "All participant ID hashes in chain of custody events must exist in participants array").refine((data) => {
1129
+ const locationIdSet = new Set(
1130
+ data.locations.map((location) => location.id_hash)
1131
+ );
1132
+ const eventLocationIds = data.chain_of_custody.events.map(
1133
+ (event) => event.location_id_hash
1134
+ );
1135
+ const allEventLocationsExist = eventLocationIds.every(
1136
+ (locationId) => locationIdSet.has(locationId)
1137
+ );
1138
+ return allEventLocationsExist;
1139
+ }, "All location ID hashes in chain of custody events must exist in locations array").meta({
1140
+ title: "MassID Data",
1141
+ description: "MassID data containing waste tracking and chain of custody information"
929
1142
  });
930
1143
 
931
1144
  // src/shared/schema-version.ts
@@ -941,115 +1154,6 @@ function getSchemaVersionOrDefault() {
941
1154
  }
942
1155
 
943
1156
  // src/mass-id/mass-id.schema.ts
944
- var AttributeWasteTypeSchema = zod.z.strictObject({
945
- trait_type: zod.z.literal("Waste Type"),
946
- value: WasteTypeSchema
947
- }).meta({
948
- title: "Waste Type Attribute",
949
- description: "Waste type attribute"
950
- });
951
- var AttributeWasteSubtypeSchema = zod.z.strictObject({
952
- trait_type: zod.z.literal("Waste Subtype"),
953
- value: WasteSubtypeSchema
954
- }).meta({
955
- title: "Waste Subtype Attribute",
956
- description: "Waste subtype attribute"
957
- });
958
- var AttributeWeightSchema = zod.z.strictObject({
959
- trait_type: zod.z.literal("Weight (kg)"),
960
- value: WeightKgSchema,
961
- display_type: zod.z.literal("number")
962
- }).meta({
963
- title: "Weight Attribute",
964
- description: "Weight attribute with numeric display"
965
- });
966
- var AttributeOriginCountrySchema = zod.z.strictObject({
967
- trait_type: zod.z.literal("Origin Country"),
968
- value: zod.z.string().max(100).meta({
969
- title: "Origin Country Value",
970
- description: "Country where the waste was generated"
971
- })
972
- }).meta({
973
- title: "Origin Country Attribute",
974
- description: "Origin country attribute"
975
- });
976
- var AttributeOriginMunicipalitySchema = zod.z.strictObject({
977
- trait_type: zod.z.literal("Origin Municipality"),
978
- value: zod.z.string().max(100).meta({
979
- title: "Origin Municipality Value",
980
- description: "Municipality where the waste was generated"
981
- })
982
- }).meta({
983
- title: "Origin Municipality Attribute",
984
- description: "Origin municipality attribute"
985
- });
986
- var AttributeOriginDivisionSchema = zod.z.strictObject({
987
- trait_type: zod.z.literal("Origin Administrative Division"),
988
- value: zod.z.string().max(100).meta({
989
- title: "Origin Division Value",
990
- description: "Administrative division (state/province) where the waste was generated"
991
- })
992
- }).meta({
993
- title: "Origin Administrative Division Attribute",
994
- description: "Origin administrative division attribute"
995
- });
996
- var AttributeRecyclerSchema = zod.z.strictObject({
997
- trait_type: zod.z.literal("Recycler"),
998
- value: zod.z.string().max(100).meta({
999
- title: "Recycler Value",
1000
- description: "Organization that processed the waste"
1001
- })
1002
- }).meta({
1003
- title: "Recycler Attribute",
1004
- description: "Recycler attribute"
1005
- });
1006
- var AttributeIntegratorSchema = zod.z.strictObject({
1007
- trait_type: zod.z.literal("Integrator"),
1008
- value: zod.z.string().max(100).meta({
1009
- title: "Integrator Value",
1010
- description: "Organization that integrated the waste into the Carrot network"
1011
- })
1012
- }).meta({
1013
- title: "Integrator Attribute",
1014
- description: "Integrator attribute"
1015
- });
1016
- var AttributePickUpDateSchema = zod.z.strictObject({
1017
- trait_type: zod.z.literal("Pick-up Date"),
1018
- value: UnixTimestampSchema.meta({
1019
- title: "Pick-up Date Value",
1020
- description: "Unix timestamp in milliseconds when the waste was picked up from the source"
1021
- }),
1022
- display_type: zod.z.literal("date")
1023
- }).meta({
1024
- title: "Pick-up Date Attribute",
1025
- description: "Pick-up date attribute with Unix timestamp"
1026
- });
1027
- var AttributeRecyclingDateSchema = zod.z.strictObject({
1028
- trait_type: zod.z.literal("Recycling Date"),
1029
- value: zod.z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Must be a valid date in YYYY-MM-DD format").meta({
1030
- title: "Recycling Date Value",
1031
- description: "Date when the waste was recycled/processed"
1032
- }),
1033
- display_type: zod.z.literal("date")
1034
- }).meta({
1035
- title: "Recycling Date Attribute",
1036
- description: "Recycling date attribute"
1037
- });
1038
- var MassIDAttributesSchema = zod.z.tuple([
1039
- AttributeWasteTypeSchema,
1040
- AttributeWasteSubtypeSchema,
1041
- AttributeWeightSchema,
1042
- AttributeOriginCountrySchema,
1043
- AttributeOriginMunicipalitySchema,
1044
- AttributeOriginDivisionSchema,
1045
- AttributeRecyclerSchema,
1046
- AttributeIntegratorSchema,
1047
- AttributePickUpDateSchema,
1048
- AttributeRecyclingDateSchema
1049
- ]).meta({
1050
- title: "MassID Attributes",
1051
- description: "Fixed set of MassID NFT attributes in required order"
1052
- });
1053
1157
  var MassIDIpfsSchemaMeta = {
1054
1158
  title: "MassID NFT IPFS Record",
1055
1159
  description: "Complete MassID NFT IPFS record including fixed attributes and detailed waste tracking data",
@@ -1063,16 +1167,11 @@ var MassIDIpfsSchema = NftIpfsSchema.safeExtend({
1063
1167
  description: "MassID NFT schema type"
1064
1168
  })
1065
1169
  }),
1066
- attributes: MassIDAttributesSchema.meta({
1067
- title: "MassID NFT Attributes",
1068
- description: "Fixed set of MassID NFT attributes enforcing order and type for each trait"
1069
- }).check(zod.z.minLength(10), zod.z.maxLength(10)),
1070
- data: MassIDDataSchema.meta({
1071
- title: "MassID Data",
1072
- description: "MassID-specific data containing waste tracking and chain of custody information"
1073
- })
1170
+ attributes: MassIDAttributesSchema,
1171
+ data: MassIDDataSchema
1074
1172
  }).meta(MassIDIpfsSchemaMeta);
1075
1173
 
1174
+ exports.MassIDAttributesSchema = MassIDAttributesSchema;
1076
1175
  exports.MassIDDataSchema = MassIDDataSchema;
1077
1176
  exports.MassIDIpfsSchema = MassIDIpfsSchema;
1078
1177
  exports.MassIDIpfsSchemaMeta = MassIDIpfsSchemaMeta;