@geoprotocol/grc-20 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/builder/entity.d.ts +16 -11
  2. package/dist/builder/entity.d.ts.map +1 -1
  3. package/dist/builder/entity.js +26 -15
  4. package/dist/builder/entity.js.map +1 -1
  5. package/dist/builder/update.d.ts +16 -11
  6. package/dist/builder/update.d.ts.map +1 -1
  7. package/dist/builder/update.js +26 -15
  8. package/dist/builder/update.js.map +1 -1
  9. package/dist/codec/op.d.ts.map +1 -1
  10. package/dist/codec/op.js +56 -4
  11. package/dist/codec/op.js.map +1 -1
  12. package/dist/codec/value.d.ts.map +1 -1
  13. package/dist/codec/value.js +87 -50
  14. package/dist/codec/value.js.map +1 -1
  15. package/dist/index.d.ts +1 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +2 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/ops/index.d.ts +213 -0
  20. package/dist/ops/index.d.ts.map +1 -0
  21. package/dist/ops/index.js +203 -0
  22. package/dist/ops/index.js.map +1 -0
  23. package/dist/test/basic.test.js +359 -3
  24. package/dist/test/basic.test.js.map +1 -1
  25. package/dist/types/op.d.ts +8 -0
  26. package/dist/types/op.d.ts.map +1 -1
  27. package/dist/types/op.js.map +1 -1
  28. package/dist/types/value.d.ts +18 -17
  29. package/dist/types/value.d.ts.map +1 -1
  30. package/dist/types/value.js +35 -13
  31. package/dist/types/value.js.map +1 -1
  32. package/dist/util/datetime.d.ts +45 -0
  33. package/dist/util/datetime.d.ts.map +1 -0
  34. package/dist/util/datetime.js +245 -0
  35. package/dist/util/datetime.js.map +1 -0
  36. package/dist/util/index.d.ts +1 -0
  37. package/dist/util/index.d.ts.map +1 -1
  38. package/dist/util/index.js +1 -0
  39. package/dist/util/index.js.map +1 -1
  40. package/package.json +5 -1
  41. package/readme.md +100 -12
