diff --git a/dist/chunk-DRJX3OJG.js b/dist/chunk-DRJX3OJG.js index 6e5aedb..ce4bdb4 100644 --- a/dist/chunk-DRJX3OJG.js +++ b/dist/chunk-DRJX3OJG.js @@ -86,6 +86,18 @@ var BaseSchema = class { or(other) { return k.union([this, other]); } + optional() { + return new KOptional(this); + } + nullable() { + return this.or(k.null()); + } + nullish() { + return this.nullable().optional(); + } + default(defaultValue) { + return new KDefault(this, defaultValue); + } example(example) { if (example === void 0) { return this.def.example; @@ -99,6 +111,74 @@ var BaseSchema = class { return this.clone({ description }); } }; +var KOptional = class extends BaseSchema { + schema; + constructor(schema) { + super({}); + this.schema = schema; + } + serialize(value) { + if (value === void 0) { + return void 0; + } + return this.schema.serialize(value); + } + toOpenAPI() { + return this.schema.toOpenAPI(); + } + parseSafe(json) { + if (json === void 0) { + return { success: true, result: void 0 }; + } + return this.schema.parseSafe(json); + } + parse(json) { + const result = this.parseSafe(json); + if (!result.success) { + throw new SchemaError(result.issues); + } + return result.result; + } + visit(visitor) { + visitor(this.schema); + this.schema.visit(visitor); + } +}; +var KDefault = class extends BaseSchema { + schema; + defaultValue; + constructor(schema, defaultValue) { + super({}); + this.schema = schema; + this.defaultValue = defaultValue; + } + serialize(value) { + return this.schema.serialize(value === void 0 ? this.defaultValue : value); + } + toOpenAPI() { + return { + ...this.schema.toOpenAPI(), + default: this.schema.serialize(this.defaultValue) + }; + } + parseSafe(json) { + if (json === void 0) { + return { success: true, result: this.defaultValue }; + } + return this.schema.parseSafe(json); + } + parse(json) { + const result = this.parseSafe(json); + if (!result.success) { + throw new SchemaError(result.issues); + } + return result.result; + } + visit(visitor) { + visitor(this.schema); + this.schema.visit(visitor); + } +}; var STRING_FORMAT_REGEXES = { uuid: /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i, email: /^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i, @@ -406,6 +486,22 @@ var KNumber = class _KNumber extends BaseSchema { visit() { } }; +var KCoercedNumber = class extends KNumber { + static create = () => new KCoercedNumber({}); + parseSafe(json) { + if (typeof json === "string") { + if (json.trim() === "") { + return { success: false, issues: /* @__PURE__ */ new Set([{ message: "Expected number", path: [] }]) }; + } + const coerced = Number(json); + if (!Number.isFinite(coerced)) { + return { success: false, issues: /* @__PURE__ */ new Set([{ message: "Expected number", path: [] }]) }; + } + return super.parseSafe(coerced); + } + return super.parseSafe(json); + } +}; var KBoolean = class _KBoolean extends BaseSchema { static create = () => new _KBoolean({}); serialize(value) { @@ -545,6 +641,9 @@ var KObject = class _KObject extends BaseSchema { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { const fieldValue = value[key]; if (fieldValue === void 0) { + if (this.def.shape[key] instanceof KOptional) { + continue; + } throw new Error(`Missing required property: ${key}`); } result[key] = this.def.shape[key].serialize(fieldValue); @@ -563,7 +662,7 @@ var KObject = class _KObject extends BaseSchema { return [key, value.toOpenAPI()]; }) ), - required: Object.keys(this.def.shape) + required: Object.entries(this.def.shape).filter(([, value]) => !(value instanceof KOptional) && !(value instanceof KDefault)).map(([key]) => key) }; } parseSafe(json) { @@ -574,11 +673,7 @@ var KObject = class _KObject extends BaseSchema { const result = {}; for (const key in this.def.shape) { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { - const value = json[key]; - if (value === void 0) { - return ctx.addIssue(`Missing required property: ${key}`, [key]); - } - const parseResult = this.def.shape[key].parseSafe(value); + const parseResult = this.def.shape[key].parseSafe(json[key]); if (!parseResult.success) { return ctx.addIssues(parseResult.issues, [key]); } @@ -621,10 +716,7 @@ var KObjectFromURLSearchParams = class _KObjectFromURLSearchParams extends KObje const result = {}; for (const key in this.def.shape) { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { - const value = json.get(key); - if (value === null) { - return ctx.addIssue(`Missing required property: ${key}`, [key]); - } + const value = json.get(key) ?? void 0; const parseResult = this.def.shape[key].parseSafe(value); if (!parseResult.success) { return ctx.addIssues(parseResult.issues, [key]); @@ -644,6 +736,9 @@ var KRef = class _KRef extends BaseSchema { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { const fieldValue = value[key]; if (fieldValue === void 0) { + if (this.def.shape[key] instanceof KOptional) { + continue; + } throw new Error(`Missing required property: ${key}`); } result[key] = this.def.shape[key].serialize(fieldValue); @@ -675,11 +770,7 @@ var KRef = class _KRef extends BaseSchema { const result = {}; for (const key in this.def.shape) { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { - const value = json[key]; - if (value === void 0) { - return ctx.addIssue(`Missing required property: ${key}`, [key]); - } - const parseResult = this.def.shape[key].parseSafe(value); + const parseResult = this.def.shape[key].parseSafe(json[key]); if (!parseResult.success) { return ctx.addIssues(parseResult.issues, [key]); } @@ -951,6 +1042,9 @@ var KLazy = class _KLazy extends BaseSchema { var k = { string: KString.create, number: KNumber.create, + coerce: { + number: KCoercedNumber.create + }, boolean: KBoolean.create, array: KArray.create, null: KNull.create, diff --git a/dist/index.cjs b/dist/index.cjs index 98f0764..47390b8 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -247,6 +247,18 @@ var BaseSchema = class { or(other) { return k.union([this, other]); } + optional() { + return new KOptional(this); + } + nullable() { + return this.or(k.null()); + } + nullish() { + return this.nullable().optional(); + } + default(defaultValue) { + return new KDefault(this, defaultValue); + } example(example) { if (example === void 0) { return this.def.example; @@ -260,6 +272,74 @@ var BaseSchema = class { return this.clone({ description }); } }; +var KOptional = class extends BaseSchema { + schema; + constructor(schema) { + super({}); + this.schema = schema; + } + serialize(value) { + if (value === void 0) { + return void 0; + } + return this.schema.serialize(value); + } + toOpenAPI() { + return this.schema.toOpenAPI(); + } + parseSafe(json) { + if (json === void 0) { + return { success: true, result: void 0 }; + } + return this.schema.parseSafe(json); + } + parse(json) { + const result = this.parseSafe(json); + if (!result.success) { + throw new SchemaError(result.issues); + } + return result.result; + } + visit(visitor) { + visitor(this.schema); + this.schema.visit(visitor); + } +}; +var KDefault = class extends BaseSchema { + schema; + defaultValue; + constructor(schema, defaultValue) { + super({}); + this.schema = schema; + this.defaultValue = defaultValue; + } + serialize(value) { + return this.schema.serialize(value === void 0 ? this.defaultValue : value); + } + toOpenAPI() { + return { + ...this.schema.toOpenAPI(), + default: this.schema.serialize(this.defaultValue) + }; + } + parseSafe(json) { + if (json === void 0) { + return { success: true, result: this.defaultValue }; + } + return this.schema.parseSafe(json); + } + parse(json) { + const result = this.parseSafe(json); + if (!result.success) { + throw new SchemaError(result.issues); + } + return result.result; + } + visit(visitor) { + visitor(this.schema); + this.schema.visit(visitor); + } +}; var STRING_FORMAT_REGEXES = { uuid: /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i, email: /^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i, @@ -567,6 +647,22 @@ var KNumber = class _KNumber extends BaseSchema { visit() { } }; +var KCoercedNumber = class extends KNumber { + static create = () => new KCoercedNumber({}); + parseSafe(json) { + if (typeof json === "string") { + if (json.trim() === "") { + return { success: false, issues: /* @__PURE__ */ new Set([{ message: "Expected number", path: [] }]) }; + } + const coerced = Number(json); + if (!Number.isFinite(coerced)) { + return { success: false, issues: /* @__PURE__ */ new Set([{ message: "Expected number", path: [] }]) }; + } + return super.parseSafe(coerced); + } + return super.parseSafe(json); + } +}; var KBoolean = class _KBoolean extends BaseSchema { static create = () => new _KBoolean({}); serialize(value) { @@ -706,6 +802,9 @@ var KObject = class _KObject extends BaseSchema { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { const fieldValue = value[key]; if (fieldValue === void 0) { + if (this.def.shape[key] instanceof KOptional) { + continue; + } throw new Error(`Missing required property: ${key}`); } result[key] = this.def.shape[key].serialize(fieldValue); @@ -724,7 +823,7 @@ var KObject = class _KObject extends BaseSchema { return [key, value.toOpenAPI()]; }) ), - required: Object.keys(this.def.shape) + required: Object.entries(this.def.shape).filter(([, value]) => !(value instanceof KOptional) && !(value instanceof KDefault)).map(([key]) => key) }; } parseSafe(json) { @@ -735,11 +834,7 @@ var KObject = class _KObject extends BaseSchema { const result = {}; for (const key in this.def.shape) { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { - const value = json[key]; - if (value === void 0) { - return ctx.addIssue(`Missing required property: ${key}`, [key]); - } - const parseResult = this.def.shape[key].parseSafe(value); + const parseResult = this.def.shape[key].parseSafe(json[key]); if (!parseResult.success) { return ctx.addIssues(parseResult.issues, [key]); } @@ -782,10 +877,7 @@ var KObjectFromURLSearchParams = class _KObjectFromURLSearchParams extends KObje const result = {}; for (const key in this.def.shape) { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { - const value = json.get(key); - if (value === null) { - return ctx.addIssue(`Missing required property: ${key}`, [key]); - } + const value = json.get(key) ?? void 0; const parseResult = this.def.shape[key].parseSafe(value); if (!parseResult.success) { return ctx.addIssues(parseResult.issues, [key]); @@ -805,6 +897,9 @@ var KRef = class _KRef extends BaseSchema { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { const fieldValue = value[key]; if (fieldValue === void 0) { + if (this.def.shape[key] instanceof KOptional) { + continue; + } throw new Error(`Missing required property: ${key}`); } result[key] = this.def.shape[key].serialize(fieldValue); @@ -836,11 +931,7 @@ var KRef = class _KRef extends BaseSchema { const result = {}; for (const key in this.def.shape) { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { - const value = json[key]; - if (value === void 0) { - return ctx.addIssue(`Missing required property: ${key}`, [key]); - } - const parseResult = this.def.shape[key].parseSafe(value); + const parseResult = this.def.shape[key].parseSafe(json[key]); if (!parseResult.success) { return ctx.addIssues(parseResult.issues, [key]); } @@ -1112,6 +1203,9 @@ var KLazy = class _KLazy extends BaseSchema { var k = { string: KString.create, number: KNumber.create, + coerce: { + number: KCoercedNumber.create + }, boolean: KBoolean.create, array: KArray.create, null: KNull.create, @@ -1312,7 +1406,7 @@ var KaitoRouter = class _KaitoRouter { }) ); const sseHeaders = new Headers(head.touched ? head.headers : void 0); - sseHeaders.set("Content-Type", "text/event-stream"); + sseHeaders.set("Content-Type", "text/event-stream; charset=utf-8"); sseHeaders.set("Cache-Control", "no-cache"); sseHeaders.set("Connection", "keep-alive"); return new Response(stringStream, { diff --git a/dist/index.js b/dist/index.js index 7e4c4ff..f74b9c5 100644 --- a/dist/index.js +++ b/dist/index.js @@ -245,7 +245,7 @@ var KaitoRouter = class _KaitoRouter { }) ); const sseHeaders = new Headers(head.touched ? head.headers : void 0); - sseHeaders.set("Content-Type", "text/event-stream"); + sseHeaders.set("Content-Type", "text/event-stream; charset=utf-8"); sseHeaders.set("Cache-Control", "no-cache"); sseHeaders.set("Connection", "keep-alive"); return new Response(stringStream, { diff --git a/dist/schema/schema.cjs b/dist/schema/schema.cjs index 26cebbd..5568fe9 100644 --- a/dist/schema/schema.cjs +++ b/dist/schema/schema.cjs @@ -129,6 +129,18 @@ var BaseSchema = class { or(other) { return k.union([this, other]); } + optional() { + return new KOptional(this); + } + nullable() { + return this.or(k.null()); + } + nullish() { + return this.nullable().optional(); + } + default(defaultValue) { + return new KDefault(this, defaultValue); + } example(example) { if (example === void 0) { return this.def.example; @@ -142,6 +154,74 @@ var BaseSchema = class { return this.clone({ description }); } }; +var KOptional = class extends BaseSchema { + schema; + constructor(schema) { + super({}); + this.schema = schema; + } + serialize(value) { + if (value === void 0) { + return void 0; + } + return this.schema.serialize(value); + } + toOpenAPI() { + return this.schema.toOpenAPI(); + } + parseSafe(json) { + if (json === void 0) { + return { success: true, result: void 0 }; + } + return this.schema.parseSafe(json); + } + parse(json) { + const result = this.parseSafe(json); + if (!result.success) { + throw new SchemaError(result.issues); + } + return result.result; + } + visit(visitor) { + visitor(this.schema); + this.schema.visit(visitor); + } +}; +var KDefault = class extends BaseSchema { + schema; + defaultValue; + constructor(schema, defaultValue) { + super({}); + this.schema = schema; + this.defaultValue = defaultValue; + } + serialize(value) { + return this.schema.serialize(value === void 0 ? this.defaultValue : value); + } + toOpenAPI() { + return { + ...this.schema.toOpenAPI(), + default: this.schema.serialize(this.defaultValue) + }; + } + parseSafe(json) { + if (json === void 0) { + return { success: true, result: this.defaultValue }; + } + return this.schema.parseSafe(json); + } + parse(json) { + const result = this.parseSafe(json); + if (!result.success) { + throw new SchemaError(result.issues); + } + return result.result; + } + visit(visitor) { + visitor(this.schema); + this.schema.visit(visitor); + } +}; var STRING_FORMAT_REGEXES = { uuid: /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i, email: /^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i, @@ -449,6 +529,22 @@ var KNumber = class _KNumber extends BaseSchema { visit() { } }; +var KCoercedNumber = class extends KNumber { + static create = () => new KCoercedNumber({}); + parseSafe(json) { + if (typeof json === "string") { + if (json.trim() === "") { + return { success: false, issues: /* @__PURE__ */ new Set([{ message: "Expected number", path: [] }]) }; + } + const coerced = Number(json); + if (!Number.isFinite(coerced)) { + return { success: false, issues: /* @__PURE__ */ new Set([{ message: "Expected number", path: [] }]) }; + } + return super.parseSafe(coerced); + } + return super.parseSafe(json); + } +}; var KBoolean = class _KBoolean extends BaseSchema { static create = () => new _KBoolean({}); serialize(value) { @@ -588,6 +684,9 @@ var KObject = class _KObject extends BaseSchema { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { const fieldValue = value[key]; if (fieldValue === void 0) { + if (this.def.shape[key] instanceof KOptional) { + continue; + } throw new Error(`Missing required property: ${key}`); } result[key] = this.def.shape[key].serialize(fieldValue); @@ -606,7 +705,7 @@ var KObject = class _KObject extends BaseSchema { return [key, value.toOpenAPI()]; }) ), - required: Object.keys(this.def.shape) + required: Object.entries(this.def.shape).filter(([, value]) => !(value instanceof KOptional) && !(value instanceof KDefault)).map(([key]) => key) }; } parseSafe(json) { @@ -617,11 +716,7 @@ var KObject = class _KObject extends BaseSchema { const result = {}; for (const key in this.def.shape) { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { - const value = json[key]; - if (value === void 0) { - return ctx.addIssue(`Missing required property: ${key}`, [key]); - } - const parseResult = this.def.shape[key].parseSafe(value); + const parseResult = this.def.shape[key].parseSafe(json[key]); if (!parseResult.success) { return ctx.addIssues(parseResult.issues, [key]); } @@ -664,10 +759,7 @@ var KObjectFromURLSearchParams = class _KObjectFromURLSearchParams extends KObje const result = {}; for (const key in this.def.shape) { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { - const value = json.get(key); - if (value === null) { - return ctx.addIssue(`Missing required property: ${key}`, [key]); - } + const value = json.get(key) ?? void 0; const parseResult = this.def.shape[key].parseSafe(value); if (!parseResult.success) { return ctx.addIssues(parseResult.issues, [key]); @@ -687,6 +779,9 @@ var KRef = class _KRef extends BaseSchema { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { const fieldValue = value[key]; if (fieldValue === void 0) { + if (this.def.shape[key] instanceof KOptional) { + continue; + } throw new Error(`Missing required property: ${key}`); } result[key] = this.def.shape[key].serialize(fieldValue); @@ -718,11 +813,7 @@ var KRef = class _KRef extends BaseSchema { const result = {}; for (const key in this.def.shape) { if (Object.prototype.hasOwnProperty.call(this.def.shape, key)) { - const value = json[key]; - if (value === void 0) { - return ctx.addIssue(`Missing required property: ${key}`, [key]); - } - const parseResult = this.def.shape[key].parseSafe(value); + const parseResult = this.def.shape[key].parseSafe(json[key]); if (!parseResult.success) { return ctx.addIssues(parseResult.issues, [key]); } @@ -994,6 +1085,9 @@ var KLazy = class _KLazy extends BaseSchema { var k = { string: KString.create, number: KNumber.create, + coerce: { + number: KCoercedNumber.create + }, boolean: KBoolean.create, array: KArray.create, null: KNull.create, diff --git a/dist/schema/schema.d.cts b/dist/schema/schema.d.cts index a174a17..ed9d9da 100644 --- a/dist/schema/schema.d.cts +++ b/dist/schema/schema.d.cts @@ -60,6 +60,10 @@ declare abstract class BaseSchema): this; protected constructor(def: Def); or>(other: BaseSchema): KUnion<(this | BaseSchema)["_input"], (this | BaseSchema)["_output"]>; + optional(): BaseSchema>; + nullable(): BaseSchema>; + nullish(): BaseSchema>; + default(defaultValue: Output): BaseSchema, BaseSchemaDef>; example(example: Input): this; example(): Input | undefined; description(description: string): this; @@ -365,6 +369,9 @@ declare class KLazy extends BaseSchema KString; number: () => KNumber; + coerce: { + number: () => KNumber; + }; boolean: () => KBoolean; array: >(items: BaseSchema) => KArray; null: () => KNull; diff --git a/dist/schema/schema.d.ts b/dist/schema/schema.d.ts index a174a17..ed9d9da 100644 --- a/dist/schema/schema.d.ts +++ b/dist/schema/schema.d.ts @@ -60,6 +60,10 @@ declare abstract class BaseSchema): this; protected constructor(def: Def); or>(other: BaseSchema): KUnion<(this | BaseSchema)["_input"], (this | BaseSchema)["_output"]>; + optional(): BaseSchema>; + nullable(): BaseSchema>; + nullish(): BaseSchema>; + default(defaultValue: Output): BaseSchema, BaseSchemaDef>; example(example: Input): this; example(): Input | undefined; description(description: string): this; @@ -365,6 +369,9 @@ declare class KLazy extends BaseSchema KString; number: () => KNumber; + coerce: { + number: () => KNumber; + }; boolean: () => KBoolean; array: >(items: BaseSchema) => KArray; null: () => KNull;