Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,8 @@ on:
branches: [master]

jobs:
release-test:
if: github.actor != 'renovate[bot]'
uses: ./.github/workflows/test.yml

release-github:
name: Github Release
needs: [release-test]
runs-on: ubuntu-latest
Comment on lines 7 to 10
concurrency:
group: release
Expand Down
1 change: 1 addition & 0 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 20 additions & 4 deletions packages/fs/src/watch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { DT } from '@targetd/api'
import { debounce, Mutex } from '@es-toolkit/es-toolkit'
import { watch as fsWatch, type WatchOptions } from 'node:fs'
import {
watch as fsWatch,
type WatchOptions as BaseWatchOptions,
} from 'node:fs'
import { load, pathIsLoadable } from './load.ts'

/**
Expand All @@ -26,6 +29,17 @@ import { load, pathIsLoadable } from './load.ts'
*/
export type OnLoad<D extends DT.Any> = (error: Error | null, data: D) => any

/**
* Options for watching a directory for rule file changes.
*/
export interface WatchOptions extends BaseWatchOptions {
/**
* Milliseconds to debounce file change events before reloading rules.
* @default 300
*/
debounceMS?: number
}

/**
* Watch a directory for rule file changes and automatically reload.
* Provides hot-reloading of targeting rules without restarting the application.
Expand Down Expand Up @@ -80,7 +94,9 @@ export function watch<D extends DT.Any>(
optionsOrOnLoad: WatchOptions | OnLoad<D>,
onLoadParam?: OnLoad<D>,
) {
const options = onLoadParam ? optionsOrOnLoad as WatchOptions : {}
const { debounceMS = 300, ...fsOptions } = onLoadParam
? optionsOrOnLoad as WatchOptions
: {}
const onLoad = (onLoadParam || optionsOrOnLoad) as OnLoad<D>
const mutex = new Mutex()

Expand All @@ -99,13 +115,13 @@ export function watch<D extends DT.Any>(

const watcher = fsWatch(
dir,
options,
fsOptions,
debounce(
async (_eventType, filename) => {
if (filename && !pathIsLoadable(filename)) return
await onChange()
},
300,
debounceMS,
),
)

Expand Down
2 changes: 1 addition & 1 deletion packages/json-schema/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"./cli": "./src/cli.ts"
},
"imports": {
"@es-toolkit/es-toolkit": "jsr:@es-toolkit/es-toolkit@^1.45.1",
"@targetd/api": "jsr:@targetd/api@^8.0.3",
"@types/yargs": "npm:@types/yargs@^17.0.35",
"yargs": "npm:yargs@^18.0.0",
Expand All @@ -31,7 +32,6 @@
"publish": {
"include": [
"deno.json",
"package.json",
"src/",
"LICENSE",
"README.md"
Expand Down
4 changes: 0 additions & 4 deletions packages/json-schema/package.json

This file was deleted.

4 changes: 2 additions & 2 deletions packages/json-schema/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { DT } from '@targetd/api'
import yargs from 'yargs'
import * as path from 'node:path'
import { pathToFileURL } from 'node:url'
import { writeFile } from 'node:fs/promises'
import { argv, cwd } from 'node:process'
import { dataJSONSchemas } from './index.ts'
Expand Down Expand Up @@ -31,8 +32,7 @@ const { dataExport, inputModule, outputFile } = await yargs()
.parseAsync(argv)

const input = await import(
// We need a forward slash here so that Deno is aware it's an absolute path
`/${path.resolve(cwd(), inputModule).replace(/^\//, '')}`
pathToFileURL(path.resolve(cwd(), inputModule)).href
)

const data = input[dataExport]
Expand Down
55 changes: 39 additions & 16 deletions packages/json-schema/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { omit } from '@es-toolkit/es-toolkit'
import {
DataItemParser,
DataItemsParser,
Expand Down Expand Up @@ -26,6 +27,7 @@ import type { $ZodType, JSONSchema } from 'zod/v4/core'
*/
export function dataJSONSchemas<D extends DT.Any>(
data: D,
params?: ToJSONSchemaParams,
): JSONSchema.BaseSchema {
return toJSONSchema(
extend(
Expand All @@ -36,7 +38,7 @@ export function dataJSONSchemas<D extends DT.Any>(
) as any,
{ $schema: optional(string()) },
),
params,
toJSONSchemaParams(params),
)
}