@@ -0,0 +1,245 @@
1
+ /**
2
+ * RFC 3339 date/time parsing and formatting utilities.
3
+ *
4
+ * Converts between RFC 3339 formatted strings and GRC-20 internal representations:
5
+ * - Date: days since Unix epoch (1970-01-01) + offset in minutes
6
+ * - Time: microseconds since midnight + offset in minutes
7
+ * - Datetime: microseconds since Unix epoch + offset in minutes
8
+ */
9
+ const MICROSECONDS_PER_SECOND = 1000000n;
10
+ const MICROSECONDS_PER_MINUTE = 60n * MICROSECONDS_PER_SECOND;
11
+ const MICROSECONDS_PER_HOUR = 60n * MICROSECONDS_PER_MINUTE;
12
+ const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
13
+ /**
14
+ * Parses a timezone offset string (Z, +HH:MM, -HH:MM) and returns offset in minutes.
15
+ */
16
+ function parseTimezoneOffset(offset) {
17
+ if (offset === "Z" || offset === "z") {
18
+ return 0;
19
+ }
20
+ const match = offset.match(/^([+-])(\d{2}):(\d{2})$/);
21
+ if (!match) {
22
+ throw new Error(`Invalid timezone offset: ${offset}`);
23
+ }
24
+ const sign = match[1] === "+" ? 1 : -1;
25
+ const hours = parseInt(match[2], 10);
26
+ const minutes = parseInt(match[3], 10);
27
+ if (hours > 23 || minutes > 59) {
28
+ throw new Error(`Invalid timezone offset: ${offset}`);
29
+ }
30
+ const totalMinutes = sign * (hours * 60 + minutes);
31
+ if (totalMinutes < -1440 || totalMinutes > 1440) {
32
+ throw new Error(`Timezone offset out of range [-24:00, +24:00]: ${offset}`);
33
+ }
34
+ return totalMinutes;
35
+ }
36
+ /**
37
+ * Formats an offset in minutes as a timezone string (Z, +HH:MM, -HH:MM).
38
+ */
39
+ function formatTimezoneOffset(offsetMin) {
40
+ if (offsetMin === 0) {
41
+ return "Z";
42
+ }
43
+ const sign = offsetMin >= 0 ? "+" : "-";
44
+ const absOffset = Math.abs(offsetMin);
45
+ const hours = Math.floor(absOffset / 60);
46
+ const minutes = absOffset % 60;
47
+ return `${sign}${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
48
+ }
49
+ /**
50
+ * Parses fractional seconds string and returns microseconds.
51
+ */
52
+ function parseFractionalSeconds(frac) {
53
+ if (!frac) {
54
+ return 0n;
55
+ }
56
+ // Pad or truncate to 6 digits (microseconds)
57
+ const padded = frac.padEnd(6, "0").slice(0, 6);
58
+ return BigInt(parseInt(padded, 10));
59
+ }
60
+ /**
61
+ * Formats microseconds as fractional seconds string, omitting if zero.
62
+ */
63
+ function formatFractionalSeconds(us) {
64
+ if (us === 0n) {
65
+ return "";
66
+ }
67
+ // Convert to 6-digit string and trim trailing zeros
68
+ const str = us.toString().padStart(6, "0");
69
+ const trimmed = str.replace(/0+$/, "");
70
+ return `.${trimmed}`;
71
+ }
72
+ // =====================
73
+ // DATE functions
74
+ // =====================
75
+ /**
76
+ * Parses an RFC 3339 date string (YYYY-MM-DD) and returns days since Unix epoch.
77
+ * Optionally accepts timezone offset suffix for date-with-offset format.
78
+ */
79
+ export function parseDateRfc3339(dateStr) {
80
+ // Match YYYY-MM-DD with optional timezone offset
81
+ const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})(?:(Z|[+-]\d{2}:\d{2}))?$/);
82
+ if (!match) {
83
+ throw new Error(`Invalid RFC 3339 date: ${dateStr}`);
84
+ }
85
+ const year = parseInt(match[1], 10);
86
+ const month = parseInt(match[2], 10);
87
+ const day = parseInt(match[3], 10);
88
+ const offsetStr = match[4];
89
+ // Validate month and day
90
+ if (month < 1 || month > 12) {
91
+ throw new Error(`Invalid month in date: ${dateStr}`);
92
+ }
93
+ if (day < 1 || day > 31) {
94
+ throw new Error(`Invalid day in date: ${dateStr}`);
95
+ }
96
+ // Calculate days since epoch using UTC Date
97
+ const date = Date.UTC(year, month - 1, day);
98
+ const days = Math.floor(date / MILLISECONDS_PER_DAY);
99
+ const offsetMin = offsetStr ? parseTimezoneOffset(offsetStr) : 0;
100
+ return { days, offsetMin };
101
+ }
102
+ /**
103
+ * Formats days since Unix epoch as RFC 3339 date string.
104
+ */
105
+ export function formatDateRfc3339(days, offsetMin = 0) {
106
+ const date = new Date(days * MILLISECONDS_PER_DAY);
107
+ const year = date.getUTCFullYear();
108
+ const month = (date.getUTCMonth() + 1).toString().padStart(2, "0");
109
+ const day = date.getUTCDate().toString().padStart(2, "0");
110
+ const offset = formatTimezoneOffset(offsetMin);
111
+ return `${year}-${month}-${day}${offset}`;
112
+ }
113
+ // =====================
114
+ // TIME functions
115
+ // =====================
116
+ /**
117
+ * Parses an RFC 3339 time string (HH:MM:SS[.ssssss][Z|+HH:MM]) and returns
118
+ * microseconds since midnight and offset in minutes.
119
+ */
120
+ export function parseTimeRfc3339(timeStr) {
121
+ // Match HH:MM:SS[.fractional][timezone]
122
+ const match = timeStr.match(/^(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,6}))?(Z|[+-]\d{2}:\d{2})?$/);
123
+ if (!match) {
124
+ throw new Error(`Invalid RFC 3339 time: ${timeStr}`);
125
+ }
126
+ const hours = parseInt(match[1], 10);
127
+ const minutes = parseInt(match[2], 10);
128
+ const seconds = parseInt(match[3], 10);
129
+ const fractional = match[4];
130
+ const offsetStr = match[5];
131
+ // Validate ranges
132
+ if (hours > 23) {
133
+ throw new Error(`Invalid hours in time: ${timeStr}`);
134
+ }
135
+ if (minutes > 59) {
136
+ throw new Error(`Invalid minutes in time: ${timeStr}`);
137
+ }
138
+ if (seconds > 59) {
139
+ throw new Error(`Invalid seconds in time: ${timeStr}`);
140
+ }
141
+ const microseconds = parseFractionalSeconds(fractional);
142
+ const timeMicros = BigInt(hours) * MICROSECONDS_PER_HOUR +
143
+ BigInt(minutes) * MICROSECONDS_PER_MINUTE +
144
+ BigInt(seconds) * MICROSECONDS_PER_SECOND +
145
+ microseconds;
146
+ // Validate total is within day
147
+ if (timeMicros > 86399999999n) {
148
+ throw new Error(`Time exceeds maximum (23:59:59.999999): ${timeStr}`);
149
+ }
150
+ const offsetMin = offsetStr ? parseTimezoneOffset(offsetStr) : 0;
151
+ return { timeMicros, offsetMin };
152
+ }
153
+ /**
154
+ * Formats microseconds since midnight as RFC 3339 time string.
155
+ */
156
+ export function formatTimeRfc3339(timeMicros, offsetMin = 0) {
157
+ const hours = Number(timeMicros / MICROSECONDS_PER_HOUR);
158
+ const remaining1 = timeMicros % MICROSECONDS_PER_HOUR;
159
+ const minutes = Number(remaining1 / MICROSECONDS_PER_MINUTE);
160
+ const remaining2 = remaining1 % MICROSECONDS_PER_MINUTE;
161
+ const seconds = Number(remaining2 / MICROSECONDS_PER_SECOND);
162
+ const microseconds = remaining2 % MICROSECONDS_PER_SECOND;
163
+ const hh = hours.toString().padStart(2, "0");
164
+ const mm = minutes.toString().padStart(2, "0");
165
+ const ss = seconds.toString().padStart(2, "0");
166
+ const frac = formatFractionalSeconds(microseconds);
167
+ const offset = formatTimezoneOffset(offsetMin);
168
+ return `${hh}:${mm}:${ss}${frac}${offset}`;
169
+ }
170
+ // =====================
171
+ // DATETIME functions
172
+ // =====================
173
+ /**
174
+ * Parses an RFC 3339 datetime string and returns microseconds since Unix epoch
175
+ * and offset in minutes.
176
+ */
177
+ export function parseDatetimeRfc3339(datetimeStr) {
178
+ // Match YYYY-MM-DDTHH:MM:SS[.fractional][timezone]
179
+ const match = datetimeStr.match(/^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,6}))?(Z|[+-]\d{2}:\d{2})?$/);
180
+ if (!match) {
181
+ throw new Error(`Invalid RFC 3339 datetime: ${datetimeStr}`);
182
+ }
183
+ const year = parseInt(match[1], 10);
184
+ const month = parseInt(match[2], 10);
185
+ const day = parseInt(match[3], 10);
186
+ const hours = parseInt(match[4], 10);
187
+ const minutes = parseInt(match[5], 10);
188
+ const seconds = parseInt(match[6], 10);
189
+ const fractional = match[7];
190
+ const offsetStr = match[8];
191
+ // Validate ranges
192
+ if (month < 1 || month > 12) {
193
+ throw new Error(`Invalid month in datetime: ${datetimeStr}`);
194
+ }
195
+ if (day < 1 || day > 31) {
196
+ throw new Error(`Invalid day in datetime: ${datetimeStr}`);
197
+ }
198
+ if (hours > 23) {
199
+ throw new Error(`Invalid hours in datetime: ${datetimeStr}`);
200
+ }
201
+ if (minutes > 59) {
202
+ throw new Error(`Invalid minutes in datetime: ${datetimeStr}`);
203
+ }
204
+ if (seconds > 59) {
205
+ throw new Error(`Invalid seconds in datetime: ${datetimeStr}`);
206
+ }
207
+ const offsetMin = offsetStr ? parseTimezoneOffset(offsetStr) : 0;
208
+ const microseconds = parseFractionalSeconds(fractional);
209
+ // Calculate epoch milliseconds in UTC
210
+ // Note: Date.UTC gives us milliseconds for the given UTC time components
211
+ const epochMs = Date.UTC(year, month - 1, day, hours, minutes, seconds);
212
+ // Convert to microseconds and add fractional component
213
+ // The epochMs is in UTC, but the datetime string represents local time with offset
214
+ // We need to subtract the offset to get the actual UTC time
215
+ const epochMicrosUTC = BigInt(epochMs) * 1000n + microseconds;
216
+ // Adjust for timezone offset: local time + offset = UTC
217
+ // So: UTC = local - offset
218
+ const offsetUs = BigInt(offsetMin) * MICROSECONDS_PER_MINUTE;
219
+ const epochMicros = epochMicrosUTC - offsetUs;
220
+ return { epochMicros, offsetMin };
221
+ }
222
+ /**
223
+ * Formats microseconds since Unix epoch as RFC 3339 datetime string.
224
+ */
225
+ export function formatDatetimeRfc3339(epochMicros, offsetMin = 0) {
226
+ // Adjust for timezone offset: local time = UTC + offset
227
+ const offsetUs = BigInt(offsetMin) * MICROSECONDS_PER_MINUTE;
228
+ const localUs = epochMicros + offsetUs;
229
+ // Convert to milliseconds for Date constructor
230
+ const epochMs = Number(localUs / 1000n);
231
+ const microseconds = localUs % 1000000n;
232
+ // Handle negative microseconds (modulo can be negative in JS for negative numbers)
233
+ const microsecondsPositive = microseconds < 0n ? microseconds + 1000000n : microseconds;
234
+ const date = new Date(epochMs);
235
+ const year = date.getUTCFullYear();
236
+ const month = (date.getUTCMonth() + 1).toString().padStart(2, "0");
237
+ const day = date.getUTCDate().toString().padStart(2, "0");
238
+ const hours = date.getUTCHours().toString().padStart(2, "0");
239
+ const minutes = date.getUTCMinutes().toString().padStart(2, "0");
240
+ const seconds = date.getUTCSeconds().toString().padStart(2, "0");
241
+ const frac = formatFractionalSeconds(microsecondsPositive);
242
+ const offset = formatTimezoneOffset(offsetMin);
243
+ return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}${frac}${offset}`;
244
+ }
245
+ //# sourceMappingURL=datetime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datetime.js","sourceRoot":"","sources":["../../src/util/datetime.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,uBAAuB,GAAG,QAAU,CAAC;AAC3C,MAAM,uBAAuB,GAAG,GAAG,GAAG,uBAAuB,CAAC;AAC9D,MAAM,qBAAqB,GAAG,GAAG,GAAG,uBAAuB,CAAC;AAC5D,MAAM,oBAAoB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEjD;;GAEG;AACH,SAAS,mBAAmB,CAAC,MAAc;IACzC,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACrC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;IACtD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,4BAA4B,MAAM,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEvC,IAAI,KAAK,GAAG,EAAE,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,4BAA4B,MAAM,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,KAAK,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC;IACnD,IAAI,YAAY,GAAG,CAAC,IAAI,IAAI,YAAY,GAAG,IAAI,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,kDAAkD,MAAM,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAAC,SAAiB;IAC7C,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACxC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,SAAS,GAAG,EAAE,CAAC;IAE/B,OAAO,GAAG,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AAC9F,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAAC,IAAwB;IACtD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,6CAA6C;IAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/C,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;AACtC,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAAC,EAAU;IACzC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACd,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,oDAAoD;IACpD,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvC,OAAO,IAAI,OAAO,EAAE,CAAC;AACvB,CAAC;AAED,wBAAwB;AACxB,iBAAiB;AACjB,wBAAwB;AAExB;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,iDAAiD;IACjD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACjF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACnC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAE3B,yBAAyB;IACzB,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,wBAAwB,OAAO,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,4CAA4C;IAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,oBAAoB,CAAC,CAAC;IAErD,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,YAAoB,CAAC;IACnE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC,CAAC;IACnD,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACnE,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAE1D,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAC/C,OAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG,GAAG,MAAM,EAAE,CAAC;AAC5C,CAAC;AAED,wBAAwB;AACxB,iBAAiB;AACjB,wBAAwB;AAExB;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,wCAAwC;IACxC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CACzB,+DAA+D,CAChE,CAAC;IACF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAE3B,kBAAkB;IAClB,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,4BAA4B,OAAO,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,4BAA4B,OAAO,EAAE,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,YAAY,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAAC;IACxD,MAAM,UAAU,GACd,MAAM,CAAC,KAAK,CAAC,GAAG,qBAAqB;QACrC,MAAM,CAAC,OAAO,CAAC,GAAG,uBAAuB;QACzC,MAAM,CAAC,OAAO,CAAC,GAAG,uBAAuB;QACzC,YAAY,CAAC;IAEf,+BAA+B;IAC/B,IAAI,UAAU,GAAG,YAAe,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,2CAA2C,OAAO,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAkB,EAAE,YAAoB,CAAC;IACzE,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,GAAG,qBAAqB,CAAC,CAAC;IACzD,MAAM,UAAU,GAAG,UAAU,GAAG,qBAAqB,CAAC;IACtD,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,GAAG,uBAAuB,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,UAAU,GAAG,uBAAuB,CAAC;IACxD,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,GAAG,uBAAuB,CAAC,CAAC;IAC7D,MAAM,YAAY,GAAG,UAAU,GAAG,uBAAuB,CAAC;IAE1D,MAAM,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC7C,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/C,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,uBAAuB,CAAC,YAAY,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAE/C,OAAO,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,GAAG,MAAM,EAAE,CAAC;AAC7C,CAAC;AAED,wBAAwB;AACxB,qBAAqB;AACrB,wBAAwB;AAExB;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,WAAmB;IACtD,mDAAmD;IACnD,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAC7B,0FAA0F,CAC3F,CAAC;IACF,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,8BAA8B,WAAW,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACrC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAE3B,kBAAkB;IAClB,IAAI,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,8BAA8B,WAAW,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,4BAA4B,WAAW,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,8BAA8B,WAAW,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,gCAAgC,WAAW,EAAE,CAAC,CAAC;IACjE,CAAC;IACD,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,gCAAgC,WAAW,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjE,MAAM,YAAY,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAAC;IAExD,sCAAsC;IACtC,yEAAyE;IACzE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAExE,uDAAuD;IACvD,mFAAmF;IACnF,4DAA4D;IAC5D,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,KAAK,GAAG,YAAY,CAAC;IAE9D,wDAAwD;IACxD,2BAA2B;IAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,uBAAuB,CAAC;IAC7D,MAAM,WAAW,GAAG,cAAc,GAAG,QAAQ,CAAC;IAE9C,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,WAAmB,EAAE,YAAoB,CAAC;IAC9E,wDAAwD;IACxD,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,uBAAuB,CAAC;IAC7D,MAAM,OAAO,GAAG,WAAW,GAAG,QAAQ,CAAC;IAEvC,+CAA+C;IAC/C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC;IACxC,MAAM,YAAY,GAAG,OAAO,GAAG,QAAU,CAAC;IAC1C,mFAAmF;IACnF,MAAM,oBAAoB,GAAG,YAAY,GAAG,EAAE,CAAC,CAAC,CAAC,YAAY,GAAG,QAAU,CAAC,CAAC,CAAC,YAAY,CAAC;IAE1F,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;IAE/B,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACnE,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACjE,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAEjE,MAAM,IAAI,GAAG,uBAAuB,CAAC,oBAAoB,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAE/C,OAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,OAAO,IAAI,OAAO,GAAG,IAAI,GAAG,MAAM,EAAE,CAAC;AAClF,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export { formatId, parseId, randomId, derivedUuid, derivedUuidAsync, derivedUuidFromString, uniqueRelationId, relationEntityId, } from "./id.js";
2
+ export { parseDateRfc3339, formatDateRfc3339, parseTimeRfc3339, formatTimeRfc3339, parseDatetimeRfc3339, formatDatetimeRfc3339, } from "./datetime.js";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/util/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/util/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,eAAe,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export { formatId, parseId, randomId, derivedUuid, derivedUuidAsync, derivedUuidFromString, uniqueRelationId, relationEntityId, } from "./id.js";
2
+ export { parseDateRfc3339, formatDateRfc3339, parseTimeRfc3339, formatTimeRfc3339, parseDatetimeRfc3339, formatDatetimeRfc3339, } from "./datetime.js";
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/util/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/util/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@geoprotocol/grc-20",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "GRC-20 TypeScript library for binary property graph encoding/decoding",
5
+ "private": false,
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
5
9
  "type": "module",
