big updat:

- update dependencies
- add webp support and webp conversion for profile images
This commit is contained in:
olcxja 2026-07-02 22:40:46 +02:00
commit 95aaaa69ea
244 changed files with 121382 additions and 86 deletions

3
node_modules/js-binary-schema-parser/.babelrc generated vendored Normal file
View file

@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}

7
node_modules/js-binary-schema-parser/.prettierrc generated vendored Normal file
View file

@ -0,0 +1,7 @@
{
"singleQuote": true,
"write": true,
"semi": false,
"tabWidth": 2,
"printWidth": 80
}

22
node_modules/js-binary-schema-parser/LICENSE generated vendored Normal file
View file

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Matt Way
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

83
node_modules/js-binary-schema-parser/README.md generated vendored Normal file
View file

@ -0,0 +1,83 @@
# js Binary Schema Parser
Parse binary files in javascript using a schema to convert to plain objects.
Years ago I needed to parse GIF images for our **[Ruffle][1]** messaging app. While this readme describes how to parse binary files in general, our _[GIF Parser][2]_ library exhibits a full use of this library (including a _[demo][2]_). I suggest looking at the other library for a quick understanding.
Basically, you provide a schema object and some data, and it will step through the binary data, and convert it into the object defined by your schema. Included in this library is a parser for the `Uint8TypedArray`, but it is easy to add them for your own types if necessary. It can parse bytes, arrays, chunks, conditionals, loops, etc.
### How to Use
_Installation:_
npm install js-binary-schema-parser
_Create a schema and parse a file:_
import { parse, conditional } from 'js-binary-schema-parser'
import { buildStream, readByte } from 'js-binary-schema-parser/lib/parsers/uint8'
const schema = [
// part definitions...
{ someKey: readByte() }
];
// get the input file data
const data = new Uint8Array(fileArrayBuffer);
// create a stream object and parse it
const parsedObject = parse(buildStream(data), schema)
### Schemas
So far in this library there is only one built in schema, which is for the GIF format. You can import included schemas like:
import GIF from 'js-binary-schema-parser/lib/schemas/gif'
Schemas are an array of _parts_, which are objects containing a single key label, and the parser to use at that point in time. This format was chosen to ensure parse ordering was consistent. _Parts_ can also contain other parts internally, and include syntax for loops, and conditionals. You can also include your own custom functions for parsing, providing direct access to the given data stream. Below is an example of a schema using the `Uint8TypedArray` parser provided to parse the GIF format header. You can also see a full example [here][2] of parsing entire GIF files.
### Example
var gifSchema = [
{
label: 'header', // gif header
parts: [
{ label: 'signature', parser: Parsers.readString(3) },
{ label: 'version', parser: Parsers.readString(3) }
]
},{
label: 'lsd', // local screen descriptor
parts: [
{ label: 'width', parser: Parsers.readUnsigned(true) },
{ label: 'height', parser: Parsers.readUnsigned(true) },
{ label: 'gct', bits: {
exists: { index: 0 },
resolution: { index: 1, length: 3 },
sort: { index: 4 },
size: { index: 5, length: 3 }
}},
{ label: 'backgroundColorIndex', parser: Parsers.readByte() },
{ label: 'pixelAspectRatio', parser: Parsers.readByte() }
]
}
];
### Why this parser?
There are other good parsers around, like [jBinary][4], but we weren't a fan of relying on object key ordering, and defining parser types as strings. This one is also extremely small, and easily exstensible in any way you want.
### Demo
You can see a full demo **[here][2]** which uses this lib to parse GIF files for manipulation.
### Who are we?
[Matt Way][3] & [Nick Drewe][5]
[Wethrift.com][6]
[1]: https://www.producthunt.com/posts/ruffle
[2]: https://github.com/matt-way/gifuct-js
[3]: https://twitter.com/_MattWay
[4]: https://github.com/jDataView/jBinary
[5]: https://twitter.com/nickdrewe
[6]: https://wethrift.com

