eyeling 1.28.4 → 1.28.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.
@@ -26,6 +26,7 @@ const {
26
26
  LIST_NS,
27
27
  LOG_NS,
28
28
  STRING_NS,
29
+ DT_NS,
29
30
  Literal,
30
31
  Iri,
31
32
  Var,
@@ -142,6 +143,10 @@ function __assertBuiltinHandlerResult(iri, out) {
142
143
  }
143
144
  }
144
145
 
146
+ function apiTermToN3(term, prefixes = PrefixEnv.newDefault()) {
147
+ return termToN3(term, prefixes || PrefixEnv.newDefault());
148
+ }
149
+
145
150
  function __buildBuiltinRegistrationApi() {
146
151
  if (__builtinApiSingleton) return __builtinApiSingleton;
147
152
 
@@ -154,7 +159,7 @@ function __buildBuiltinRegistrationApi() {
154
159
  literalParts,
155
160
  termToJsString,
156
161
  termToJsStringDecoded,
157
- termToN3,
162
+ termToN3: apiTermToN3,
158
163
  iriValue,
159
164
  unifyTerm,
160
165
  applySubstTerm,
@@ -171,7 +176,7 @@ function __buildBuiltinRegistrationApi() {
171
176
  literalsEquivalentAsXsdString,
172
177
  materializeRdfLists,
173
178
  terms: { Literal, Iri, Var, Blank, ListTerm, OpenListTerm, GraphTerm, Triple, Rule },
174
- ns: { RDF_NS, XSD_NS, CRYPTO_NS, MATH_NS, TIME_NS, LIST_NS, LOG_NS, STRING_NS },
179
+ ns: { RDF_NS, XSD_NS, CRYPTO_NS, MATH_NS, TIME_NS, LIST_NS, LOG_NS, STRING_NS, DT_NS },
175
180
  };
176
181
 
177
182
  __builtinApiSingleton = __freezeBuiltinApi(api);
@@ -908,6 +913,63 @@ const XSD_DOUBLE_DT = XSD_NS + 'double';
908
913
  const XSD_FLOAT_DT = XSD_NS + 'float';
909
914
  const XSD_INTEGER_DT = XSD_NS + 'integer';
910
915
 
916
+ const XSD_STRING_DT = XSD_NS + 'string';
917
+ const XSD_BOOLEAN_DT = XSD_NS + 'boolean';
918
+ const XSD_DATETIME_DT = XSD_NS + 'dateTime';
919
+ const XSD_DATETIME_STAMP_DT = XSD_NS + 'dateTimeStamp';
920
+ const XSD_HEX_BINARY_DT = XSD_NS + 'hexBinary';
921
+ const XSD_BASE64_BINARY_DT = XSD_NS + 'base64Binary';
922
+ const XSD_ANY_URI_DT = XSD_NS + 'anyURI';
923
+ const RDF_LANGSTRING_DT = RDF_NS + 'langString';
924
+
925
+ const XSD_STRING_LIKE_DTS = new Set([
926
+ XSD_STRING_DT,
927
+ XSD_NS + 'normalizedString',
928
+ XSD_NS + 'token',
929
+ XSD_NS + 'language',
930
+ XSD_NS + 'Name',
931
+ XSD_NS + 'NCName',
932
+ XSD_NS + 'NMTOKEN',
933
+ ]);
934
+
935
+ const XSD_INTEGER_BOUNDS = new Map([
936
+ [XSD_NS + 'nonPositiveInteger', [null, 0n]],
937
+ [XSD_NS + 'negativeInteger', [null, -1n]],
938
+ [XSD_NS + 'long', [-(2n ** 63n), 2n ** 63n - 1n]],
939
+ [XSD_NS + 'int', [-(2n ** 31n), 2n ** 31n - 1n]],
940
+ [XSD_NS + 'short', [-(2n ** 15n), 2n ** 15n - 1n]],
941
+ [XSD_NS + 'byte', [-(2n ** 7n), 2n ** 7n - 1n]],
942
+ [XSD_NS + 'nonNegativeInteger', [0n, null]],
943
+ [XSD_NS + 'unsignedLong', [0n, 2n ** 64n - 1n]],
944
+ [XSD_NS + 'unsignedInt', [0n, 2n ** 32n - 1n]],
945
+ [XSD_NS + 'unsignedShort', [0n, 2n ** 16n - 1n]],
946
+ [XSD_NS + 'unsignedByte', [0n, 2n ** 8n - 1n]],
947
+ [XSD_NS + 'positiveInteger', [1n, null]],
948
+ ]);
949
+
950
+ const XSD_DATATYPE_BUILTIN_DTS = new Set([
951
+ RDF_LANGSTRING_DT,
952
+ XSD_STRING_DT,
953
+ XSD_NS + 'normalizedString',
954
+ XSD_NS + 'token',
955
+ XSD_NS + 'language',
956
+ XSD_NS + 'Name',
957
+ XSD_NS + 'NCName',
958
+ XSD_NS + 'NMTOKEN',
959
+ XSD_BOOLEAN_DT,
960
+ XSD_DECIMAL_DT,
961
+ XSD_INTEGER_DT,
962
+ XSD_FLOAT_DT,
963
+ XSD_DOUBLE_DT,
964
+ XSD_HEX_BINARY_DT,
965
+ XSD_BASE64_BINARY_DT,
966
+ XSD_ANY_URI_DT,
967
+ XSD_DATETIME_DT,
968
+ XSD_DATETIME_STAMP_DT,
969
+ ...XSD_INTEGER_BOUNDS.keys(),
970
+ ]);
971
+
972
+
911
973
  // Integer-derived datatypes from XML Schema Part 2 (and commonly used ones).
912
974
  const XSD_INTEGER_DERIVED_DTS = new Set([
913
975
  XSD_INTEGER_DT,
@@ -963,6 +1025,537 @@ function inferDatatypeForShorthandLexical(lex) {
963
1025
  return null;
964
1026
  }
965
1027
 
1028
+
1029
+ function extractLiteralLanguageTag(litVal) {
1030
+ if (typeof litVal !== 'string' || !literalHasLangTag(litVal)) return null;
1031
+ const lastQuote = litVal.lastIndexOf('"');
1032
+ if (lastQuote < 0) return null;
1033
+ const after = lastQuote + 1;
1034
+ if (after >= litVal.length || litVal[after] !== '@') return null;
1035
+ const tag = litVal.slice(after + 1);
1036
+ if (!/^[A-Za-z]+(?:-[A-Za-z0-9]+)*(?:--(?:ltr|rtl))?$/.test(tag)) return null;
1037
+ return tag;
1038
+ }
1039
+
1040
+ function literalLexicalValue(litVal) {
1041
+ const [lex] = literalParts(litVal);
1042
+ if (isQuotedLexical(lex)) return decodeN3StringEscapes(stripQuotes(lex));
1043
+ return typeof lex === 'string' ? lex : String(lex);
1044
+ }
1045
+
1046
+ function literalDatatypeIri(t) {
1047
+ if (!(t instanceof Literal)) return null;
1048
+ if (literalHasLangTag(t.value)) return RDF_LANGSTRING_DT;
1049
+
1050
+ const [lex, dt] = literalParts(t.value);
1051
+ if (dt !== null) return dt;
1052
+ if (isPlainStringLiteralValue(t.value)) return XSD_STRING_DT;
1053
+
1054
+ return inferDatatypeForShorthandLexical(lex) || XSD_STRING_DT;
1055
+ }
1056
+
1057
+ function isSupportedDatatypeIri(dt) {
1058
+ return XSD_DATATYPE_BUILTIN_DTS.has(dt);
1059
+ }
1060
+
1061
+ function makeDatatypeTypedLiteral(lexical, dt) {
1062
+ return internLiteral(`${JSON.stringify(String(lexical))}^^<${dt}>`);
1063
+ }
1064
+
1065
+ function makeLangStringLiteral(lexical, lang) {
1066
+ return internLiteral(`${JSON.stringify(String(lexical))}@${String(lang).toLowerCase()}`);
1067
+ }
1068
+
1069
+ function canonicalIntegerLex(v) {
1070
+ return v === 0n ? '0' : v.toString();
1071
+ }
1072
+
1073
+ function canonicalDecimalLexFromParts(num, scale) {
1074
+ if (num === 0n) return '0.0';
1075
+ const neg = num < 0n;
1076
+ let digits = (neg ? -num : num).toString();
1077
+ if (scale > 0) {
1078
+ while (digits.length <= scale) digits = `0${digits}`;
1079
+ }
1080
+ let intPart = scale > 0 ? digits.slice(0, digits.length - scale) : digits;
1081
+ let fracPart = scale > 0 ? digits.slice(digits.length - scale) : '';
1082
+ intPart = intPart.replace(/^0+(?=\d)/, '') || '0';
1083
+ fracPart = fracPart.replace(/0+$/g, '');
1084
+ if (!fracPart) fracPart = '0';
1085
+ return `${neg ? '-' : ''}${intPart}.${fracPart}`;
1086
+ }
1087
+
1088
+ function canonicalFloatDoubleLex(n) {
1089
+ const special = formatXsdFloatSpecialLex(n);
1090
+ if (special !== null) return special;
1091
+ if (Object.is(n, -0)) return '0';
1092
+ return String(n).replace('e', 'E');
1093
+ }
1094
+
1095
+ function parseStringLikeDatatypeValue(lexical, dt) {
1096
+ let value = String(lexical);
1097
+
1098
+ if (dt === XSD_NS + 'normalizedString') {
1099
+ value = value.replace(/[\t\n\r]/g, ' ');
1100
+ } else if (dt !== XSD_STRING_DT) {
1101
+ value = value.replace(/[\t\n\r ]+/g, ' ').trim();
1102
+ }
1103
+
1104
+ if (dt === XSD_NS + 'language') {
1105
+ if (!/^[A-Za-z]{1,8}(?:-[A-Za-z0-9]{1,8})*$/.test(value)) return null;
1106
+ value = value.toLowerCase();
1107
+ } else if (dt === XSD_NS + 'Name') {
1108
+ if (!/^[:_\p{L}][:_\p{L}\p{N}.\-\u00B7\u0300-\u036F\u203F-\u2040]*$/u.test(value)) return null;
1109
+ } else if (dt === XSD_NS + 'NCName') {
1110
+ if (!/^[_\p{L}][_\p{L}\p{N}.\-\u00B7\u0300-\u036F\u203F-\u2040]*$/u.test(value)) return null;
1111
+ } else if (dt === XSD_NS + 'NMTOKEN') {
1112
+ if (!/^[:_\p{L}\p{N}.\-\u00B7\u0300-\u036F\u203F-\u2040]+$/u.test(value)) return null;
1113
+ }
1114
+
1115
+ return {
1116
+ family: 'string',
1117
+ dt,
1118
+ value,
1119
+ canonicalLex: value,
1120
+ canonicalLiteral: makeDatatypeTypedLiteral(value, dt),
1121
+ comparable: true,
1122
+ };
1123
+ }
1124
+
1125
+ function parseBooleanDatatypeValue(lexical) {
1126
+ const s = String(lexical);
1127
+ if (s === 'true' || s === '1') {
1128
+ return {
1129
+ family: 'boolean',
1130
+ dt: XSD_BOOLEAN_DT,
1131
+ value: true,
1132
+ canonicalLex: 'true',
1133
+ canonicalLiteral: makeDatatypeTypedLiteral('true', XSD_BOOLEAN_DT),
1134
+ comparable: true,
1135
+ };
1136
+ }
1137
+ if (s === 'false' || s === '0') {
1138
+ return {
1139
+ family: 'boolean',
1140
+ dt: XSD_BOOLEAN_DT,
1141
+ value: false,
1142
+ canonicalLex: 'false',
1143
+ canonicalLiteral: makeDatatypeTypedLiteral('false', XSD_BOOLEAN_DT),
1144
+ comparable: true,
1145
+ };
1146
+ }
1147
+ return null;
1148
+ }
1149
+
1150
+ function parseIntegerDatatypeValue(lexical, dt) {
1151
+ const s = String(lexical);
1152
+ if (!/^[+-]?\d+$/.test(s)) return null;
1153
+ let value;
1154
+ try {
1155
+ value = BigInt(s);
1156
+ } catch (_) {
1157
+ return null;
1158
+ }
1159
+
1160
+ const bounds = XSD_INTEGER_BOUNDS.get(dt);
1161
+ if (bounds) {
1162
+ const [min, max] = bounds;
1163
+ if (min !== null && value < min) return null;
1164
+ if (max !== null && value > max) return null;
1165
+ }
1166
+
1167
+ const canonicalLex = canonicalIntegerLex(value);
1168
+ return {
1169
+ family: 'numeric',
1170
+ numericKind: 'decimalExact',
1171
+ dt,
1172
+ decimalNum: value,
1173
+ decimalScale: 0,
1174
+ value,
1175
+ canonicalLex,
1176
+ canonicalLiteral: makeDatatypeTypedLiteral(canonicalLex, dt),
1177
+ comparable: true,
1178
+ };
1179
+ }
1180
+
1181
+ function parseDecimalDatatypeValue(lexical) {
1182
+ const parsed = parseXsdDecimalToBigIntScale(String(lexical));
1183
+ if (!parsed) return null;
1184
+ const canonicalLex = canonicalDecimalLexFromParts(parsed.num, parsed.scale);
1185
+ return {
1186
+ family: 'numeric',
1187
+ numericKind: 'decimalExact',
1188
+ dt: XSD_DECIMAL_DT,
1189
+ decimalNum: parsed.num,
1190
+ decimalScale: parsed.scale,
1191
+ canonicalLex,
1192
+ canonicalLiteral: makeDatatypeTypedLiteral(canonicalLex, XSD_DECIMAL_DT),
1193
+ comparable: true,
1194
+ };
1195
+ }
1196
+
1197
+ function isXsdFloatDoubleLexical(s) {
1198
+ if (parseXsdFloatSpecialLex(s) !== null) return true;
1199
+ return /^[+-]?(?:(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?|\d+(?:[eE][+-]?\d+))$/.test(s);
1200
+ }
1201
+
1202
+ function parseFloatDoubleDatatypeValue(lexical, dt) {
1203
+ const s = String(lexical);
1204
+ if (!isXsdFloatDoubleLexical(s)) return null;
1205
+ const special = parseXsdFloatSpecialLex(s);
1206
+ const value = special !== null ? special : Number(s);
1207
+ if (Number.isNaN(value) && s !== 'NaN') return null;
1208
+ const canonicalLex = canonicalFloatDoubleLex(value);
1209
+ return {
1210
+ family: 'numeric',
1211
+ numericKind: 'floatDouble',
1212
+ dt,
1213
+ value,
1214
+ canonicalLex,
1215
+ canonicalLiteral: makeDatatypeTypedLiteral(canonicalLex, dt),
1216
+ comparable: !Number.isNaN(value),
1217
+ };
1218
+ }
1219
+
1220
+ function normalizeBase64Lexical(s) {
1221
+ return String(s).replace(/[\t\n\r ]+/g, '');
1222
+ }
1223
+
1224
+ const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
1225
+ const BASE64_LOOKUP = (() => {
1226
+ const m = Object.create(null);
1227
+ for (let i = 0; i < BASE64_ALPHABET.length; i++) m[BASE64_ALPHABET[i]] = i;
1228
+ return m;
1229
+ })();
1230
+
1231
+ function decodeBase64Bytes(input) {
1232
+ const s = normalizeBase64Lexical(input);
1233
+ if (s.length === 0) return [];
1234
+ if (s.length % 4 !== 0) return null;
1235
+ if (!/^[A-Za-z0-9+/]*={0,2}$/.test(s)) return null;
1236
+ const pad = s.endsWith('==') ? 2 : s.endsWith('=') ? 1 : 0;
1237
+ if (pad && s.slice(0, -pad).includes('=')) return null;
1238
+
1239
+ const out = [];
1240
+ for (let i = 0; i < s.length; i += 4) {
1241
+ const c0 = BASE64_LOOKUP[s[i]];
1242
+ const c1 = BASE64_LOOKUP[s[i + 1]];
1243
+ const c2 = s[i + 2] === '=' ? 0 : BASE64_LOOKUP[s[i + 2]];
1244
+ const c3 = s[i + 3] === '=' ? 0 : BASE64_LOOKUP[s[i + 3]];
1245
+ if (c0 == null || c1 == null || c2 == null || c3 == null) return null;
1246
+ const n = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3;
1247
+ out.push((n >> 16) & 0xff);
1248
+ if (s[i + 2] !== '=') out.push((n >> 8) & 0xff);
1249
+ if (s[i + 3] !== '=') out.push(n & 0xff);
1250
+ if ((s[i + 2] === '=' || s[i + 3] === '=') && i + 4 !== s.length) return null;
1251
+ }
1252
+ return out;
1253
+ }
1254
+
1255
+ function encodeBase64Bytes(bytes) {
1256
+ let out = '';
1257
+ for (let i = 0; i < bytes.length; i += 3) {
1258
+ const b0 = bytes[i];
1259
+ const b1 = i + 1 < bytes.length ? bytes[i + 1] : 0;
1260
+ const b2 = i + 2 < bytes.length ? bytes[i + 2] : 0;
1261
+ const n = (b0 << 16) | (b1 << 8) | b2;
1262
+ out += BASE64_ALPHABET[(n >> 18) & 63];
1263
+ out += BASE64_ALPHABET[(n >> 12) & 63];
1264
+ out += i + 1 < bytes.length ? BASE64_ALPHABET[(n >> 6) & 63] : '=';
1265
+ out += i + 2 < bytes.length ? BASE64_ALPHABET[n & 63] : '=';
1266
+ }
1267
+ return out;
1268
+ }
1269
+
1270
+ function parseBinaryDatatypeValue(lexical, dt) {
1271
+ const s = String(lexical);
1272
+ let bytes;
1273
+ let canonicalLex;
1274
+ if (dt === XSD_HEX_BINARY_DT) {
1275
+ if (!/^(?:[0-9A-Fa-f]{2})*$/.test(s)) return null;
1276
+ bytes = [];
1277
+ for (let i = 0; i < s.length; i += 2) bytes.push(parseInt(s.slice(i, i + 2), 16));
1278
+ canonicalLex = s.toUpperCase();
1279
+ } else {
1280
+ bytes = decodeBase64Bytes(s);
1281
+ if (bytes === null) return null;
1282
+ canonicalLex = encodeBase64Bytes(bytes);
1283
+ }
1284
+
1285
+ return {
1286
+ family: 'binary',
1287
+ dt,
1288
+ bytes,
1289
+ canonicalLex,
1290
+ canonicalLiteral: makeDatatypeTypedLiteral(canonicalLex, dt),
1291
+ comparable: true,
1292
+ };
1293
+ }
1294
+
1295
+ function parseAnyUriDatatypeValue(lexical) {
1296
+ const value = String(lexical);
1297
+ if (/[\u0000-\u001F\u007F]/.test(value)) return null;
1298
+ return {
1299
+ family: 'anyURI',
1300
+ dt: XSD_ANY_URI_DT,
1301
+ value,
1302
+ canonicalLex: value,
1303
+ canonicalLiteral: makeDatatypeTypedLiteral(value, XSD_ANY_URI_DT),
1304
+ comparable: true,
1305
+ };
1306
+ }
1307
+
1308
+ function isLeapYearNumber(year) {
1309
+ if (year % 400 === 0) return true;
1310
+ if (year % 100 === 0) return false;
1311
+ return year % 4 === 0;
1312
+ }
1313
+
1314
+ function daysInMonthNumber(year, month) {
1315
+ if (month === 2) return isLeapYearNumber(year) ? 29 : 28;
1316
+ return [4, 6, 9, 11].includes(month) ? 30 : 31;
1317
+ }
1318
+
1319
+ function floorDivNumber(a, b) {
1320
+ return Math.floor(a / b);
1321
+ }
1322
+
1323
+ function daysFromCivilNumber(year, month, day) {
1324
+ let y = year;
1325
+ y -= month <= 2 ? 1 : 0;
1326
+ const era = floorDivNumber(y, 400);
1327
+ const yoe = y - era * 400;
1328
+ const mp = month + (month > 2 ? -3 : 9);
1329
+ const doy = Math.floor((153 * mp + 2) / 5) + day - 1;
1330
+ const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;
1331
+ return era * 146097 + doe - 719468;
1332
+ }
1333
+
1334
+ function parseDateTimeDatatypeValue(lexical, dt) {
1335
+ const s = String(lexical);
1336
+ const m = /^(-?\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(Z|[+-]\d{2}:\d{2})?$/.exec(s);
1337
+ if (!m) return null;
1338
+
1339
+ const year = Number(m[1]);
1340
+ const month = Number(m[2]);
1341
+ let day = Number(m[3]);
1342
+ let hour = Number(m[4]);
1343
+ const minute = Number(m[5]);
1344
+ const second = Number(m[6]);
1345
+ let frac = m[7] || '';
1346
+ const tz = m[8] || null;
1347
+ if (!Number.isSafeInteger(year)) return null;
1348
+ if (dt === XSD_DATETIME_STAMP_DT && !tz) return null;
1349
+ if (!(month >= 1 && month <= 12)) return null;
1350
+ if (!(day >= 1 && day <= daysInMonthNumber(year, month))) return null;
1351
+ if (!(minute >= 0 && minute <= 59)) return null;
1352
+ if (!(second >= 0 && second <= 59)) return null;
1353
+ if (hour === 24) {
1354
+ if (minute !== 0 || second !== 0 || /[1-9]/.test(frac)) return null;
1355
+ hour = 0;
1356
+ const d = new Date(Date.UTC(year, month - 1, day));
1357
+ d.setUTCFullYear(year);
1358
+ d.setUTCDate(d.getUTCDate() + 1);
1359
+ day = d.getUTCDate();
1360
+ } else if (!(hour >= 0 && hour <= 23)) {
1361
+ return null;
1362
+ }
1363
+
1364
+ frac = frac.replace(/0+$/g, '');
1365
+ const fracScale = frac.length;
1366
+ const fracNum = frac ? BigInt(frac) : 0n;
1367
+
1368
+ let offsetMinutes = null;
1369
+ if (tz) {
1370
+ if (tz === 'Z') {
1371
+ offsetMinutes = 0;
1372
+ } else {
1373
+ const sign = tz[0] === '-' ? -1 : 1;
1374
+ const hh = Number(tz.slice(1, 3));
1375
+ const mm = Number(tz.slice(4, 6));
1376
+ if (!(hh >= 0 && hh <= 14) || !(mm >= 0 && mm <= 59)) return null;
1377
+ if (hh === 14 && mm !== 0) return null;
1378
+ offsetMinutes = sign * (hh * 60 + mm);
1379
+ }
1380
+ }
1381
+
1382
+ const dayCount = daysFromCivilNumber(year, month, day);
1383
+ let totalSeconds = BigInt(dayCount) * 86400n + BigInt(hour * 3600 + minute * 60 + second);
1384
+ if (offsetMinutes !== null) totalSeconds -= BigInt(offsetMinutes * 60);
1385
+
1386
+ const localKey = `${m[1]}-${m[2]}-${String(day).padStart(2, '0')}T${String(hour).padStart(2, '0')}:${m[5]}:${m[6]}${frac ? `.${frac}` : ''}`;
1387
+ const canonicalLex = canonicalDateTimeLex(totalSeconds, fracNum, fracScale, offsetMinutes !== null, localKey);
1388
+ return {
1389
+ family: 'dateTime',
1390
+ dt,
1391
+ hasTimezone: offsetMinutes !== null,
1392
+ totalSeconds,
1393
+ fracNum,
1394
+ fracScale,
1395
+ localKey,
1396
+ canonicalLex,
1397
+ canonicalLiteral: makeDatatypeTypedLiteral(canonicalLex, dt),
1398
+ comparable: true,
1399
+ };
1400
+ }
1401
+
1402
+ function canonicalDateTimeLex(totalSeconds, fracNum, fracScale, hasTimezone, localKey) {
1403
+ const frac = fracScale > 0 ? `.${fracNum.toString().padStart(fracScale, '0')}` : '';
1404
+ if (!hasTimezone) return localKey;
1405
+
1406
+ const minSafeSeconds = BigInt(Math.ceil(Number.MIN_SAFE_INTEGER / 1000));
1407
+ const maxSafeSeconds = BigInt(Math.floor(Number.MAX_SAFE_INTEGER / 1000));
1408
+ if (totalSeconds < minSafeSeconds || totalSeconds > maxSafeSeconds) return `${localKey}Z`;
1409
+
1410
+ const d = new Date(Number(totalSeconds) * 1000);
1411
+ if (Number.isNaN(d.getTime())) return `${localKey}Z`;
1412
+ let iso = d.toISOString().replace(/\.000Z$/, 'Z');
1413
+ if (frac) iso = iso.replace('Z', `${frac}Z`);
1414
+ return iso;
1415
+ }
1416
+
1417
+ function parseLangStringDatatypeValue(t, lexical) {
1418
+ const tag = extractLiteralLanguageTag(t.value);
1419
+ if (tag === null) return null;
1420
+ return {
1421
+ family: 'langString',
1422
+ dt: RDF_LANGSTRING_DT,
1423
+ value: String(lexical),
1424
+ lang: tag.toLowerCase(),
1425
+ canonicalLex: String(lexical),
1426
+ canonicalLiteral: makeLangStringLiteral(lexical, tag),
1427
+ comparable: true,
1428
+ };
1429
+ }
1430
+
1431
+ function parseDatatypeValueForDatatype(t, dt) {
1432
+ if (!(t instanceof Literal) || typeof dt !== 'string' || !isSupportedDatatypeIri(dt)) return null;
1433
+ const lexical = literalLexicalValue(t.value);
1434
+
1435
+ if (dt === RDF_LANGSTRING_DT) return parseLangStringDatatypeValue(t, lexical);
1436
+ if (literalHasLangTag(t.value)) return null;
1437
+ if (XSD_STRING_LIKE_DTS.has(dt)) return parseStringLikeDatatypeValue(lexical, dt);
1438
+ if (dt === XSD_BOOLEAN_DT) return parseBooleanDatatypeValue(lexical);
1439
+ if (dt === XSD_INTEGER_DT || XSD_INTEGER_BOUNDS.has(dt)) return parseIntegerDatatypeValue(lexical, dt);
1440
+ if (dt === XSD_DECIMAL_DT) return parseDecimalDatatypeValue(lexical);
1441
+ if (dt === XSD_FLOAT_DT || dt === XSD_DOUBLE_DT) return parseFloatDoubleDatatypeValue(lexical, dt);
1442
+ if (dt === XSD_HEX_BINARY_DT || dt === XSD_BASE64_BINARY_DT) return parseBinaryDatatypeValue(lexical, dt);
1443
+ if (dt === XSD_ANY_URI_DT) return parseAnyUriDatatypeValue(lexical);
1444
+ if (dt === XSD_DATETIME_DT || dt === XSD_DATETIME_STAMP_DT) return parseDateTimeDatatypeValue(lexical, dt);
1445
+
1446
+ return null;
1447
+ }
1448
+
1449
+ function parseDatatypeValue(t) {
1450
+ const dt = literalDatatypeIri(t);
1451
+ return dt === null ? null : parseDatatypeValueForDatatype(t, dt);
1452
+ }
1453
+
1454
+ function compareDecimalExactValues(a, b) {
1455
+ const scale = Math.max(a.decimalScale, b.decimalScale);
1456
+ return a.decimalNum * pow10n(scale - a.decimalScale) === b.decimalNum * pow10n(scale - b.decimalScale);
1457
+ }
1458
+
1459
+ function datatypeValuesSame(a, b) {
1460
+ if (!a || !b || !a.comparable || !b.comparable) return false;
1461
+ if (a.family === 'langString' || b.family === 'langString') {
1462
+ return a.family === b.family && a.value === b.value && a.lang === b.lang;
1463
+ }
1464
+ if (a.family === 'string' && b.family === 'string') return a.value === b.value;
1465
+ if (a.family === 'boolean' && b.family === 'boolean') return a.value === b.value;
1466
+ if (a.family === 'anyURI' && b.family === 'anyURI') return a.value === b.value;
1467
+ if (a.family === 'binary' && b.family === 'binary') {
1468
+ if (a.bytes.length !== b.bytes.length) return false;
1469
+ for (let i = 0; i < a.bytes.length; i++) if (a.bytes[i] !== b.bytes[i]) return false;
1470
+ return true;
1471
+ }
1472
+ if (a.family === 'numeric' && b.family === 'numeric') {
1473
+ if (a.numericKind === 'decimalExact' && b.numericKind === 'decimalExact') return compareDecimalExactValues(a, b);
1474
+ const av = a.numericKind === 'decimalExact' ? Number(a.decimalNum) / 10 ** a.decimalScale : a.value;
1475
+ const bv = b.numericKind === 'decimalExact' ? Number(b.decimalNum) / 10 ** b.decimalScale : b.value;
1476
+ return !Number.isNaN(av) && !Number.isNaN(bv) && av === bv;
1477
+ }
1478
+ if (a.family === 'dateTime' && b.family === 'dateTime') {
1479
+ if (a.hasTimezone !== b.hasTimezone) return false;
1480
+ if (!a.hasTimezone) return a.localKey === b.localKey;
1481
+ if (a.totalSeconds !== b.totalSeconds) return false;
1482
+ const scale = Math.max(a.fracScale, b.fracScale);
1483
+ return a.fracNum * pow10n(scale - a.fracScale) === b.fracNum * pow10n(scale - b.fracScale);
1484
+ }
1485
+ return false;
1486
+ }
1487
+
1488
+ function datatypeValuesComparable(a, b) {
1489
+ if (!a || !b || !a.comparable || !b.comparable) return false;
1490
+ if (a.family === 'dateTime' && b.family === 'dateTime' && a.hasTimezone !== b.hasTimezone) return false;
1491
+ if (a.family === 'langString' || b.family === 'langString') return a.family === b.family;
1492
+ if (a.family === 'string' || b.family === 'string') return a.family === b.family;
1493
+ return a.family === b.family;
1494
+ }
1495
+
1496
+ function evalBindBuiltinObject(outTerm, valueTerm, subst) {
1497
+ if (outTerm instanceof Var) {
1498
+ const s2 = __cloneSubst(subst);
1499
+ s2[outTerm.name] = valueTerm;
1500
+ return [s2];
1501
+ }
1502
+ if (outTerm instanceof Blank) return [__cloneSubst(subst)];
1503
+ const s2 = unifyTerm(outTerm, valueTerm, subst);
1504
+ return s2 !== null ? [s2] : [];
1505
+ }
1506
+
1507
+ function evalDatatypeInspectionBuiltin(g, subst, kind) {
1508
+ if (!(g.s instanceof Literal)) return [];
1509
+
1510
+ let valueTerm;
1511
+ if (kind === 'datatype') {
1512
+ const dt = literalDatatypeIri(g.s);
1513
+ if (dt === null) return [];
1514
+ valueTerm = internIri(dt);
1515
+ } else if (kind === 'lexicalForm') {
1516
+ valueTerm = makeStringLiteral(literalLexicalValue(g.s.value));
1517
+ } else if (kind === 'language') {
1518
+ const tag = extractLiteralLanguageTag(g.s.value);
1519
+ if (tag === null) return [];
1520
+ valueTerm = makeStringLiteral(tag);
1521
+ } else if (kind === 'canonicalLiteral') {
1522
+ const info = parseDatatypeValue(g.s);
1523
+ if (!info) return [];
1524
+ valueTerm = info.canonicalLiteral;
1525
+ } else {
1526
+ return [];
1527
+ }
1528
+
1529
+ return evalBindBuiltinObject(g.o, valueTerm, subst);
1530
+ }
1531
+
1532
+ function evalDatatypeValidityBuiltin(g, subst, positive) {
1533
+ if (!(g.s instanceof Literal)) return [];
1534
+
1535
+ let dt = null;
1536
+ if (g.o instanceof Iri) dt = g.o.value;
1537
+ else if (g.o instanceof Var) dt = literalDatatypeIri(g.s);
1538
+ else return [];
1539
+
1540
+ if (dt === null || !isSupportedDatatypeIri(dt)) return [];
1541
+ const ok = parseDatatypeValueForDatatype(g.s, dt) !== null;
1542
+ if (positive && !ok) return [];
1543
+ if (!positive && ok) return [];
1544
+
1545
+ if (g.o instanceof Var) return evalBindBuiltinObject(g.o, internIri(dt), subst);
1546
+ return [__cloneSubst(subst)];
1547
+ }
1548
+
1549
+ function evalDatatypeValueComparisonBuiltin(g, subst, same) {
1550
+ if (!(g.s instanceof Literal) || !(g.o instanceof Literal)) return [];
1551
+ const a = parseDatatypeValue(g.s);
1552
+ const b = parseDatatypeValue(g.o);
1553
+ if (!datatypeValuesComparable(a, b)) return [];
1554
+ const eq = datatypeValuesSame(a, b);
1555
+ if (same ? eq : !eq) return [__cloneSubst(subst)];
1556
+ return [];
1557
+ }
1558
+
966
1559
  // ===========================================================================
967
1560
  // Math builtin helpers
968
1561
  // ===========================================================================
@@ -2111,6 +2704,19 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
2111
2704
  const registeredBuiltinResult = __evalRegisteredBuiltin(pv, g, subst, facts, backRules, depth, varGen, maxResults);
2112
2705
  if (registeredBuiltinResult !== null) return registeredBuiltinResult;
2113
2706
 
2707
+ // -----------------------------------------------------------------
2708
+ // 4.0 datatype: builtins (Eyeling datatype namespace)
2709
+ // -----------------------------------------------------------------
2710
+
2711
+ if (pv === DT_NS + 'datatype') return evalDatatypeInspectionBuiltin(g, subst, 'datatype');
2712
+ if (pv === DT_NS + 'lexicalForm') return evalDatatypeInspectionBuiltin(g, subst, 'lexicalForm');
2713
+ if (pv === DT_NS + 'language') return evalDatatypeInspectionBuiltin(g, subst, 'language');
2714
+ if (pv === DT_NS + 'canonicalLiteral') return evalDatatypeInspectionBuiltin(g, subst, 'canonicalLiteral');
2715
+ if (pv === DT_NS + 'validForDatatype') return evalDatatypeValidityBuiltin(g, subst, true);
2716
+ if (pv === DT_NS + 'invalidForDatatype') return evalDatatypeValidityBuiltin(g, subst, false);
2717
+ if (pv === DT_NS + 'sameValueAs') return evalDatatypeValueComparisonBuiltin(g, subst, true);
2718
+ if (pv === DT_NS + 'differentValueFrom') return evalDatatypeValueComparisonBuiltin(g, subst, false);
2719
+
2114
2720
  // -----------------------------------------------------------------
2115
2721
  // 4.1 crypto: builtins
2116
2722
  // -----------------------------------------------------------------
@@ -4436,6 +5042,7 @@ function isBuiltinPred(p) {
4436
5042
  v.startsWith(MATH_NS) ||
4437
5043
  v.startsWith(LOG_NS) ||
4438
5044
  v.startsWith(STRING_NS) ||
5045
+ v.startsWith(DT_NS) ||
4439
5046
  v.startsWith(TIME_NS) ||
4440
5047
  v.startsWith(LIST_NS)
4441
5048
  );
@@ -14200,6 +14807,7 @@ const TIME_NS = 'http://www.w3.org/2000/10/swap/time#';
14200
14807
  const LIST_NS = 'http://www.w3.org/2000/10/swap/list#';
14201
14808
  const LOG_NS = 'http://www.w3.org/2000/10/swap/log#';
14202
14809
  const STRING_NS = 'http://www.w3.org/2000/10/swap/string#';
14810
+ const DT_NS = 'https://eyereasoner.github.io/eyeling/datatype#';
14203
14811
  const SKOLEM_NS = 'https://eyereasoner.github.io/.well-known/genid/';
14204
14812
  const RDF_JSON_DT = RDF_NS + 'JSON';
14205
14813
 
@@ -14718,6 +15326,7 @@ class PrefixEnv {
14718
15326
  m['string'] = STRING_NS;
14719
15327
  m['list'] = LIST_NS;
14720
15328
  m['time'] = TIME_NS;
15329
+ m['dt'] = DT_NS;
14721
15330
  m['genid'] = SKOLEM_NS;
14722
15331
  m[''] = ''; // empty prefix default namespace
14723
15332
  return new PrefixEnv(m, ''); // base IRI starts empty
@@ -14902,6 +15511,7 @@ module.exports = {
14902
15511
  LIST_NS,
14903
15512
  LOG_NS,
14904
15513
  STRING_NS,
15514
+ DT_NS,
14905
15515
  SKOLEM_NS,
14906
15516
  RDF_JSON_DT,
14907
15517
  resolveIriRef,