6
10
  "main": "./dist/index.js",
7
11
  "types": "./dist/index.d.ts",
package/readme.md CHANGED
@@ -100,6 +100,7 @@ import {
100
100
  EntityBuilder, // Build entity values
101
101
  UpdateEntityBuilder, // Build update operations
102
102
  RelationBuilder, // Build relation operations
103
+ UpdateRelationBuilder,// Build relation update operations
103
104
  } from "@geoprotocol/grc-20";
104
105
  ```
105
106
 
@@ -114,16 +115,19 @@ const edit = new EditBuilder(editId)
114
115
  .text(propId, "value", languageId)
115
116
  .int64(propId, 42n, unitId)
116
117
  .float64(propId, 3.14, undefined)
118
+ .decimal(propId, { exponent: -2, mantissa: 1234n }, undefined)
117
119
  .bool(propId, true)
118
120
  .bytes(propId, new Uint8Array([1, 2, 3]))
119
121
  .point(propId, 40.7128, -74.006)
120
- .date(propId, 19738, 0) // days since epoch, offset in minutes
121
- .time(propId, 37800000000n, 0) // microseconds since midnight, offset
122
- .datetime(propId, 1705312200000000n, 0) // microseconds since epoch, offset
122
+ .date(propId, "2024-01-15Z") // RFC 3339 date
123
+ .time(propId, "10:30:00Z") // RFC 3339 time
124
+ .datetime(propId, "2024-01-15T10:30:00Z")// RFC 3339 datetime
123
125
  )
124
126
  .updateEntity(entityId, u => u
125
127
  .setText(propId, "new value", undefined)
126
- .unsetAll(propId)
128
+ .setInt64(propId, 100n, undefined)
129
+ .unsetText(propId, languageId) // Unset specific language
130
+ .unsetAll(propId) // Unset all values for property
127
131
  )
128
132
  .deleteEntity(entityId)
129
133
  .restoreEntity(entityId)
@@ -134,9 +138,54 @@ const edit = new EditBuilder(editId)
134
138
  .relationType(relationTypeId)
135
139
  )
136
140
  .deleteRelation(relationId)
141
+ .restoreRelation(relationId)
142
+ .createValueRef(valueRefId, entityId, propId, {
143
+ type: "text",
144
+ value: "Referenceable value"
145
+ })
137
146
  .build();
138
147
  ```