BIN
node_modules/js-binary-schema-parser/example/box.gif generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 B

BIN
node_modules/js-binary-schema-parser/example/dog.gif generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
node_modules/js-binary-schema-parser/example/horses.gif generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

10
node_modules/js-binary-schema-parser/example/index.js generated vendored Normal file
View file

@ -0,0 +1,10 @@
import fs from 'fs'
import { parse } from '../src'
import { buildStream } from '../src/parsers/uint8'
import { GIF } from '../src/schemas'
debugger
const data = fs.readFileSync('./example/dog.gif')
const result = parse(buildStream(new Uint8Array(data)), GIF)
console.log(result)

66
node_modules/js-binary-schema-parser/lib/index.js generated vendored Normal file
View file

@ -0,0 +1,66 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.loop = exports.conditional = exports.parse = void 0;
var parse = function parse(stream, schema) {
var result = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var parent = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : result;
if (Array.isArray(schema)) {
schema.forEach(function (partSchema) {
return parse(stream, partSchema, result, parent);
});
} else if (typeof schema === 'function') {
schema(stream, result, parent, parse);
} else {
var key = Object.keys(schema)[0];
if (Array.isArray(schema[key])) {
parent[key] = {};
parse(stream, schema[key], result, parent[key]);
} else {
parent[key] = schema[key](stream, result, parent, parse);
}
}
return result;
};
exports.parse = parse;
var conditional = function conditional(schema, conditionFunc) {
return function (stream, result, parent, parse) {
if (conditionFunc(stream, result, parent)) {
parse(stream, schema, result, parent);
}
};
};
exports.conditional = conditional;
var loop = function loop(schema, continueFunc) {
return function (stream, result, parent, parse) {
var arr = [];
var lastStreamPos = stream.pos;
while (continueFunc(stream, result, parent)) {
var newParent = {};
parse(stream, schema, result, newParent); // cases when whole file is parsed but no termination is there and stream position is not getting updated as well
// it falls into infinite recursion, null check to avoid the same
if (stream.pos === lastStreamPos) {
break;
}
lastStreamPos = stream.pos;
arr.push(newParent);
}
return arr;
};
};
exports.loop = loop;

View file

@ -0,0 +1,122 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.readBits = exports.readArray = exports.readUnsigned = exports.readString = exports.peekBytes = exports.readBytes = exports.peekByte = exports.readByte = exports.buildStream = void 0;
// Default stream and parsers for Uint8TypedArray data type
var buildStream = function buildStream(uint8Data) {
return {
data: uint8Data,
pos: 0
};
};
exports.buildStream = buildStream;
var readByte = function readByte() {
return function (stream) {
return stream.data[stream.pos++];
};
};
exports.readByte = readByte;
var peekByte = function peekByte() {
var offset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
return function (stream) {
return stream.data[stream.pos + offset];
};
};
exports.peekByte = peekByte;
var readBytes = function readBytes(length) {
return function (stream) {
return stream.data.subarray(stream.pos, stream.pos += length);
};
};
exports.readBytes = readBytes;
var peekBytes = function peekBytes(length) {
return function (stream) {
return stream.data.subarray(stream.pos, stream.pos + length);
};
};
exports.peekBytes = peekBytes;
var readString = function readString(length) {
return function (stream) {
return Array.from(readBytes(length)(stream)).map(function (value) {
return String.fromCharCode(value);
}).join('');
};
};
exports.readString = readString;
var readUnsigned = function readUnsigned(littleEndian) {
return function (stream) {
var bytes = readBytes(2)(stream);
return littleEndian ? (bytes[1] << 8) + bytes[0] : (bytes[0] << 8) + bytes[1];
};
};
exports.readUnsigned = readUnsigned;
var readArray = function readArray(byteSize, totalOrFunc) {
return function (stream, result, parent) {
var total = typeof totalOrFunc === 'function' ? totalOrFunc(stream, result, parent) : totalOrFunc;
var parser = readBytes(byteSize);
var arr = new Array(total);
for (var i = 0; i < total; i++) {
arr[i] = parser(stream);
}
return arr;
};
};
exports.readArray = readArray;
var subBitsTotal = function subBitsTotal(bits, startIndex, length) {
var result = 0;
for (var i = 0; i < length; i++) {
result += bits[startIndex + i] && Math.pow(2, length - i - 1);
}
return result;
};
var readBits = function readBits(schema) {
return function (stream) {
var _byte = readByte()(stream); // convert the byte to bit array
var bits = new Array(8);
for (var i = 0; i < 8; i++) {
bits[7 - i] = !!(_byte & 1 << i);
} // convert the bit array to values based on the schema
return Object.keys(schema).reduce(function (res, key) {
var def = schema[key];
if (def.length) {
res[key] = subBitsTotal(bits, def.index, def.length);
} else {
res[key] = bits[def.index];
}
return res;
}, {});
};
};
exports.readBits = readBits;