Expand All @@ -58,6 +60,7 @@ export function dataJSONSchemas<D extends DT.Any>(
export function dataJSONSchema<D extends DT.Any>(
data: D,
name: keyof DT.PayloadParsers<D>,
params?: ToJSONSchemaParams,
): JSONSchema.BaseSchema {
return toJSONSchema(
extend(
Expand All @@ -69,25 +72,45 @@ export function dataJSONSchema<D extends DT.Any>(
) as any,
{ $schema: optional(string()) },
),
params,
toJSONSchemaParams(params),
)
}

const params: NonNullable<Parameters<typeof toJSONSchema>[1]> = {
io: 'input',
unrepresentable: 'any',
reused: 'ref',
override(ctx) {
if (isZodSwitch(ctx.zodSchema)) {
const union = switchRegistry.get(ctx.zodSchema)
?.union
if (union) {
ctx.jsonSchema = toJSONSchema(union as any, params)
type _ToJSONSchemaParams = NonNullable<Parameters<typeof toJSONSchema>[1]>

export type ToJSONSchemaParams = _ToJSONSchemaParams & {
override?: (
...args: Parameters<Required<_ToJSONSchemaParams>['override']>
) => void | boolean
}
Comment on lines +81 to +85

function toJSONSchemaParams(params?: ToJSONSchemaParams): _ToJSONSchemaParams {
return {
io: 'input',
reused: 'ref',
unrepresentable: 'any',
override(ctx) {
if (params?.override?.(ctx)) return
if (isZodSwitch(ctx.zodSchema)) {
const union = switchRegistry.get(ctx.zodSchema)
?.union
if (union) {
const schema = toJSONSchema(
union as any,
toJSONSchemaParams(params),
)
// Mutate the jsonSchema in place - ctx.jsonSchema is a reference
// to the actual schema object, so we must modify it directly
for (const key in ctx.jsonSchema) {
delete (ctx.jsonSchema as Record<string, unknown>)[key]
}
Object.assign(ctx.jsonSchema, schema)
delete (ctx.jsonSchema as Record<string, unknown>).$schema
}
}
} else if (ctx.zodSchema._zod.def.type === 'transform') {
ctx.jsonSchema = {}
}
},
},
...(params && omit(params, ['override'])),
}
}

function isZodSwitch(parser: $ZodType): parser is ZodSwitch {
Expand Down
157 changes: 156 additions & 1 deletion packages/json-schema/test/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,15 @@ snapshot[`json schema for simple data object 1`] = `
items: {
additionalProperties: false,
properties: {
payload: {},
payload: {
anyOf: [
{
pattern: "^\\\\{\\\\{[\\\\s\\\\S]{0,}\\\\}\\\\}\$",
type: "string",
},
{},
],
},
targeting: {
anyOf: [
{
Expand Down Expand Up @@ -68,13 +76,160 @@ snapshot[`json schema for simple data object 1`] = `
},
type: "object",
},
__schema6: {
additionalProperties: false,
properties: {
browser: {
"\$ref": "#/\$defs/__schema2",
},
weather: {
"\$ref": "#/\$defs/__schema1",
},
},
type: "object",
},
__schema7: {
type: "string",
},
__schema8: {
items: {
additionalProperties: false,
properties: {
payload: {
anyOf: [
{
pattern: "^\\\\{\\\\{[\\\\s\\\\S]{0,}\\\\}\\\\}\$",
type: "string",
},
{},
],
},
targeting: {
anyOf: [
{
"\$ref": "#/\$defs/__schema9",
},
{
items: {
"\$ref": "#/\$defs/__schema9",
},
type: "array",
},
],
},
},
required: [
"payload",
],
type: "object",
},
type: "array",
},
__schema9: {
additionalProperties: false,
properties: {
browser: {
"\$ref": "#/\$defs/__schema2",
},
weather: {
"\$ref": "#/\$defs/__schema1",
},
},
type: "object",
},
},
"\$schema": "https://json-schema.org/draft/2020-12/schema",
additionalProperties: false,
properties: {
"\$schema": {
type: "string",
},
bar: {
additionalProperties: false,
properties: {
rules: {
items: {
additionalProperties: false,
properties: {
payload: {
anyOf: [
{
pattern: "^\\\\{\\\\{[\\\\s\\\\S]{0,}\\\\}\\\\}\$",
type: "string",
},
{
additionalProperties: false,
properties: {
a: {
anyOf: [
{
pattern: "^\\\\{\\\\{[\\\\s\\\\S]{0,}\\\\}\\\\}\$",
type: "string",
},
{
type: "number",
},
],
},
b: {
anyOf: [
{
pattern: "^\\\\{\\\\{[\\\\s\\\\S]{0,}\\\\}\\\\}\$",
type: "string",
},
{
items: {
type: "string",
},
type: "array",
},
],
},
},
required: [
"a",
"b",
],
type: "object",
},
],
},
targeting: {
anyOf: [
{
"\$ref": "#/\$defs/__schema6",
},
{
items: {
"\$ref": "#/\$defs/__schema6",
},
type: "array",
},
],
},
},
required: [
"payload",
],
type: "object",
},
type: "array",
},
variables: {
additionalProperties: {
"\$ref": "#/\$defs/__schema8",
},
propertyNames: {
"\$ref": "#/\$defs/__schema7",
},
type: "object",
},
},
required: [
"rules",
],
type: "object",
},
foo: {
additionalProperties: false,
properties: {
Expand Down
Loading
Loading