139
148
 
149
+ ### Ops (Functional API)
150
+
151
+ `createEdit` defaults `id` to `randomId()` and `createdAt` to `0n` when omitted.
152
+
153
+ ```typescript
154
+ import {
155
+ createEdit,
156
+ createEntity,
157
+ createRelation,
158
+ randomId,
159
+ properties,
160
+ relationTypes,
161
+ } from "@geoprotocol/grc-20";
162
+
163
+ const entityId = randomId();
164
+ const authorId = randomId();
165
+
166
+ const ops = [
167
+ createEntity({
168
+ id: entityId,
169
+ values: [
170
+ { property: properties.name(), value: { type: "text", value: "Alice" } },
171
+ { property: properties.description(), value: { type: "text", value: "A person" } },
172
+ ],
173
+ }),
174
+ createRelation({
175
+ id: randomId(),
176
+ relationType: relationTypes.types(),
177
+ from: entityId,
178
+ to: randomId(),
179
+ }),
180
+ ];
181
+
182
+ const edit = createEdit({
183
+ name: "Create Alice",
184
+ author: authorId,
185
+ ops,
186
+ });
187
+ ```
188
+
140
189
  ### Codec
141
190
 
142
191
  ```typescript
@@ -205,18 +254,57 @@ If using native ES modules without a bundler, add an import map for the WASM dep
205
254
 
206
255
  ```typescript