219
node_modules/js-binary-schema-parser/lib/schemas/gif.js generated vendored Normal file
View file

@ -0,0 +1,219 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _ = require("../");
var _uint = require("../parsers/uint8");
// a set of 0x00 terminated subblocks
var subBlocksSchema = {
blocks: function blocks(stream) {
var terminator = 0x00;
var chunks = [];
var streamSize = stream.data.length;
var total = 0;
for (var size = (0, _uint.readByte)()(stream); size !== terminator; size = (0, _uint.readByte)()(stream)) {
// size becomes undefined for some case when file is corrupted and terminator is not proper
// null check to avoid recursion
if (!size) break; // catch corrupted files with no terminator
if (stream.pos + size >= streamSize) {
var availableSize = streamSize - stream.pos;
chunks.push((0, _uint.readBytes)(availableSize)(stream));
total += availableSize;
break;
}
chunks.push((0, _uint.readBytes)(size)(stream));
total += size;
}
var result = new Uint8Array(total);
var offset = 0;
for (var i = 0; i < chunks.length; i++) {
result.set(chunks[i], offset);
offset += chunks[i].length;
}
return result;
}
}; // global control extension
var gceSchema = (0, _.conditional)({
gce: [{
codes: (0, _uint.readBytes)(2)
}, {
byteSize: (0, _uint.readByte)()
}, {
extras: (0, _uint.readBits)({
future: {
index: 0,
length: 3
},
disposal: {
index: 3,
length: 3
},
userInput: {
index: 6
},
transparentColorGiven: {
index: 7
}
})
}, {
delay: (0, _uint.readUnsigned)(true)
}, {
transparentColorIndex: (0, _uint.readByte)()
}, {
terminator: (0, _uint.readByte)()
}]
}, function (stream) {
var codes = (0, _uint.peekBytes)(2)(stream);
return codes[0] === 0x21 && codes[1] === 0xf9;
}); // image pipeline block
var imageSchema = (0, _.conditional)({
image: [{
code: (0, _uint.readByte)()
}, {
descriptor: [{
left: (0, _uint.readUnsigned)(true)
}, {
top: (0, _uint.readUnsigned)(true)
}, {
width: (0, _uint.readUnsigned)(true)
}, {
height: (0, _uint.readUnsigned)(true)
}, {
lct: (0, _uint.readBits)({
exists: {
index: 0
},
interlaced: {
index: 1
},
sort: {
index: 2
},
future: {
index: 3,
length: 2
},
size: {
index: 5,
length: 3
}
})
}]
}, (0, _.conditional)({
lct: (0, _uint.readArray)(3, function (stream, result, parent) {
return Math.pow(2, parent.descriptor.lct.size + 1);
})
}, function (stream, result, parent) {
return parent.descriptor.lct.exists;
}), {
data: [{
minCodeSize: (0, _uint.readByte)()
}, subBlocksSchema]
}]
}, function (stream) {
return (0, _uint.peekByte)()(stream) === 0x2c;
}); // plain text block
var textSchema = (0, _.conditional)({
text: [{
codes: (0, _uint.readBytes)(2)
}, {
blockSize: (0, _uint.readByte)()
}, {
preData: function preData(stream, result, parent) {
return (0, _uint.readBytes)(parent.text.blockSize)(stream);
}
}, subBlocksSchema]
}, function (stream) {
var codes = (0, _uint.peekBytes)(2)(stream);
return codes[0] === 0x21 && codes[1] === 0x01;
}); // application block
var applicationSchema = (0, _.conditional)({
application: [{
codes: (0, _uint.readBytes)(2)
}, {
blockSize: (0, _uint.readByte)()
}, {
id: function id(stream, result, parent) {
return (0, _uint.readString)(parent.blockSize)(stream);
}
}, subBlocksSchema]
}, function (stream) {
var codes = (0, _uint.peekBytes)(2)(stream);
return codes[0] === 0x21 && codes[1] === 0xff;
}); // comment block
var commentSchema = (0, _.conditional)({
comment: [{
codes: (0, _uint.readBytes)(2)
}, subBlocksSchema]
}, function (stream) {
var codes = (0, _uint.peekBytes)(2)(stream);
return codes[0] === 0x21 && codes[1] === 0xfe;
});
var schema = [{
header: [{
signature: (0, _uint.readString)(3)
}, {
version: (0, _uint.readString)(3)
}]
}, {
lsd: [{
width: (0, _uint.readUnsigned)(true)
}, {
height: (0, _uint.readUnsigned)(true)
}, {
gct: (0, _uint.readBits)({
exists: {
index: 0
},
resolution: {
index: 1,
length: 3
},
sort: {
index: 4
},
size: {
index: 5,
length: 3
}
})
}, {
backgroundColorIndex: (0, _uint.readByte)()
}, {
pixelAspectRatio: (0, _uint.readByte)()
}]
}, (0, _.conditional)({
gct: (0, _uint.readArray)(3, function (stream, result) {
return Math.pow(2, result.lsd.gct.size + 1);
})
}, function (stream, result) {
return result.lsd.gct.exists;
}), // content frames
{
frames: (0, _.loop)([gceSchema, applicationSchema, commentSchema, imageSchema, textSchema], function (stream) {
var nextCode = (0, _uint.peekByte)()(stream); // rather than check for a terminator, we should check for the existence
// of an ext or image block to avoid infinite loops
//var terminator = 0x3B;
//return nextCode !== terminator;
return nextCode === 0x21 || nextCode === 0x2c;
})
}];
var _default = schema;
exports["default"] = _default;

