@stately-cloud/schema
Advanced tools
Comparing version 0.16.0 to 0.17.0
#!/usr/bin/env node | ||
import { build } from "./driver.js"; | ||
try { | ||
await build(process.argv[2], process.argv[3]); | ||
const [_nodePath, _scriptPath, inputFile, outputFileDescriptorName, migrationsFromSchemaVersionStr = undefined,] = process.argv; | ||
const migrationsFromSchemaVersion = migrationsFromSchemaVersionStr | ||
? BigInt(migrationsFromSchemaVersionStr) | ||
: undefined; | ||
await build(inputFile, outputFileDescriptorName, migrationsFromSchemaVersion); | ||
} | ||
@@ -6,0 +10,0 @@ catch (e) { |
@@ -14,2 +14,2 @@ export declare const getPackageName: () => string; | ||
*/ | ||
export declare function build(inputPath: string, fileName: string): Promise<void>; | ||
export declare function build(inputPath: string, fileName: string, migrationsFromSchemaVersion?: bigint): Promise<void>; |
import { create, toBinary } from "@bufbuild/protobuf"; | ||
import { readFile } from "node:fs/promises"; | ||
import path from "node:path"; | ||
@@ -6,3 +7,2 @@ import process from "node:process"; | ||
import ts from "typescript"; | ||
import packageJson from "../package.json" with { type: "json" }; | ||
import { buildSourceCodeInfo, extractCommentBindings } from "./comments.js"; | ||
@@ -27,3 +27,3 @@ import { SchemaError } from "./errors.js"; | ||
*/ | ||
export async function build(inputPath, fileName) { | ||
export async function build(inputPath, fileName, migrationsFromSchemaVersion) { | ||
const fullInputPath = path.resolve(inputPath); | ||
@@ -131,10 +131,12 @@ // We need to be able to generate unique package names for each schema file so that | ||
} | ||
// Collect and expand all the migrations from the latest version. In the | ||
// future I suppose we can pass a specific from-version argument. | ||
const latestMigrations = getLatestMigrations(deferredMigrations); | ||
const migrations = latestMigrations.map((migration) => migration.build()); | ||
// Collect and expand all the migrations from selected schema version. | ||
// If an old version of the CLI has not passed migrationsFromSchemaVersion then just | ||
// return the migrations with the highest version number. | ||
const migrationsFromTargetVersions = migrationsFromSchemaVersion !== undefined | ||
? deferredMigrations.filter((m) => m.fromSchemaVersion === migrationsFromSchemaVersion) | ||
: getLatestMigrations(deferredMigrations); | ||
const migrations = migrationsFromTargetVersions.map((migration) => migration.build()); | ||
const output = create(DSLResponseSchema, { | ||
fileDescriptor: fd, | ||
migrations: migrations, | ||
dslVersion: packageJson.version, | ||
}); | ||
@@ -165,2 +167,4 @@ respond(output); | ||
async function respond(output) { | ||
// Get the version of the DSL from the package.json | ||
const packageJson = JSON.parse((await readFile(new URL("../package.json", import.meta.url))).toString("utf8")); | ||
output.dslVersion = packageJson.version; | ||
@@ -167,0 +171,0 @@ // Export the DSLResponse to the output path |
@@ -5,8 +5,2 @@ import { SchemaType } from "./types.js"; | ||
/** | ||
* The default value of the enum. This must be one of the values. If a default | ||
* is specified then the enum starts at 0 (which is the default if the field | ||
* is not set). If no default is set, the enum values start at 1, and 0 is | ||
* reserved for a special "unspecified" value. | ||
*/ | ||
/** | ||
* Whether this enum as a whole is deprecated. This will be marked in | ||
@@ -31,7 +25,5 @@ * generated code. | ||
* export const Rank = enumType("Rank", { | ||
* Beginner: 1, | ||
* Intermediate: 2, | ||
* Expert: 3, | ||
* }, { | ||
* default: "Beginner", | ||
* Beginner: 0, // This is the default value, but also the zero value | ||
* Intermediate: 1, | ||
* Expert: 2, | ||
* }); | ||
@@ -42,3 +34,5 @@ * export const Player = itemType("Player", { | ||
* type: Rank, | ||
* fieldNum: 1, | ||
* // If the field was required (the default) you couldn't use the | ||
* // zero (Beginner) value | ||
* required: false, | ||
* }, | ||
@@ -45,0 +39,0 @@ * }, |
@@ -14,7 +14,5 @@ import { create } from "@bufbuild/protobuf"; | ||
* export const Rank = enumType("Rank", { | ||
* Beginner: 1, | ||
* Intermediate: 2, | ||
* Expert: 3, | ||
* }, { | ||
* default: "Beginner", | ||
* Beginner: 0, // This is the default value, but also the zero value | ||
* Intermediate: 1, | ||
* Expert: 2, | ||
* }); | ||
@@ -25,3 +23,5 @@ * export const Player = itemType("Player", { | ||
* type: Rank, | ||
* fieldNum: 1, | ||
* // If the field was required (the default) you couldn't use the | ||
* // zero (Beginner) value | ||
* required: false, | ||
* }, | ||
@@ -76,3 +76,2 @@ * }, | ||
parentType: enumDescriptor, | ||
// default: enumDescriptor.value.find((v) => v.name === config.default)?.number, | ||
deprecated: config.deprecated, | ||
@@ -79,0 +78,0 @@ }; |
@@ -49,8 +49,2 @@ import { FieldDescriptorProto } from "@bufbuild/protobuf/wkt"; | ||
/** | ||
* A number to uniquely identify this field in its enclosing object. This is | ||
* used to compactly encode the field without having to write out its name, | ||
* which as a side effect makes name changes backwards compatible. | ||
*/ | ||
fieldNum: number; | ||
/** | ||
* Whether this field is deprecated. This will be marked in generated code. | ||
@@ -57,0 +51,0 @@ */ |
@@ -41,13 +41,9 @@ import { create, isMessage, setExtension } from "@bufbuild/protobuf"; | ||
export function field(fieldName, fieldConfig) { | ||
if (fieldConfig.fieldNum <= 0 || fieldConfig.fieldNum !== Math.floor(fieldConfig.fieldNum)) { | ||
throw new Error(`Field number ${fieldConfig.fieldNum} for field ${fieldName} must be a positive nonzero integer`); | ||
} | ||
const field = create(FieldDescriptorProtoSchema, { | ||
name: fieldName, | ||
jsonName: fieldName, | ||
number: fieldConfig.fieldNum, | ||
}); | ||
const type = resolveDeferred(fieldConfig.type); | ||
if (isItemType(type)) { | ||
throw new Error(`Item types should not be used as fields - consider storing a keyPath of ${fieldConfig.type.name}) instead`); | ||
throw new Error(`Item types should not be used as fields - consider storing just the key path string pointing to ${type.name} instead, or declare it using objectType instead of itemType.`); | ||
} | ||
@@ -54,0 +50,0 @@ const typeInfo = resolveType(type); |
@@ -38,3 +38,2 @@ import { Field } from "./fields.js"; | ||
* type: string, | ||
* fieldNum: 1, | ||
* } | ||
@@ -49,4 +48,2 @@ * }; | ||
* type: string, | ||
* // You still need to make sure field numbers don't collide. | ||
* fieldNum: 2, | ||
* }, | ||
@@ -79,7 +76,2 @@ * }, | ||
/** | ||
* Field numbers that are no longer used but used to be. When removing a | ||
* field, the field number should be added to this list to prevent re-use. | ||
*/ | ||
reservedFieldNums?: number[]; | ||
/** | ||
* Field names that are no longer used but used to be. When removing or | ||
@@ -120,3 +112,3 @@ * renaming a field, the old field name should be added to this list to | ||
export declare function itemType(name: string, itemTypeConfig: ItemTypeConfig): SchemaType; | ||
export type ObjectTypeConfig = Pick<ItemTypeConfig, "fields" | "reservedFieldNums" | "reservedNames" | "deprecated">; | ||
export type ObjectTypeConfig = Pick<ItemTypeConfig, "fields" | "reservedNames" | "deprecated">; | ||
/** | ||
@@ -123,0 +115,0 @@ * An object type is very much like an item type, but it can only be used as a |
@@ -6,3 +6,3 @@ import { create, setExtension } from "@bufbuild/protobuf"; | ||
import { resolveDeferred, resolvePlural } from "./type-util.js"; | ||
import { DescriptorProto_ReservedRangeSchema, DescriptorProtoSchema, MessageOptionsSchema, } from "@bufbuild/protobuf/wkt"; | ||
import { DescriptorProtoSchema, MessageOptionsSchema, } from "@bufbuild/protobuf/wkt"; | ||
import { getRegisteredType, registerType, TypeDefinitionError } from "./type-registry.js"; | ||
@@ -75,3 +75,3 @@ import { validateName } from "./validate.js"; | ||
message.field = populateFields(name, itemTypeConfig.fields); | ||
// Reserved field numbers / names | ||
// Reserved field names | ||
populateReserved(message, itemTypeConfig); | ||
@@ -99,3 +99,3 @@ // addSyntheticOneofs(message); | ||
message.field = populateFields(name, itemTypeConfig.fields); | ||
// Reserved field numbers / names | ||
// Reserved field names | ||
populateReserved(message, itemTypeConfig); | ||
@@ -110,3 +110,2 @@ // addSyntheticOneofs(message); | ||
function populateFields(typeName, fieldConfigs) { | ||
const usedNums = new Set(); | ||
const fields = []; | ||
@@ -120,9 +119,2 @@ for (const [fieldName, fieldConfig] of Object.entries(fieldConfigs)) { | ||
const fieldProto = field(fieldName, resolveDeferred(fieldConfig)); | ||
if (fieldProto.number === undefined) { | ||
throw new Error(`Field number must be specified for ${typeName}.${fieldName}`); | ||
} | ||
if (usedNums.has(fieldProto.number)) { | ||
throw new Error(`Field number ${fieldProto.number} used by ${typeName}.${fieldName} is already used by another field.`); | ||
} | ||
usedNums.add(fieldProto.number); | ||
fields.push(fieldProto); | ||
@@ -158,14 +150,2 @@ } | ||
} | ||
for (const fieldNum of itemTypeConfig.reservedFieldNums?.sort() ?? []) { | ||
if (message.field.some((field) => field.number === fieldNum)) { | ||
throw new Error(`Field number ${fieldNum} is reserved in ${message.name} but is also used as a field number.`); | ||
} | ||
const lastRange = message.reservedRange[message.reservedRange.length - 1]; | ||
if (lastRange && lastRange.end === fieldNum) { | ||
lastRange.end++; | ||
} | ||
else { | ||
message.reservedRange.push(create(DescriptorProto_ReservedRangeSchema, { start: fieldNum, end: fieldNum + 1 })); | ||
} | ||
} | ||
} | ||
@@ -172,0 +152,0 @@ // /** |
@@ -88,3 +88,3 @@ import { deepEqual } from "fast-equals"; | ||
* A sort of lightweight comparison of fields - they're the same if there are | ||
* exactly the same names and field numbers. We can't compare the types directly | ||
* exactly the same names. We can't compare the types directly | ||
* because they may be functions. | ||
@@ -97,3 +97,3 @@ */ | ||
for (const key in a) { | ||
if (!b[key] || a[key].fieldNum !== b[key].fieldNum) { | ||
if (!b[key]) { | ||
return false; | ||
@@ -100,0 +100,0 @@ } |
{ | ||
"name": "@stately-cloud/schema", | ||
"version": "0.16.0", | ||
"version": "0.17.0", | ||
"engines": { | ||
"node": ">=18.20" | ||
}, | ||
"author": "Stately Cloud <support@stately.cloud> (https://stately.cloud/)", | ||
@@ -5,0 +8,0 @@ "description": "Schema language for StatelyDB", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
293118
3790