@carrot-foundation/schemas 0.1.6
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/LICENSE +201 -0
- package/README.md +45 -0
- package/dist/index.js +1 -0
- package/dist/mass-id/index.js +2 -0
- package/dist/mass-id/mass-id.data.schema.js +346 -0
- package/dist/mass-id/mass-id.schema.js +162 -0
- package/dist/shared/base.schema.js +127 -0
- package/dist/shared/definitions.schema.js +283 -0
- package/dist/shared/entities/location.schema.js +68 -0
- package/dist/shared/entities/participant.schema.js +24 -0
- package/dist/shared/helpers.schema.js +16 -0
- package/dist/shared/nft.schema.js +193 -0
- package/package.json +89 -0
- package/schemas/ipfs/collection/collection.example.json +25 -0
- package/schemas/ipfs/collection/collection.schema.json +47 -0
- package/schemas/ipfs/credit/credit.example.json +27 -0
- package/schemas/ipfs/credit/credit.schema.json +61 -0
- package/schemas/ipfs/gas-id/gas-id.attributes.schema.json +219 -0
- package/schemas/ipfs/gas-id/gas-id.data.schema.json +120 -0
- package/schemas/ipfs/gas-id/gas-id.example.json +245 -0
- package/schemas/ipfs/gas-id/gas-id.schema.json +29 -0
- package/schemas/ipfs/mass-id/mass-id.example.json +318 -0
- package/schemas/ipfs/mass-id/mass-id.schema.json +1325 -0
- package/schemas/ipfs/mass-id-audit/mass-id-audit.data.schema.json +130 -0
- package/schemas/ipfs/mass-id-audit/mass-id-audit.example.json +291 -0
- package/schemas/ipfs/mass-id-audit/mass-id-audit.schema.json +26 -0
- package/schemas/ipfs/methodology/methodology.data.schema.json +121 -0
- package/schemas/ipfs/methodology/methodology.example.json +222 -0
- package/schemas/ipfs/methodology/methodology.schema.json +26 -0
- package/schemas/ipfs/purchase-id/purchase-id.attributes.schema.json +91 -0
- package/schemas/ipfs/purchase-id/purchase-id.data.schema.json +337 -0
- package/schemas/ipfs/purchase-id/purchase-id.example.json +224 -0
- package/schemas/ipfs/purchase-id/purchase-id.schema.json +29 -0
- package/schemas/ipfs/recycled-id/recycled-id.attributes.schema.json +202 -0
- package/schemas/ipfs/recycled-id/recycled-id.data.schema.json +63 -0
- package/schemas/ipfs/recycled-id/recycled-id.example.json +213 -0
- package/schemas/ipfs/recycled-id/recycled-id.schema.json +29 -0
- package/schemas/ipfs/shared/base/base.schema.json +163 -0
- package/schemas/ipfs/shared/certificate/certificate.schema.json +145 -0
- package/schemas/ipfs/shared/definitions/definitions.schema.json +250 -0
- package/schemas/ipfs/shared/entities/location/location.schema.json +95 -0
- package/schemas/ipfs/shared/entities/participant/participant.schema.json +28 -0
- package/schemas/ipfs/shared/nft/nft.schema.json +182 -0
- package/schemas/ipfs/shared/references/audit-reference/audit-reference.schema.json +42 -0
- package/schemas/ipfs/shared/references/gas-id-reference/gas-id-reference.schema.json +27 -0
- package/schemas/ipfs/shared/references/mass-id-reference/mass-id-reference.schema.json +27 -0
- package/schemas/ipfs/shared/references/methodology-reference/methodology-reference.schema.json +34 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { NftIpfsSchema } from '../shared/nft.schema.js';
|
|
3
|
+
import { MassIDDataSchema } from './mass-id.data.schema.js';
|
|
4
|
+
import { WasteTypeSchema, WasteSubtypeSchema, WeightKgSchema, } from '../shared/definitions.schema.js';
|
|
5
|
+
const AttributeWasteTypeSchema = z
|
|
6
|
+
.strictObject({
|
|
7
|
+
trait_type: z.literal('Waste Type'),
|
|
8
|
+
value: WasteTypeSchema,
|
|
9
|
+
})
|
|
10
|
+
.meta({
|
|
11
|
+
title: 'Waste Type Attribute',
|
|
12
|
+
description: 'Waste type attribute',
|
|
13
|
+
});
|
|
14
|
+
const AttributeWasteSubtypeSchema = z
|
|
15
|
+
.strictObject({
|
|
16
|
+
trait_type: z.literal('Waste Subtype'),
|
|
17
|
+
value: WasteSubtypeSchema,
|
|
18
|
+
})
|
|
19
|
+
.meta({
|
|
20
|
+
title: 'Waste Subtype Attribute',
|
|
21
|
+
description: 'Waste subtype attribute',
|
|
22
|
+
});
|
|
23
|
+
const AttributeWeightSchema = z
|
|
24
|
+
.strictObject({
|
|
25
|
+
trait_type: z.literal('Weight (kg)'),
|
|
26
|
+
value: WeightKgSchema,
|
|
27
|
+
display_type: z.literal('number'),
|
|
28
|
+
})
|
|
29
|
+
.meta({
|
|
30
|
+
title: 'Weight Attribute',
|
|
31
|
+
description: 'Weight attribute with numeric display',
|
|
32
|
+
});
|
|
33
|
+
const AttributeOriginCountrySchema = z
|
|
34
|
+
.strictObject({
|
|
35
|
+
trait_type: z.literal('Origin Country'),
|
|
36
|
+
value: z.string().max(100).meta({
|
|
37
|
+
title: 'Origin Country Value',
|
|
38
|
+
description: 'Country where the waste was generated',
|
|
39
|
+
}),
|
|
40
|
+
})
|
|
41
|
+
.meta({
|
|
42
|
+
title: 'Origin Country Attribute',
|
|
43
|
+
description: 'Origin country attribute',
|
|
44
|
+
});
|
|
45
|
+
const AttributeOriginMunicipalitySchema = z
|
|
46
|
+
.strictObject({
|
|
47
|
+
trait_type: z.literal('Origin Municipality'),
|
|
48
|
+
value: z.string().max(100).meta({
|
|
49
|
+
title: 'Origin Municipality Value',
|
|
50
|
+
description: 'Municipality where the waste was generated',
|
|
51
|
+
}),
|
|
52
|
+
})
|
|
53
|
+
.meta({
|
|
54
|
+
title: 'Origin Municipality Attribute',
|
|
55
|
+
description: 'Origin municipality attribute',
|
|
56
|
+
});
|
|
57
|
+
const AttributeOriginDivisionSchema = z
|
|
58
|
+
.strictObject({
|
|
59
|
+
trait_type: z.literal('Origin Administrative Division'),
|
|
60
|
+
value: z.string().max(100).meta({
|
|
61
|
+
title: 'Origin Division Value',
|
|
62
|
+
description: 'Administrative division (state/province) where the waste was generated',
|
|
63
|
+
}),
|
|
64
|
+
})
|
|
65
|
+
.meta({
|
|
66
|
+
title: 'Origin Administrative Division Attribute',
|
|
67
|
+
description: 'Origin administrative division attribute',
|
|
68
|
+
});
|
|
69
|
+
const AttributeRecyclerSchema = z
|
|
70
|
+
.strictObject({
|
|
71
|
+
trait_type: z.literal('Recycler'),
|
|
72
|
+
value: z.string().max(100).meta({
|
|
73
|
+
title: 'Recycler Value',
|
|
74
|
+
description: 'Organization that processed the waste',
|
|
75
|
+
}),
|
|
76
|
+
})
|
|
77
|
+
.meta({
|
|
78
|
+
title: 'Recycler Attribute',
|
|
79
|
+
description: 'Recycler attribute',
|
|
80
|
+
});
|
|
81
|
+
const AttributeIntegratorSchema = z
|
|
82
|
+
.strictObject({
|
|
83
|
+
trait_type: z.literal('Integrator'),
|
|
84
|
+
value: z.string().max(100).meta({
|
|
85
|
+
title: 'Integrator Value',
|
|
86
|
+
description: 'Organization that integrated the waste into the Carrot network',
|
|
87
|
+
}),
|
|
88
|
+
})
|
|
89
|
+
.meta({
|
|
90
|
+
title: 'Integrator Attribute',
|
|
91
|
+
description: 'Integrator attribute',
|
|
92
|
+
});
|
|
93
|
+
const AttributePickUpDateSchema = z
|
|
94
|
+
.strictObject({
|
|
95
|
+
trait_type: z.literal('Pick-up Date'),
|
|
96
|
+
value: z
|
|
97
|
+
.string()
|
|
98
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/, 'Must be a valid date in YYYY-MM-DD format')
|
|
99
|
+
.meta({
|
|
100
|
+
title: 'Pick-up Date Value',
|
|
101
|
+
description: 'Date when the waste was picked up from the source',
|
|
102
|
+
}),
|
|
103
|
+
display_type: z.literal('date'),
|
|
104
|
+
})
|
|
105
|
+
.meta({
|
|
106
|
+
title: 'Pick-up Date Attribute',
|
|
107
|
+
description: 'Pick-up date attribute',
|
|
108
|
+
});
|
|
109
|
+
const AttributeRecyclingDateSchema = z
|
|
110
|
+
.strictObject({
|
|
111
|
+
trait_type: z.literal('Recycling Date'),
|
|
112
|
+
value: z
|
|
113
|
+
.string()
|
|
114
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/, 'Must be a valid date in YYYY-MM-DD format')
|
|
115
|
+
.meta({
|
|
116
|
+
title: 'Recycling Date Value',
|
|
117
|
+
description: 'Date when the waste was recycled/processed',
|
|
118
|
+
}),
|
|
119
|
+
display_type: z.literal('date'),
|
|
120
|
+
})
|
|
121
|
+
.meta({
|
|
122
|
+
title: 'Recycling Date Attribute',
|
|
123
|
+
description: 'Recycling date attribute',
|
|
124
|
+
});
|
|
125
|
+
const MassIDAttributesSchema = z
|
|
126
|
+
.tuple([
|
|
127
|
+
AttributeWasteTypeSchema,
|
|
128
|
+
AttributeWasteSubtypeSchema,
|
|
129
|
+
AttributeWeightSchema,
|
|
130
|
+
AttributeOriginCountrySchema,
|
|
131
|
+
AttributeOriginMunicipalitySchema,
|
|
132
|
+
AttributeOriginDivisionSchema,
|
|
133
|
+
AttributeRecyclerSchema,
|
|
134
|
+
AttributeIntegratorSchema,
|
|
135
|
+
AttributePickUpDateSchema,
|
|
136
|
+
AttributeRecyclingDateSchema,
|
|
137
|
+
])
|
|
138
|
+
.meta({
|
|
139
|
+
title: 'MassID Attributes',
|
|
140
|
+
description: 'Fixed set of MassID NFT attributes in required order',
|
|
141
|
+
});
|
|
142
|
+
export const MassIDIpfsSchema = NftIpfsSchema.safeExtend({
|
|
143
|
+
schema: NftIpfsSchema.shape.schema.safeExtend({
|
|
144
|
+
type: z.literal('MassID').meta({
|
|
145
|
+
title: 'MassID Schema Type',
|
|
146
|
+
description: 'MassID NFT schema type',
|
|
147
|
+
}),
|
|
148
|
+
}),
|
|
149
|
+
attributes: MassIDAttributesSchema.meta({
|
|
150
|
+
title: 'MassID NFT Attributes',
|
|
151
|
+
description: 'Fixed set of MassID NFT attributes enforcing order and type for each trait',
|
|
152
|
+
}).check(z.minLength(10), z.maxLength(10)),
|
|
153
|
+
data: MassIDDataSchema.meta({
|
|
154
|
+
title: 'MassID Data',
|
|
155
|
+
description: 'MassID-specific data containing waste tracking and chain of custody information',
|
|
156
|
+
}),
|
|
157
|
+
}).meta({
|
|
158
|
+
title: 'MassID NFT IPFS Record',
|
|
159
|
+
description: 'Complete MassID NFT IPFS record including fixed attributes and detailed waste tracking data',
|
|
160
|
+
$id: 'https://raw.githubusercontent.com/carrot-foundation/schemas/refs/heads/main/schemas/ipfs/mass-id/mass-id.schema.json',
|
|
161
|
+
version: '1.0.0',
|
|
162
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Keccak256HashSchema, SemanticVersionSchema, Sha256HashSchema, IsoTimestampSchema, ExternalIdSchema, ExternalUrlSchema, UuidSchema, RecordSchemaTypeSchema, IpfsUriSchema, RecordRelationshipTypeSchema, } from './definitions.schema.js';
|
|
3
|
+
const SchemaInfoSchema = z
|
|
4
|
+
.strictObject({
|
|
5
|
+
hash: Keccak256HashSchema.meta({
|
|
6
|
+
title: 'Schema Hash',
|
|
7
|
+
description: 'Keccak256 hash of the JSON Schema this record was validated against',
|
|
8
|
+
}),
|
|
9
|
+
type: RecordSchemaTypeSchema.meta({
|
|
10
|
+
title: 'Schema Type',
|
|
11
|
+
description: 'Type/category of this schema',
|
|
12
|
+
}),
|
|
13
|
+
version: SemanticVersionSchema.meta({
|
|
14
|
+
title: 'Schema Version',
|
|
15
|
+
description: 'Version of the schema, using semantic versioning',
|
|
16
|
+
}),
|
|
17
|
+
})
|
|
18
|
+
.meta({
|
|
19
|
+
title: 'Schema Information',
|
|
20
|
+
});
|
|
21
|
+
const RecordCreatorSchema = z
|
|
22
|
+
.strictObject({
|
|
23
|
+
name: z.string().meta({
|
|
24
|
+
title: 'Creator Name',
|
|
25
|
+
description: 'Company or individual name that created this record',
|
|
26
|
+
examples: ['Carrot Foundation', 'Alice', 'Bob'],
|
|
27
|
+
}),
|
|
28
|
+
id: UuidSchema.meta({
|
|
29
|
+
title: 'Creator ID',
|
|
30
|
+
description: 'Unique identifier for the creator',
|
|
31
|
+
}),
|
|
32
|
+
})
|
|
33
|
+
.meta({
|
|
34
|
+
title: 'Creator',
|
|
35
|
+
description: 'Entity that created this record',
|
|
36
|
+
});
|
|
37
|
+
const RecordRelationshipSchema = z
|
|
38
|
+
.strictObject({
|
|
39
|
+
target_uri: IpfsUriSchema.meta({
|
|
40
|
+
title: 'Target IPFS URI',
|
|
41
|
+
description: 'Target IPFS URI of the referenced record',
|
|
42
|
+
}),
|
|
43
|
+
type: RecordRelationshipTypeSchema.meta({
|
|
44
|
+
title: 'Relationship Type',
|
|
45
|
+
description: 'Type of relationship to the referenced record',
|
|
46
|
+
}),
|
|
47
|
+
description: z
|
|
48
|
+
.string()
|
|
49
|
+
.optional()
|
|
50
|
+
.meta({
|
|
51
|
+
title: 'Relationship Description',
|
|
52
|
+
description: 'Human-readable description of the relationship',
|
|
53
|
+
examples: [
|
|
54
|
+
'This record supersedes the previous version',
|
|
55
|
+
'Related carbon credit batch',
|
|
56
|
+
'Source document for this verification',
|
|
57
|
+
'Child record derived from this parent',
|
|
58
|
+
'Updated version of original record',
|
|
59
|
+
],
|
|
60
|
+
}),
|
|
61
|
+
})
|
|
62
|
+
.meta({
|
|
63
|
+
title: 'Relationship',
|
|
64
|
+
description: 'Relationship to another IPFS record',
|
|
65
|
+
});
|
|
66
|
+
export const RecordEnvironmentSchema = z
|
|
67
|
+
.strictObject({
|
|
68
|
+
blockchain_network: z.enum(['mainnet', 'testnet']).meta({
|
|
69
|
+
title: 'Blockchain Network',
|
|
70
|
+
description: 'Blockchain Network Environment',
|
|
71
|
+
}),
|
|
72
|
+
deployment: z.enum(['production', 'development', 'testing']).meta({
|
|
73
|
+
title: 'Deployment Environment',
|
|
74
|
+
description: 'System environment where this record was generated',
|
|
75
|
+
}),
|
|
76
|
+
data_set_name: z.enum(['TEST', 'PROD']).meta({
|
|
77
|
+
title: 'Data Set Name',
|
|
78
|
+
description: 'Name of the data set for this record',
|
|
79
|
+
}),
|
|
80
|
+
})
|
|
81
|
+
.meta({
|
|
82
|
+
title: 'Environment',
|
|
83
|
+
description: 'Environment information',
|
|
84
|
+
});
|
|
85
|
+
export const BaseIpfsSchema = z
|
|
86
|
+
.strictObject({
|
|
87
|
+
$schema: z.url('Must be a valid URI').meta({
|
|
88
|
+
title: 'JSON Schema URI',
|
|
89
|
+
description: 'URI of the JSON Schema used to validate this record',
|
|
90
|
+
example: 'https://raw.githubusercontent.com/carrot-foundation/schemas/refs/heads/main/schemas/ipfs/shared/base/base.schema.json',
|
|
91
|
+
}),
|
|
92
|
+
schema: SchemaInfoSchema,
|
|
93
|
+
created_at: IsoTimestampSchema.meta({
|
|
94
|
+
title: 'Created At',
|
|
95
|
+
description: 'ISO 8601 creation timestamp for this record',
|
|
96
|
+
}),
|
|
97
|
+
external_id: ExternalIdSchema.meta({
|
|
98
|
+
title: 'External ID',
|
|
99
|
+
description: 'Off-chain reference ID (UUID from Carrot backend)',
|
|
100
|
+
}),
|
|
101
|
+
external_url: ExternalUrlSchema.meta({
|
|
102
|
+
title: 'External URL',
|
|
103
|
+
description: 'External URL of the content',
|
|
104
|
+
}),
|
|
105
|
+
original_content_hash: Sha256HashSchema.meta({
|
|
106
|
+
title: 'Original Content Hash',
|
|
107
|
+
description: 'SHA-256 hash of the original JSON content including private data before schema validation',
|
|
108
|
+
}),
|
|
109
|
+
content_hash: Sha256HashSchema.meta({
|
|
110
|
+
title: 'Content Hash',
|
|
111
|
+
description: 'SHA-256 hash of RFC 8785 canonicalized JSON after schema validation',
|
|
112
|
+
}),
|
|
113
|
+
creator: RecordCreatorSchema.optional(),
|
|
114
|
+
relationships: z.array(RecordRelationshipSchema).optional().meta({
|
|
115
|
+
title: 'Relationships',
|
|
116
|
+
description: 'References to other IPFS records this record relates to',
|
|
117
|
+
}),
|
|
118
|
+
environment: RecordEnvironmentSchema.optional(),
|
|
119
|
+
data: z.record(z.string(), z.unknown()).optional().meta({
|
|
120
|
+
title: 'Custom Data',
|
|
121
|
+
description: "Custom data block that includes the record's data",
|
|
122
|
+
}),
|
|
123
|
+
})
|
|
124
|
+
.meta({
|
|
125
|
+
title: 'Base IPFS Record',
|
|
126
|
+
description: 'Base fields for all Carrot IPFS records, providing common structure for any JSON content stored in IPFS',
|
|
127
|
+
});
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const UuidSchema = z.uuidv4('Must be a valid UUID v4 string').meta({
|
|
3
|
+
title: 'UUID',
|
|
4
|
+
description: 'A universally unique identifier version 4',
|
|
5
|
+
examples: [
|
|
6
|
+
'ad44dd3f-f176-4b98-bf78-5ee6e77d0530',
|
|
7
|
+
'6f520d88-864d-432d-bf9f-5c3166c4818f',
|
|
8
|
+
'f77afa89-1c58-40fd-9bf5-8a86703a8af4',
|
|
9
|
+
],
|
|
10
|
+
});
|
|
11
|
+
export const EthereumAddressSchema = z
|
|
12
|
+
.string()
|
|
13
|
+
.regex(/^0x[a-fA-F0-9]{40}$/, 'Must be a valid Ethereum address in lowercase hexadecimal format')
|
|
14
|
+
.meta({
|
|
15
|
+
title: 'Ethereum Address',
|
|
16
|
+
description: 'A valid Ethereum address in hexadecimal format',
|
|
17
|
+
examples: [
|
|
18
|
+
'0x1234567890abcdef1234567890abcdef12345678',
|
|
19
|
+
'0xabcdef1234567890abcdef1234567890abcdef12',
|
|
20
|
+
],
|
|
21
|
+
});
|
|
22
|
+
export const IsoTimestampSchema = z.iso
|
|
23
|
+
.datetime({
|
|
24
|
+
message: 'Must be a valid ISO 8601 timestamp with timezone',
|
|
25
|
+
})
|
|
26
|
+
.meta({
|
|
27
|
+
title: 'ISO Timestamp',
|
|
28
|
+
description: 'ISO 8601 formatted timestamp with timezone information',
|
|
29
|
+
examples: ['2024-12-05T11:02:47.000Z', '2025-02-22T10:35:12.000Z'],
|
|
30
|
+
});
|
|
31
|
+
export const IsoDateSchema = z.iso
|
|
32
|
+
.date('Must be a valid ISO 8601 date (YYYY-MM-DD)')
|
|
33
|
+
.meta({
|
|
34
|
+
title: 'ISO Date',
|
|
35
|
+
description: 'ISO 8601 formatted date in YYYY-MM-DD format',
|
|
36
|
+
examples: ['2024-12-05', '2025-02-22', '2024-02-10'],
|
|
37
|
+
});
|
|
38
|
+
export const IsoCountryCodeSchema = z
|
|
39
|
+
.string()
|
|
40
|
+
.regex(/^[A-Z]{2}$/, 'Must be a valid ISO 3166-1 alpha-2 country code')
|
|
41
|
+
.meta({
|
|
42
|
+
title: 'ISO Country Code',
|
|
43
|
+
description: 'Two-letter country code following ISO 3166-1 alpha-2 standard',
|
|
44
|
+
examples: ['BR', 'US', 'DE'],
|
|
45
|
+
});
|
|
46
|
+
export const IsoAdministrativeDivisionCodeSchema = z
|
|
47
|
+
.string()
|
|
48
|
+
.regex(/^[A-Z]{2}-[A-Z0-9]{1,3}$/, 'Must be a valid ISO 3166-2 administrative division code')
|
|
49
|
+
.meta({
|
|
50
|
+
title: 'ISO Administrative Division Code',
|
|
51
|
+
description: 'Administrative division code following ISO 3166-2 standard',
|
|
52
|
+
examples: ['BR-AP', 'BR-ES', 'US-CA'],
|
|
53
|
+
});
|
|
54
|
+
export const LatitudeSchema = z
|
|
55
|
+
.number()
|
|
56
|
+
.min(-90)
|
|
57
|
+
.max(90)
|
|
58
|
+
.meta({
|
|
59
|
+
title: 'Latitude',
|
|
60
|
+
description: 'Geographic latitude coordinate in decimal degrees',
|
|
61
|
+
examples: [-0.02, -20.38, 40.7128],
|
|
62
|
+
});
|
|
63
|
+
export const LongitudeSchema = z
|
|
64
|
+
.number()
|
|
65
|
+
.min(-180)
|
|
66
|
+
.max(180)
|
|
67
|
+
.meta({
|
|
68
|
+
title: 'Longitude',
|
|
69
|
+
description: 'Geographic longitude coordinate in decimal degrees',
|
|
70
|
+
examples: [-51.06, -40.34, -74.006],
|
|
71
|
+
});
|
|
72
|
+
export const WeightKgSchema = z
|
|
73
|
+
.number()
|
|
74
|
+
.min(0)
|
|
75
|
+
.meta({
|
|
76
|
+
title: 'Weight (kg)',
|
|
77
|
+
description: 'Weight measurement in kilograms',
|
|
78
|
+
examples: [3000, 1500, 500],
|
|
79
|
+
});
|
|
80
|
+
export const NonEmptyStringSchema = z
|
|
81
|
+
.string()
|
|
82
|
+
.min(1, 'Cannot be empty')
|
|
83
|
+
.meta({
|
|
84
|
+
title: 'Non-Empty String',
|
|
85
|
+
description: 'A string that contains at least one character',
|
|
86
|
+
examples: ['Example text', 'Sample value', 'Test string'],
|
|
87
|
+
});
|
|
88
|
+
export const SlugSchema = NonEmptyStringSchema.regex(/^[a-z0-9-]+$/, 'Must contain only lowercase letters, numbers, and hyphens')
|
|
89
|
+
.max(100)
|
|
90
|
+
.meta({
|
|
91
|
+
title: 'Slug',
|
|
92
|
+
description: 'URL-friendly identifier with lowercase letters, numbers, and hyphens',
|
|
93
|
+
examples: ['mass-id-123', 'recycled-plastic', 'organic-waste'],
|
|
94
|
+
});
|
|
95
|
+
export const WasteTypeSchema = NonEmptyStringSchema.meta({
|
|
96
|
+
title: 'Waste Type',
|
|
97
|
+
description: 'Category or type of waste material',
|
|
98
|
+
examples: ['Organic', 'Plastic', 'Metal'],
|
|
99
|
+
});
|
|
100
|
+
export const WasteSubtypeSchema = NonEmptyStringSchema.max(100).meta({
|
|
101
|
+
title: 'Waste Subtype',
|
|
102
|
+
description: 'Specific subcategory of waste within a waste type',
|
|
103
|
+
examples: ['Food, Food Waste and Beverages', 'PET Bottles', 'Aluminum Cans'],
|
|
104
|
+
});
|
|
105
|
+
export const ParticipantRoleSchema = NonEmptyStringSchema.max(100).meta({
|
|
106
|
+
title: 'Participant Role',
|
|
107
|
+
description: 'Role that a participant plays in the waste management supply chain',
|
|
108
|
+
examples: ['Waste Generator', 'Hauler', 'Recycler'],
|
|
109
|
+
});
|
|
110
|
+
export const ParticipantNameSchema = NonEmptyStringSchema.max(100).meta({
|
|
111
|
+
title: 'Participant Name',
|
|
112
|
+
description: 'Name of a participant in the waste management system',
|
|
113
|
+
examples: ['Enlatados Produção', 'Eco Reciclagem', 'Green Tech Corp'],
|
|
114
|
+
});
|
|
115
|
+
export const FacilityTypeSchema = z
|
|
116
|
+
.enum([
|
|
117
|
+
'Waste Generation',
|
|
118
|
+
'Collection Point',
|
|
119
|
+
'Transfer Station',
|
|
120
|
+
'Sorting Facility',
|
|
121
|
+
'Composting Facility',
|
|
122
|
+
'Recycling Facility',
|
|
123
|
+
'Processing Facility',
|
|
124
|
+
'Disposal Facility',
|
|
125
|
+
'Administrative Office',
|
|
126
|
+
])
|
|
127
|
+
.meta({
|
|
128
|
+
title: 'Facility Type',
|
|
129
|
+
description: 'Type of facility in the waste management infrastructure',
|
|
130
|
+
examples: ['Waste Generation', 'Recycling Facility', 'Collection Point'],
|
|
131
|
+
});
|
|
132
|
+
export const BlockchainChainIdSchema = z
|
|
133
|
+
.number()
|
|
134
|
+
.int()
|
|
135
|
+
.min(1)
|
|
136
|
+
.meta({
|
|
137
|
+
title: 'Chain ID',
|
|
138
|
+
description: 'Blockchain network identifier',
|
|
139
|
+
examples: [1, 137, 11155111],
|
|
140
|
+
});
|
|
141
|
+
export const PercentageSchema = z
|
|
142
|
+
.number()
|
|
143
|
+
.min(0)
|
|
144
|
+
.max(100)
|
|
145
|
+
.meta({
|
|
146
|
+
title: 'Percentage',
|
|
147
|
+
description: 'Percentage value between 0 and 100',
|
|
148
|
+
examples: [50, 75.5, 100],
|
|
149
|
+
});
|
|
150
|
+
export const NonNegativeIntegerSchema = z
|
|
151
|
+
.number()
|
|
152
|
+
.int()
|
|
153
|
+
.min(0)
|
|
154
|
+
.meta({
|
|
155
|
+
title: 'Non-Negative Integer',
|
|
156
|
+
description: 'Integer value that is zero or positive',
|
|
157
|
+
examples: [0, 123, 4126],
|
|
158
|
+
});
|
|
159
|
+
export const PositiveIntegerSchema = z
|
|
160
|
+
.number()
|
|
161
|
+
.int()
|
|
162
|
+
.min(1)
|
|
163
|
+
.meta({
|
|
164
|
+
title: 'Positive Integer',
|
|
165
|
+
description: 'Integer value that is greater than zero',
|
|
166
|
+
examples: [1, 123, 456],
|
|
167
|
+
});
|
|
168
|
+
export const NonNegativeFloatSchema = z
|
|
169
|
+
.number()
|
|
170
|
+
.min(0)
|
|
171
|
+
.meta({
|
|
172
|
+
title: 'Non-Negative Float',
|
|
173
|
+
description: 'Floating-point number that is zero or positive',
|
|
174
|
+
examples: [0.0, 45.2, 72.5],
|
|
175
|
+
});
|
|
176
|
+
export const HoursSchema = z
|
|
177
|
+
.number()
|
|
178
|
+
.min(0)
|
|
179
|
+
.multipleOf(0.1)
|
|
180
|
+
.meta({
|
|
181
|
+
title: 'Hours',
|
|
182
|
+
description: 'Time duration in hours with 0.1 hour precision',
|
|
183
|
+
examples: [72.5, 24.0, 168.5],
|
|
184
|
+
});
|
|
185
|
+
export const IpfsUriSchema = NonEmptyStringSchema.regex(/^ipfs:\/\/[a-zA-Z0-9]+(\/.*)?$/, 'Must be a valid IPFS URI with CID').meta({
|
|
186
|
+
title: 'IPFS URI',
|
|
187
|
+
description: 'InterPlanetary File System URI pointing to distributed content',
|
|
188
|
+
examples: [
|
|
189
|
+
'ipfs://QmTy8w65yBXgyfG2ZBg5TrfB2hPjrDQH3RCQFJGkARStJb/mass-id-organic.png',
|
|
190
|
+
'ipfs://QmYjtig7VJQ6XsnUjqqJvj7QaMcCAwtrgNdahSiFofrE7o',
|
|
191
|
+
],
|
|
192
|
+
});
|
|
193
|
+
export const SemanticVersionSchema = NonEmptyStringSchema.regex(/^v?\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/, 'Must be a valid semantic version string').meta({
|
|
194
|
+
title: 'Semantic Version',
|
|
195
|
+
description: 'Version string following semantic versioning specification',
|
|
196
|
+
examples: ['0.1.0', '1.0.0', '2.1.3'],
|
|
197
|
+
});
|
|
198
|
+
export const TokenIdSchema = NonEmptyStringSchema.regex(/^\d+$/, 'Must be a numeric string (supports uint256)').meta({
|
|
199
|
+
title: 'Token ID',
|
|
200
|
+
description: 'Numeric identifier for blockchain tokens as string',
|
|
201
|
+
examples: ['123', '456789', '1000000'],
|
|
202
|
+
});
|
|
203
|
+
export const HexColorSchema = NonEmptyStringSchema.regex(/^#[0-9A-F]{6}$/, 'Must be a hex color code with # prefix and uppercase').meta({
|
|
204
|
+
title: 'Hex Color',
|
|
205
|
+
description: 'Hexadecimal color code with hash prefix',
|
|
206
|
+
examples: ['#2D5A27', '#FF5733', '#1E90FF'],
|
|
207
|
+
});
|
|
208
|
+
export const Sha256HashSchema = z
|
|
209
|
+
.hash('sha256', {
|
|
210
|
+
error: 'Must be a SHA256 hash as 32-byte hex string',
|
|
211
|
+
})
|
|
212
|
+
.meta({
|
|
213
|
+
format: undefined,
|
|
214
|
+
title: 'SHA256 Hash',
|
|
215
|
+
description: 'SHA-256 cryptographic hash as hexadecimal string',
|
|
216
|
+
examples: [
|
|
217
|
+
'87f633634cc4b02f628685651f0a29b7bfa22a0bd841f725c6772dd00a58d489',
|
|
218
|
+
'6e83b8e6373847bbdc056549bedda38dc88854ce41ba4fca11e0fc6ce3e07ef6',
|
|
219
|
+
],
|
|
220
|
+
});
|
|
221
|
+
export const Keccak256HashSchema = Sha256HashSchema.meta({
|
|
222
|
+
title: 'Keccak256 Hash',
|
|
223
|
+
description: 'Keccak256 cryptographic hash as hexadecimal string',
|
|
224
|
+
examples: [
|
|
225
|
+
'ac08c3cf2e175e55961d6affdb38bc24591b84ceef7f3707c69ae3d52c148b2f',
|
|
226
|
+
'b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2',
|
|
227
|
+
],
|
|
228
|
+
});
|
|
229
|
+
export const ExternalIdSchema = UuidSchema.meta({
|
|
230
|
+
title: 'External ID',
|
|
231
|
+
description: 'UUID identifier for external system references',
|
|
232
|
+
examples: [
|
|
233
|
+
'ad44dd3f-f176-4b98-bf78-5ee6e77d0530',
|
|
234
|
+
'b2c4e6f8-a1b3-4c5d-9e8f-123456789abc',
|
|
235
|
+
],
|
|
236
|
+
});
|
|
237
|
+
export const ExternalUrlSchema = z.url('Must be a valid URL').meta({
|
|
238
|
+
title: 'External URL',
|
|
239
|
+
description: 'Valid URL pointing to external resources',
|
|
240
|
+
examples: [
|
|
241
|
+
'https://explore.carrot.eco/document/ad44dd3f-f176-4b98-bf78-5ee6e77d0530',
|
|
242
|
+
'https://carrot.eco/whitepaper.pdf',
|
|
243
|
+
],
|
|
244
|
+
});
|
|
245
|
+
export const RecordSchemaTypeSchema = z
|
|
246
|
+
.enum([
|
|
247
|
+
'MassID',
|
|
248
|
+
'MassID Audit',
|
|
249
|
+
'RecycledID',
|
|
250
|
+
'GasID',
|
|
251
|
+
'PurchaseID',
|
|
252
|
+
'Methodology',
|
|
253
|
+
'Credit',
|
|
254
|
+
'Collection',
|
|
255
|
+
])
|
|
256
|
+
.meta({
|
|
257
|
+
title: 'Schema Type',
|
|
258
|
+
description: 'Type of schema in the Carrot ecosystem',
|
|
259
|
+
examples: ['MassID', 'RecycledID', 'GasID'],
|
|
260
|
+
});
|
|
261
|
+
export const TokenSymbolSchema = NonEmptyStringSchema.max(10)
|
|
262
|
+
.regex(/^[A-Z0-9-]+$/, 'Must contain only uppercase letters, numbers, and hyphens')
|
|
263
|
+
.meta({
|
|
264
|
+
title: 'Token Symbol',
|
|
265
|
+
description: 'Symbol representing a token or cryptocurrency',
|
|
266
|
+
examples: ['MASS', 'REC', 'GAS'],
|
|
267
|
+
});
|
|
268
|
+
export const RecordRelationshipTypeSchema = z
|
|
269
|
+
.enum([
|
|
270
|
+
'collection',
|
|
271
|
+
'credit',
|
|
272
|
+
'gas-id',
|
|
273
|
+
'mass-id',
|
|
274
|
+
'mass-id-audit',
|
|
275
|
+
'methodology',
|
|
276
|
+
'purchase-id',
|
|
277
|
+
'recycled-id',
|
|
278
|
+
])
|
|
279
|
+
.meta({
|
|
280
|
+
title: 'Relationship Type',
|
|
281
|
+
description: 'Type of relationship between different entities in the system',
|
|
282
|
+
examples: ['mass-id', 'audit', 'collection'],
|
|
283
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { UuidSchema, NonEmptyStringSchema, LatitudeSchema, LongitudeSchema, IsoCountryCodeSchema, IsoAdministrativeDivisionCodeSchema, FacilityTypeSchema, } from '../definitions.schema.js';
|
|
3
|
+
const PrecisionLevelSchema = z
|
|
4
|
+
.enum(['exact', 'neighborhood', 'city', 'region', 'country'])
|
|
5
|
+
.meta({
|
|
6
|
+
title: 'Precision Level',
|
|
7
|
+
description: 'Level of coordinate precision',
|
|
8
|
+
examples: ['city', 'exact', 'neighborhood'],
|
|
9
|
+
});
|
|
10
|
+
export const CoordinatesSchema = z
|
|
11
|
+
.strictObject({
|
|
12
|
+
latitude: LatitudeSchema.meta({
|
|
13
|
+
title: 'Latitude',
|
|
14
|
+
description: 'GPS latitude coordinate',
|
|
15
|
+
}),
|
|
16
|
+
longitude: LongitudeSchema.meta({
|
|
17
|
+
title: 'Longitude',
|
|
18
|
+
description: 'GPS longitude coordinate',
|
|
19
|
+
}),
|
|
20
|
+
precision_level: PrecisionLevelSchema,
|
|
21
|
+
})
|
|
22
|
+
.meta({
|
|
23
|
+
title: 'Coordinates',
|
|
24
|
+
description: 'GPS coordinates of the location',
|
|
25
|
+
});
|
|
26
|
+
export const LocationSchema = z
|
|
27
|
+
.strictObject({
|
|
28
|
+
id: UuidSchema.meta({
|
|
29
|
+
title: 'Location ID',
|
|
30
|
+
description: 'Unique identifier for the location',
|
|
31
|
+
}),
|
|
32
|
+
municipality: NonEmptyStringSchema.max(50).meta({
|
|
33
|
+
title: 'Municipality',
|
|
34
|
+
description: 'Municipality or city name',
|
|
35
|
+
examples: ['New York', 'São Paulo', 'London', 'Tokyo'],
|
|
36
|
+
}),
|
|
37
|
+
administrative_division: NonEmptyStringSchema.max(50).meta({
|
|
38
|
+
title: 'Administrative Division',
|
|
39
|
+
description: 'State, province, or administrative region',
|
|
40
|
+
examples: ['California', 'Ontario', 'Bavaria', 'Queensland'],
|
|
41
|
+
}),
|
|
42
|
+
administrative_division_code: IsoAdministrativeDivisionCodeSchema.meta({
|
|
43
|
+
title: 'Administrative Division Code',
|
|
44
|
+
description: 'ISO 3166-2 administrative division code',
|
|
45
|
+
}),
|
|
46
|
+
country: NonEmptyStringSchema.max(50).meta({
|
|
47
|
+
title: 'Country',
|
|
48
|
+
description: 'Full country name in English',
|
|
49
|
+
examples: ['United States', 'Canada', 'Germany', 'Australia'],
|
|
50
|
+
}),
|
|
51
|
+
country_code: IsoCountryCodeSchema.meta({
|
|
52
|
+
title: 'Country Code',
|
|
53
|
+
description: 'ISO 3166-1 alpha-2 country code',
|
|
54
|
+
}),
|
|
55
|
+
responsible_participant_id: UuidSchema.meta({
|
|
56
|
+
title: 'Responsible Participant ID',
|
|
57
|
+
description: 'ID of the participant responsible for this location',
|
|
58
|
+
}),
|
|
59
|
+
coordinates: CoordinatesSchema,
|
|
60
|
+
facility_type: FacilityTypeSchema.meta({
|
|
61
|
+
title: 'Facility Type',
|
|
62
|
+
description: 'Type of facility at this location',
|
|
63
|
+
}),
|
|
64
|
+
})
|
|
65
|
+
.meta({
|
|
66
|
+
title: 'Location',
|
|
67
|
+
description: 'Geographic location with address and coordinate information',
|
|
68
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { UuidSchema, ParticipantNameSchema, ParticipantRoleSchema, } from '../definitions.schema.js';
|
|
3
|
+
import { uniqueArrayItems } from '../helpers.schema.js';
|
|
4
|
+
export const ParticipantSchema = z
|
|
5
|
+
.strictObject({
|
|
6
|
+
id: UuidSchema.meta({
|
|
7
|
+
title: 'Participant ID',
|
|
8
|
+
description: 'Unique identifier for the participant',
|
|
9
|
+
}),
|
|
10
|
+
name: ParticipantNameSchema.meta({
|
|
11
|
+
title: 'Participant Name',
|
|
12
|
+
description: 'Name of the participant',
|
|
13
|
+
}),
|
|
14
|
+
roles: uniqueArrayItems(ParticipantRoleSchema, 'Participant roles must be unique')
|
|
15
|
+
.min(1)
|
|
16
|
+
.meta({
|
|
17
|
+
title: 'Participant Roles',
|
|
18
|
+
description: 'Roles of the participant in the waste management supply chain',
|
|
19
|
+
}),
|
|
20
|
+
})
|
|
21
|
+
.meta({
|
|
22
|
+
title: 'Participant',
|
|
23
|
+
description: 'A participant in the waste management supply chain',
|
|
24
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function uniqueArrayItems(schema, errorMessage = 'Array items must be unique') {
|
|
3
|
+
return z
|
|
4
|
+
.array(schema)
|
|
5
|
+
.refine((items) => new Set(items).size === items.length, {
|
|
6
|
+
message: errorMessage,
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export function uniqueBy(schema, selector, errorMessage = 'Items must be unique') {
|
|
10
|
+
return z.array(schema).refine((items) => {
|
|
11
|
+
const values = items.map(selector);
|
|
12
|
+
return new Set(values).size === values.length;
|
|
13
|
+
}, {
|
|
14
|
+
message: errorMessage,
|
|
15
|
+
});
|
|
16
|
+
}
|