35
node_modules/js-binary-schema-parser/package.json generated vendored Normal file
View file

@ -0,0 +1,35 @@
{
"name": "js-binary-schema-parser",
"version": "2.0.3",
"description": "Parse binary files with a schema into nicely readable objects",
"dependencies": {},
"devDependencies": {
"@babel/cli": "^7.8.4",
"@babel/core": "^7.8.4",
"@babel/node": "^7.8.4",
"@babel/preset-env": "^7.8.4"
},
"main": "lib/index.js",
"scripts": {
"example": "babel-node ./example/index.js",
"example-debug": "babel-node --inspect-brk ./example/index.js",
"build": "babel src --out-dir lib"
},
"repository": {
"type": "git",
"url": "https://github.com/matt-way/jsBinarySchemaParser.git"
},
"keywords": [
"javascript",
"binary",
"file",
"parser",
"schema"
],
"author": "Matt Way",
"license": "MIT",
"bugs": {
"url": "https://github.com/matt-way/jsBinarySchemaParser/issues"
},
"homepage": "https://github.com/matt-way/jsBinarySchemaParser"
}

49
node_modules/js-binary-schema-parser/src/index.js generated vendored Normal file
View file

@ -0,0 +1,49 @@
export const parse = (stream, schema, result = {}, parent = result) => {
if (Array.isArray(schema)) {
schema.forEach(partSchema => parse(stream, partSchema, result, parent))
} else if (typeof schema === 'function') {
schema(stream, result, parent, parse)
} else {
const key = Object.keys(schema)[0]
if (Array.isArray(schema[key])) {
parent[key] = {}
parse(stream, schema[key], result, parent[key])
} else {
parent[key] = schema[key](stream, result, parent, parse)
}
}
return result
}
export const conditional = (schema, conditionFunc) => (
stream,
result,
parent,
parse
) => {
if (conditionFunc(stream, result, parent)) {
parse(stream, schema, result, parent)
}
}
export const loop = (schema, continueFunc) => (
stream,
result,
parent,
parse
) => {
const arr = []
let lastStreamPos = stream.pos;
while (continueFunc(stream, result, parent)) {
const newParent = {}
parse(stream, schema, result, newParent)
// cases when whole file is parsed but no termination is there and stream position is not getting updated as well
// it falls into infinite recursion, null check to avoid the same
if(stream.pos === lastStreamPos) {
break
}
lastStreamPos = stream.pos
arr.push(newParent)
}
return arr
}