207
256
  import {
208
- randomId, // Generate random UUIDv4
209
- parseId, // Parse hex string to Id
210
- formatId, // Format Id as hex string
211
- derivedUuid, // Derive UUIDv8 from bytes (SHA-256)
257
+ randomId, // Generate random UUIDv4
258
+ parseId, // Parse hex string to Id
259
+ formatId, // Format Id as hex string
260
+ derivedUuid, // Derive UUIDv8 from bytes (SHA-256, sync)
261
+ derivedUuidAsync, // Derive UUIDv8 from bytes (SHA-256, async)
212
262
  derivedUuidFromString,
213
- uniqueRelationId, // Derive relation ID from endpoints
214
- relationEntityId, // Derive entity ID from relation ID
215
- idsEqual, // Compare two Ids
216
- NIL_ID, // Zero UUID
263
+ uniqueRelationId, // Derive relation ID from endpoints
264
+ relationEntityId, // Derive entity ID from relation ID
265
+ idsEqual, // Compare two Ids for equality
266
+ compareIds, // Compare two Ids for ordering (-1, 0, 1)
267
+ NIL_ID, // Zero UUID
217
268
  } from "@geoprotocol/grc-20";
218
269
  ```
219
270
 
271
+ ### Validation
272
+
273
+ Validate values and positions before encoding:
274
+
275
+ ```typescript
276
+ import { validateValue, validatePosition } from "@geoprotocol/grc-20";
277
+
278
+ // Validate a value matches its declared type
279
+ const result = validateValue(value, DataType.Text);
280
+ if (!result.valid) {
281
+ console.error(result.error);
282
+ }
283
+
284
+ // Validate position string format
285
+ const posResult = validatePosition("a0");
286
+ if (!posResult.valid) {
287
+ console.error(posResult.error);
288
+ }
289
+ ```
290
+
291
+ ### Data Types Reference
292
+
293
+ | Type | TypeScript Representation |
294
+ |------|---------------------------|
295
+ | `BOOL` | `{ type: "bool", value: boolean }` |
296
+ | `INT64` | `{ type: "int64", value: bigint, unit?: Id }` |
297
+ | `FLOAT64` | `{ type: "float64", value: number, unit?: Id }` |
298
+ | `DECIMAL` | `{ type: "decimal", exponent: number, mantissa: bigint, unit?: Id }` |
299
+ | `TEXT` | `{ type: "text", value: string, language?: Id }` |
300
+ | `BYTES` | `{ type: "bytes", value: Uint8Array }` |
301
+ | `DATE` | `{ type: "date", value: string }` (RFC 3339, e.g. `"2024-01-15Z"`) |
302
+ | `TIME` | `{ type: "time", value: string }` (RFC 3339, e.g. `"14:30:00Z"`) |
303
+ | `DATETIME` | `{ type: "datetime", value: string }` (RFC 3339, e.g. `"2024-01-15T14:30:00Z"`) |
304
+ | `SCHEDULE` | `{ type: "schedule", value: string }` (cron-like) |
305
+ | `POINT` | `{ type: "point", lat: number, lon: number }` |
306
+ | `EMBEDDING` | `{ type: "embedding", subType: EmbeddingSubType.Float32 \| EmbeddingSubType.Int8 \| EmbeddingSubType.Binary, data: number[] }` |
307
+
220
308
  ### Genesis IDs
221
309
 
222
310
  Well-known IDs from the Genesis Space: