Skip to content

Commit f4a1c68

Browse files
committed
Merge branch 'release/v0.28.5'
2 parents 3e377d9 + 7f778c5 commit f4a1c68

9 files changed

Lines changed: 90 additions & 18 deletions

File tree

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "zeed",
33
"type": "module",
4-
"version": "0.28.4",
4+
"version": "0.28.5",
55
"packageManager": "pnpm@10.10.0",
66
"description": "🌱 Simple foundation library",
77
"author": {
@@ -77,12 +77,12 @@
7777
"@vitest/browser": "^3.1.2",
7878
"@vitest/coverage-v8": "^3.1.2",
7979
"esbuild": "^0.25.3",
80-
"eslint": "^9.25.1",
80+
"eslint": "^9.26.0",
8181
"playwright": "^1.52.0",
8282
"pnpm": "^10.7.0",
8383
"tsup": "^8.4.0",
8484
"typescript": "^5.8.3",
85-
"vite": "^6.3.3",
85+
"vite": "^6.3.4",
8686
"vitest": "^3.1.2"
8787
},
8888
"pnpm": {

src/browser/gravatar.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,9 @@ function gravatar(
157157
for (n = 0; n < 64; n += 4) {
158158
t[n >> 2]
159159
= e.charCodeAt(n)
160-
+ (e.charCodeAt(n + 1) << 8)
161-
+ (e.charCodeAt(n + 2) << 16)
162-
+ (e.charCodeAt(n + 3) << 24)
160+
+ (e.charCodeAt(n + 1) << 8)
161+
+ (e.charCodeAt(n + 2) << 16)
162+
+ (e.charCodeAt(n + 3) << 24)
163163
}
164164
return t
165165
}

src/common/log/log-console-original.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { getGlobalContext } from '../global'
22

33
// Global logger to guarantee all submodules use the same logger instance
44

5-
export type OriginalConsole = Pick<Console, 'log' | 'info' | 'warn' | 'error' | 'debug' > & { console: Console }
5+
export type OriginalConsole = Pick<Console, 'log' | 'info' | 'warn' | 'error' | 'debug'> & { console: Console }
66

77
declare global {
88
interface ZeedGlobalContext {

src/common/schema/README-SCHEMA.md

Whitespace-only changes.

src/common/schema/parse-args.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ describe('args.spec', () => {
6767
"argDesc": "Shows help",
6868
"argShort": "h",
6969
},
70+
"extend": [Function],
7071
"type": "boolean",
7172
},
7273
"someStuff": TypeGeneric {
@@ -75,9 +76,11 @@ describe('args.spec', () => {
7576
"_props": Object {
7677
"argDesc": "Does some stuff",
7778
},
79+
"extend": [Function],
7880
"type": "number",
7981
},
8082
},
83+
"extend": [Function],
8184
"type": "object",
8285
}
8386
`)

src/common/schema/rpc.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,12 @@ describe('rpc.spec', () => {
9191
"_args": Array [
9292
TypeStringClass {
9393
"_check": [Function],
94+
"extend": [Function],
9495
"type": "string",
9596
},
9697
TypeGeneric {
9798
"_check": [Function],
99+
"extend": [Function],
98100
"type": "boolean",
99101
},
100102
],
@@ -105,12 +107,15 @@ describe('rpc.spec', () => {
105107
"_ret": TypeStringClass {
106108
"_check": [Function],
107109
"_optional": true,
110+
"extend": [Function],
108111
"type": "string",
109112
},
113+
"extend": [Function],
110114
"type": "function",
111115
},
112116
"noFunc": TypeStringClass {
113117
"_check": [Function],
118+
"extend": [Function],
114119
"type": "string",
115120
},
116121
"rpc": TypeRpcClass {
@@ -120,36 +125,45 @@ describe('rpc.spec', () => {
120125
"_object": Object {
121126
"id": TypeStringClass {
122127
"_check": [Function],
128+
"extend": [Function],
123129
"type": "string",
124130
},
125131
"method": TypeStringClass {
126132
"_check": [Function],
133+
"extend": [Function],
127134
"type": "string",
128135
},
129136
"params": TypeObjectClass {
130137
"_check": [Function],
131138
"_object": Object {
132139
"a": TypeStringClass {
133140
"_check": [Function],
141+
"extend": [Function],
134142
"type": "string",
135143
},
136144
"b": TypeGeneric {
137145
"_check": [Function],
146+
"extend": [Function],
138147
"type": "boolean",
139148
},
140149
},
150+
"extend": [Function],
141151
"type": "object",
142152
},
143153
},
154+
"extend": [Function],
144155
"type": "object",
145156
},
146157
"_ret": TypeGeneric {
147158
"_check": [Function],
159+
"extend": [Function],
148160
"type": "none",
149161
},
162+
"extend": [Function],
150163
"type": "rpc",
151164
},
152165
},
166+
"extend": [Function],
153167
"type": "object",
154168
}
155169
`)

src/common/schema/schema.spec.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { Infer } from './schema'
22
import type { Expect, IsEqual } from './type-test'
33
import { cloneJsonObject } from '../data'
4-
import { any, array, boolean, float, int, literal, number, object, string, stringLiterals, tuple, union } from './schema'
4+
import { uuid } from '../uuid'
5+
import { any, array, boolean, float, int, literal, number, object, string, stringLiterals, tuple, union, z } from './schema'
56

67
describe('schema', () => {
78
it('create schema', async () => {
@@ -216,4 +217,28 @@ describe('schema', () => {
216217
type _SchemaTest = Expect<IsEqual<Schema, SchemaExpected>> // Should pass
217218
expectTypeOf<Schema>().toMatchTypeOf<SchemaExpected>()
218219
})
220+
221+
it('mimic extend schema', async () => {
222+
const baseSchema = z.object({
223+
id: string().default(uuid),
224+
})
225+
const extendedSchema = baseSchema.extend({
226+
name: string(),
227+
age: int().optional(),
228+
active: boolean(),
229+
tags: array(string()).optional(),
230+
info: any(),
231+
})
232+
type BaseSchema = Infer<typeof baseSchema>
233+
type ExtendedSchema = Infer<typeof extendedSchema>
234+
expectTypeOf<BaseSchema>().toMatchObjectType<{ id: string }>()
235+
expectTypeOf<ExtendedSchema>().toMatchObjectType<{
236+
id: string
237+
age?: number | undefined
238+
tags?: string[] | undefined
239+
info?: any
240+
name: string
241+
active: boolean
242+
}>()
243+
})
219244
})

src/common/schema/schema.ts

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export interface Type<T = unknown> {
2323
parse: (obj: any) => T
2424
map: (obj: any, fn: (this: Type<T>, obj: any, schema: Type<T>) => any) => any
2525
props: (props: TypeProps) => Type<T>
26+
extend: <O>(obj: O) => Type<T & InferObject<O>>
2627
}
2728

2829
export abstract class TypeClass<T = unknown> implements Type<T> {
@@ -78,6 +79,11 @@ export abstract class TypeClass<T = unknown> implements Type<T> {
7879
this._props = props
7980
return this
8081
}
82+
83+
extend: <O>(obj: O) => Type<T & InferObject<O>> = (obj: any) => {
84+
const newObj = { ...this._object, ...obj }
85+
return object(newObj) as any
86+
}
8187
}
8288

8389
export type Infer<T> = T extends Type<infer TT> ? TT : never
@@ -100,10 +106,12 @@ class TypeStringClass<T extends string> extends TypeClass<T> {
100106
}
101107
}
102108

109+
/// Just a simple string type
103110
export function string() {
104111
return new TypeStringClass<string>('string', isString)
105112
}
106113

114+
/// Number as in Javascript, could be float or int
107115
export function number() {
108116
return generic<number>('number', {
109117
_check: isNumber,
@@ -114,12 +122,14 @@ export const float = number
114122
export const double = number
115123
export const real = number
116124

125+
/// Integer
117126
export function int() {
118127
return generic<number>('int', {
119128
_check: isInteger,
120129
})
121130
}
122131

132+
/// Boolean
123133
export function boolean() {
124134
return generic<boolean>('boolean', {
125135
_check: isBoolean,
@@ -134,7 +144,7 @@ export function none() {
134144
})
135145
}
136146

137-
// todo: appears to result in optional inside object
147+
/// todo: appears to result in optional inside object
138148
export function any() {
139149
return generic<any>('any', {
140150
_check: v => v != null,
@@ -198,6 +208,7 @@ export class TypeObjectClass<T, O = InferObject<T>> extends TypeClass<O> {
198208
}
199209
}
200210

211+
/// Object that can have any properties
201212
export function object<T>(tobj: T): Type<InferObject<T>> {
202213
return new TypeObjectClass(tobj)
203214
}
@@ -215,23 +226,25 @@ export function union<T extends (Type<any>)[]>(options: T): Type<TransformToUnio
215226
})
216227
}
217228

218-
// Literal
229+
// Literals
219230

220231
type Literal = string | number | bigint | boolean
221232

233+
/// todo: string?
222234
export function literal<T extends Literal>(value: T): Type<T> {
223235
return generic<T>('string', {
224236
_check: v => v === value,
225237
})
226238
}
227239

240+
/// Sting that can only be one of the values, like: `"a" | "b" | "c"``
228241
export function stringLiterals<const T extends readonly string[], O = T[number]>(value: T): Type<O> {
229242
return generic<O>('string', {
230243
_check: v => value.includes(v),
231244
})
232245
}
233246

234-
// Function
247+
// Functions
235248

236249
type TupleOutput<T extends Type[]> = {
237250
[K in keyof T]: T[K] extends Type<infer U> ? U : never
@@ -247,6 +260,7 @@ type ArrayType<
247260
Rest extends Type | undefined = Type | undefined,
248261
> = Type<ArrayOutput<Head, Rest>>
249262

263+
// Like an array but with fixed length and types
250264
export function tuple<T extends [] | [Type, ...Type[]]>(items: T): ArrayType<T, undefined> {
251265
return generic('tuple', {
252266
_check: v => items.every((item, i) => item._check(v[i])),
@@ -289,6 +303,7 @@ class TypeFuncClass<T, Args, Ret> extends TypeClass<T> {
289303
_ret?: Ret
290304
}
291305

306+
/// Regular function
292307
export function func<
293308
Args extends [Type<unknown>, ...Type<any>[]] | [],
294309
Ret = Type,
@@ -312,6 +327,7 @@ export class TypeRpcClass<T, Info, Ret> extends TypeClass<T> {
312327
_ret?: Ret
313328
}
314329

330+
/// RPC function that only takes one argument and returns a promise
315331
export function rpc<
316332
Info extends Type<unknown> | undefined = undefined,
317333
Ret extends Type<unknown> = Type<void>, // ReturnType<typeof none>,
@@ -320,9 +336,23 @@ export function rpc<
320336
return new TypeRpcClass<T, Info, Ret>('rpc', info, ret ?? none() as Ret)
321337
}
322338

323-
// const fn = func([string(), boolean(), int()], string()) // typeof fn should be: Type<(...args: [string, boolean]) => string>
324-
325-
// type typeFn = Infer<typeof fn> // typeFn should be: (...args: [string, boolean]) => string
326-
327-
// type f1 = (a: number, b?: string) => boolean
328-
// type xx = Parameters<f1>
339+
/// Reduce conflicts with real type names, use like z.string()
340+
export const z = {
341+
string,
342+
number,
343+
int,
344+
boolean,
345+
none,
346+
any,
347+
object,
348+
array,
349+
tuple,
350+
union,
351+
func,
352+
rpc,
353+
literal,
354+
stringLiterals,
355+
float,
356+
double,
357+
real,
358+
}

src/common/uuid.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export function uuidDecodeB32(uuid: string): Uint8Array {
4444
// https://stackoverflow.com/a/2117523/140927
4545
const pattern = '10000000-1000-4000-8000-100000000000' // String([1e7] + -1e3 + -4e3 + -8e3 + -1e11)
4646

47-
export const uuidv4 = function () {
47+
export function uuidv4() {
4848
return (typeof crypto !== 'undefined' && crypto.randomUUID != null)
4949
? crypto.randomUUID() // native!
5050
: pattern.replace(/[018]/g, (c: any) => (c ^ (randomUint8Array(1)[0] & (15 >> (c / 4)))).toString(16))

0 commit comments

Comments
 (0)