View file

@ -0,0 +1,78 @@
// Default stream and parsers for Uint8TypedArray data type
export const buildStream = uint8Data => ({
data: uint8Data,
pos: 0
})
export const readByte = () => stream => {
return stream.data[stream.pos++]
}
export const peekByte = (offset = 0) => stream => {
return stream.data[stream.pos + offset]
}
export const readBytes = length => stream => {
return stream.data.subarray(stream.pos, (stream.pos += length))
}
export const peekBytes = length => stream => {
return stream.data.subarray(stream.pos, stream.pos + length)
}
export const readString = length => stream => {
return Array.from(readBytes(length)(stream))
.map(value => String.fromCharCode(value))
.join('')
}
export const readUnsigned = littleEndian => stream => {
const bytes = readBytes(2)(stream)
return littleEndian ? (bytes[1] << 8) + bytes[0] : (bytes[0] << 8) + bytes[1]
}
export const readArray = (byteSize, totalOrFunc) => (
stream,
result,
parent
) => {
const total =
typeof totalOrFunc === 'function'
? totalOrFunc(stream, result, parent)
: totalOrFunc
const parser = readBytes(byteSize)
const arr = new Array(total)
for (var i = 0; i < total; i++) {
arr[i] = parser(stream)
}
return arr
}
const subBitsTotal = (bits, startIndex, length) => {
var result = 0
for (var i = 0; i < length; i++) {
result += bits[startIndex + i] && 2 ** (length - i - 1)
}
return result
}
export const readBits = schema => stream => {
const byte = readByte()(stream)
// convert the byte to bit array
const bits = new Array(8)
for (var i = 0; i < 8; i++) {
bits[7 - i] = !!(byte & (1 << i))
}
// convert the bit array to values based on the schema
return Object.keys(schema).reduce((res, key) => {
const def = schema[key]
if (def.length) {
res[key] = subBitsTotal(bits, def.index, def.length)
} else {
res[key] = bits[def.index]
}
return res
}, {})
}

201
node_modules/js-binary-schema-parser/src/schemas/gif.js generated vendored Normal file
View file

@ -0,0 +1,201 @@
import { conditional, loop } from '../'
import {
readByte,
peekByte,
readBytes,
peekBytes,
readString,
readUnsigned,
readArray,
readBits,
} from '../parsers/uint8'
// a set of 0x00 terminated subblocks
var subBlocksSchema = {
blocks: (stream) => {
const terminator = 0x00
const chunks = []
const streamSize = stream.data.length
var total = 0
for (
var size = readByte()(stream);
size !== terminator;
size = readByte()(stream)
) {
// size becomes undefined for some case when file is corrupted and terminator is not proper
// null check to avoid recursion
if(!size) break;
// catch corrupted files with no terminator
if (stream.pos + size >= streamSize) {
const availableSize = streamSize - stream.pos
chunks.push(readBytes(availableSize)(stream))
total += availableSize
break
}
chunks.push(readBytes(size)(stream))
total += size
}
const result = new Uint8Array(total)
var offset = 0
for (var i = 0; i < chunks.length; i++) {
result.set(chunks[i], offset)
offset += chunks[i].length
}
return result
},
}
// global control extension
const gceSchema = conditional(
{
gce: [
{ codes: readBytes(2) },
{ byteSize: readByte() },
{
extras: readBits({
future: { index: 0, length: 3 },
disposal: { index: 3, length: 3 },
userInput: { index: 6 },
transparentColorGiven: { index: 7 },
}),
},
{ delay: readUnsigned(true) },
{ transparentColorIndex: readByte() },
{ terminator: readByte() },
],
},
(stream) => {
var codes = peekBytes(2)(stream)
return codes[0] === 0x21 && codes[1] === 0xf9
}
)
// image pipeline block
const imageSchema = conditional(
{
image: [
{ code: readByte() },
{
descriptor: [
{ left: readUnsigned(true) },
{ top: readUnsigned(true) },
{ width: readUnsigned(true) },
{ height: readUnsigned(true) },
{
lct: readBits({
exists: { index: 0 },
interlaced: { index: 1 },
sort: { index: 2 },
future: { index: 3, length: 2 },
size: { index: 5, length: 3 },
}),
},
],
},
conditional(
{
lct: readArray(3, (stream, result, parent) => {
return Math.pow(2, parent.descriptor.lct.size + 1)
}),
},
(stream, result, parent) => {
return parent.descriptor.lct.exists
}
),
{ data: [{ minCodeSize: readByte() }, subBlocksSchema] },
],
},
(stream) => {
return peekByte()(stream) === 0x2c
}
)
// plain text block
const textSchema = conditional(
{
text: [
{ codes: readBytes(2) },
{ blockSize: readByte() },
{
preData: (stream, result, parent) =>
readBytes(parent.text.blockSize)(stream),
},
subBlocksSchema,
],
},
(stream) => {
var codes = peekBytes(2)(stream)
return codes[0] === 0x21 && codes[1] === 0x01
}
)
// application block
const applicationSchema = conditional(
{
application: [
{ codes: readBytes(2) },
{ blockSize: readByte() },
{ id: (stream, result, parent) => readString(parent.blockSize)(stream) },
subBlocksSchema,
],
},
(stream) => {
var codes = peekBytes(2)(stream)
return codes[0] === 0x21 && codes[1] === 0xff
}
)
// comment block
const commentSchema = conditional(
{
comment: [{ codes: readBytes(2) }, subBlocksSchema],
},
(stream) => {
var codes = peekBytes(2)(stream)
return codes[0] === 0x21 && codes[1] === 0xfe
}
)
const schema = [
{ header: [{ signature: readString(3) }, { version: readString(3) }] },
{
lsd: [
{ width: readUnsigned(true) },
{ height: readUnsigned(true) },
{
gct: readBits({
exists: { index: 0 },
resolution: { index: 1, length: 3 },
sort: { index: 4 },
size: { index: 5, length: 3 },
}),
},
{ backgroundColorIndex: readByte() },
{ pixelAspectRatio: readByte() },
],
},
conditional(
{
gct: readArray(3, (stream, result) =>
Math.pow(2, result.lsd.gct.size + 1)
),
},
(stream, result) => result.lsd.gct.exists
),
// content frames
{
frames: loop(
[gceSchema, applicationSchema, commentSchema, imageSchema, textSchema],
(stream) => {
var nextCode = peekByte()(stream)
// rather than check for a terminator, we should check for the existence
// of an ext or image block to avoid infinite loops
//var terminator = 0x3B;
//return nextCode !== terminator;
return nextCode === 0x21 || nextCode === 0x2c
}
),
},
]
export default schema