mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 03:19:46 +00:00
fmt
This commit is contained in:
parent
e533ec22b6
commit
3ace715498
4 changed files with 153 additions and 153 deletions
|
|
@ -9,42 +9,42 @@ type ParamValue = string | number | boolean;
|
||||||
export { PolicyRegistry };
|
export { PolicyRegistry };
|
||||||
|
|
||||||
export const policyRegistry = new PolicyRegistry({
|
export const policyRegistry = new PolicyRegistry({
|
||||||
antiDuplicationPolicyStore: {
|
antiDuplicationPolicyStore: {
|
||||||
get: (key: Deno.KvKey) => Promise.resolve({ key, value: null, versionstamp: null }),
|
get: (key: Deno.KvKey) => Promise.resolve({ key, value: null, versionstamp: null }),
|
||||||
set: () => Promise.resolve({ ok: true, versionstamp: '00000000000000000000' }),
|
set: () => Promise.resolve({ ok: true, versionstamp: '00000000000000000000' }),
|
||||||
},
|
},
|
||||||
store: new MockRelay(),
|
store: new MockRelay(),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface PolicySpecItem {
|
interface PolicySpecItem {
|
||||||
name: keyof typeof policyRegistry.available;
|
name: keyof typeof policyRegistry.available;
|
||||||
params?: Record<string, ParamValue | (number | string)[]>;
|
params?: Record<string, ParamValue | (number | string)[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PolicySpec {
|
export interface PolicySpec {
|
||||||
policies: PolicySpecItem[];
|
policies: PolicySpecItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const normalizeNpub = (itm: string) => {
|
export const normalizeNpub = (itm: string) => {
|
||||||
if (!itm.startsWith('npub1')) return itm;
|
if (!itm.startsWith('npub1')) return itm;
|
||||||
return nip19.decode(itm as `npub1${string}`).data;
|
return nip19.decode(itm as `npub1${string}`).data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_POLICY_SPEC: PolicySpec = {
|
export const DEFAULT_POLICY_SPEC: PolicySpec = {
|
||||||
policies: [
|
policies: [
|
||||||
{ 'name': 'AntiDuplicationPolicy' },
|
{ 'name': 'AntiDuplicationPolicy' },
|
||||||
{ 'name': 'HellthreadPolicy' },
|
{ 'name': 'HellthreadPolicy' },
|
||||||
{ 'name': 'ReplyBotPolicy' },
|
{ 'name': 'ReplyBotPolicy' },
|
||||||
{ 'name': 'SizePolicy' },
|
{ 'name': 'SizePolicy' },
|
||||||
{ 'name': 'HashtagPolicy', 'params': { 'hashtags': ['NSFW', 'explicit', 'violence', 'cp', 'porn'] } },
|
{ 'name': 'HashtagPolicy', 'params': { 'hashtags': ['NSFW', 'explicit', 'violence', 'cp', 'porn'] } },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createPolicyEvent = async (conf: DittoConf, policies: PolicySpec) => {
|
export const createPolicyEvent = async (conf: DittoConf, policies: PolicySpec) => {
|
||||||
return await conf.signer.signEvent({
|
return await conf.signer.signEvent({
|
||||||
kind: 11984,
|
kind: 11984,
|
||||||
content: JSON.stringify(policies),
|
content: JSON.stringify(policies),
|
||||||
created_at: nostrNow(),
|
created_at: nostrNow(),
|
||||||
tags: [],
|
tags: [],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,103 +3,103 @@ import { z } from 'zod';
|
||||||
import { zodSchemaToFields } from './parameters.ts';
|
import { zodSchemaToFields } from './parameters.ts';
|
||||||
|
|
||||||
Deno.test('zodSchemaToFields - basic types', () => {
|
Deno.test('zodSchemaToFields - basic types', () => {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
age: z.number(),
|
age: z.number(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = zodSchemaToFields(schema);
|
const result = zodSchemaToFields(schema);
|
||||||
assertEquals(result, {
|
assertEquals(result, {
|
||||||
name: { type: 'string' },
|
name: { type: 'string' },
|
||||||
age: { type: 'number' },
|
age: { type: 'number' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('zodSchemaToFields - array types', () => {
|
Deno.test('zodSchemaToFields - array types', () => {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
tags: z.array(z.string()),
|
tags: z.array(z.string()),
|
||||||
scores: z.array(z.number()),
|
scores: z.array(z.number()),
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = zodSchemaToFields(schema);
|
const result = zodSchemaToFields(schema);
|
||||||
assertEquals(result, {
|
assertEquals(result, {
|
||||||
tags: { type: 'multi_string' },
|
tags: { type: 'multi_string' },
|
||||||
scores: { type: 'multi_number' },
|
scores: { type: 'multi_number' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('zodSchemaToFields - special-case NIP-01 filters', () => {
|
Deno.test('zodSchemaToFields - special-case NIP-01 filters', () => {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
filters: z.array(z.string()),
|
filters: z.array(z.string()),
|
||||||
keywords: z.array(z.string()),
|
keywords: z.array(z.string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = zodSchemaToFields(schema);
|
const result = zodSchemaToFields(schema);
|
||||||
assertEquals(result, {
|
assertEquals(result, {
|
||||||
filters: { type: 'multi_string' },
|
filters: { type: 'multi_string' },
|
||||||
keywords: { type: 'multi_string' },
|
keywords: { type: 'multi_string' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('zodSchemaToFields - mixed types', () => {
|
Deno.test('zodSchemaToFields - mixed types', () => {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
values: z.array(z.number()),
|
values: z.array(z.number()),
|
||||||
flags: z.array(z.string()).describe('Test description'),
|
flags: z.array(z.string()).describe('Test description'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = zodSchemaToFields(schema);
|
const result = zodSchemaToFields(schema);
|
||||||
assertEquals(result, {
|
assertEquals(result, {
|
||||||
id: { type: 'string' },
|
id: { type: 'string' },
|
||||||
values: { type: 'multi_number' },
|
values: { type: 'multi_number' },
|
||||||
flags: { type: 'multi_string', description: 'Test description' },
|
flags: { type: 'multi_string', description: 'Test description' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('zodSchemaToFields - optional fields', () => {
|
Deno.test('zodSchemaToFields - optional fields', () => {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
name: z.string().optional(),
|
name: z.string().optional(),
|
||||||
age: z.number().optional(),
|
age: z.number().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = zodSchemaToFields(schema);
|
const result = zodSchemaToFields(schema);
|
||||||
assertEquals(result, {
|
assertEquals(result, {
|
||||||
name: { type: 'string', optional: true },
|
name: { type: 'string', optional: true },
|
||||||
age: { type: 'number', optional: true },
|
age: { type: 'number', optional: true },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('zodSchemaToFields - default values', () => {
|
Deno.test('zodSchemaToFields - default values', () => {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
name: z.string().default('John Doe'),
|
name: z.string().default('John Doe'),
|
||||||
age: z.number().default(30),
|
age: z.number().default(30),
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = zodSchemaToFields(schema);
|
const result = zodSchemaToFields(schema);
|
||||||
assertEquals(result, {
|
assertEquals(result, {
|
||||||
name: { type: 'string', default: 'John Doe' },
|
name: { type: 'string', default: 'John Doe' },
|
||||||
age: { type: 'number', default: 30 },
|
age: { type: 'number', default: 30 },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('zodSchemaToFields - boolean fields', () => {
|
Deno.test('zodSchemaToFields - boolean fields', () => {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
active: z.boolean(),
|
active: z.boolean(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = zodSchemaToFields(schema);
|
const result = zodSchemaToFields(schema);
|
||||||
assertEquals(result, {
|
assertEquals(result, {
|
||||||
active: { type: 'boolean' },
|
active: { type: 'boolean' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test('zodSchemaToFields - invalid schema', () => {
|
Deno.test('zodSchemaToFields - invalid schema', () => {
|
||||||
const schema = z.object({
|
const schema = z.object({
|
||||||
invalid: z.any(),
|
invalid: z.any(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = zodSchemaToFields(schema);
|
const result = zodSchemaToFields(schema);
|
||||||
assertEquals(result, {
|
assertEquals(result, {
|
||||||
invalid: { type: 'unknown' },
|
invalid: { type: 'unknown' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,52 +3,52 @@ import { z } from 'zod';
|
||||||
type FieldType = 'string' | 'multi_string' | 'number' | 'multi_number' | 'boolean' | 'unknown';
|
type FieldType = 'string' | 'multi_string' | 'number' | 'multi_number' | 'boolean' | 'unknown';
|
||||||
|
|
||||||
export interface FieldItem {
|
export interface FieldItem {
|
||||||
type: FieldType;
|
type: FieldType;
|
||||||
description?: string;
|
description?: string;
|
||||||
optional?: boolean;
|
optional?: boolean;
|
||||||
default?: any;
|
default?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UnwrappedZodType {
|
interface UnwrappedZodType {
|
||||||
baseType: z.ZodTypeAny;
|
baseType: z.ZodTypeAny;
|
||||||
optional?: boolean;
|
optional?: boolean;
|
||||||
defaultValue?: any;
|
defaultValue?: any;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the base type from wrapped Zod types like ZodOptional and ZodDefault.
|
* Extracts the base type from wrapped Zod types like ZodOptional and ZodDefault.
|
||||||
*/
|
*/
|
||||||
function unwrapZodType(field: z.ZodTypeAny): UnwrappedZodType {
|
function unwrapZodType(field: z.ZodTypeAny): UnwrappedZodType {
|
||||||
let optional = false;
|
let optional = false;
|
||||||
let defaultValue: any = undefined;
|
let defaultValue: any = undefined;
|
||||||
let description: string | undefined = undefined;
|
let description: string | undefined = undefined;
|
||||||
|
|
||||||
description = field.description;
|
description = field.description;
|
||||||
|
|
||||||
while (field instanceof z.ZodOptional || field instanceof z.ZodDefault) {
|
while (field instanceof z.ZodOptional || field instanceof z.ZodDefault) {
|
||||||
if (field instanceof z.ZodOptional) optional = true;
|
if (field instanceof z.ZodOptional) optional = true;
|
||||||
if (field instanceof z.ZodDefault) defaultValue = field._def.defaultValue();
|
if (field instanceof z.ZodDefault) defaultValue = field._def.defaultValue();
|
||||||
if (!description) description = field.description;
|
if (!description) description = field.description;
|
||||||
|
|
||||||
field = field._def.innerType;
|
field = field._def.innerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result: UnwrappedZodType = { baseType: field };
|
||||||
|
|
||||||
|
if (optional) result.optional = true;
|
||||||
|
if (typeof defaultValue !== 'undefined') {
|
||||||
|
if (
|
||||||
|
typeof defaultValue === 'string' && defaultValue.length > 0 ||
|
||||||
|
(typeof defaultValue === 'number') ||
|
||||||
|
(typeof defaultValue === 'object' && Object.keys(defaultValue).length > 0) ||
|
||||||
|
(Array.isArray(defaultValue) && defaultValue.length > 0)
|
||||||
|
) {
|
||||||
|
result.defaultValue = defaultValue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const result: UnwrappedZodType = { baseType: field };
|
if (description) result.description = description;
|
||||||
|
return result;
|
||||||
if (optional) result.optional = true;
|
|
||||||
if (typeof defaultValue !== 'undefined') {
|
|
||||||
if (
|
|
||||||
typeof defaultValue === 'string' && defaultValue.length > 0 ||
|
|
||||||
(typeof defaultValue === 'number') ||
|
|
||||||
(typeof defaultValue === 'object' && Object.keys(defaultValue).length > 0) ||
|
|
||||||
(Array.isArray(defaultValue) && defaultValue.length > 0)
|
|
||||||
) {
|
|
||||||
result.defaultValue = defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (description) result.description = description;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -64,27 +64,27 @@ function unwrapZodType(field: z.ZodTypeAny): UnwrappedZodType {
|
||||||
* Special-cases NIP-01 filters as `multi_string`.
|
* Special-cases NIP-01 filters as `multi_string`.
|
||||||
*/
|
*/
|
||||||
export function zodSchemaToFields(schema: z.ZodObject<any>): Record<string, FieldItem> {
|
export function zodSchemaToFields(schema: z.ZodObject<any>): Record<string, FieldItem> {
|
||||||
const result: Record<string, FieldItem> = {};
|
const result: Record<string, FieldItem> = {};
|
||||||
|
|
||||||
for (const [key, field] of Object.entries(schema.shape) as [string, z.ZodTypeAny][]) {
|
for (const [key, field] of Object.entries(schema.shape) as [string, z.ZodTypeAny][]) {
|
||||||
const { baseType, optional, defaultValue, description } = unwrapZodType(field);
|
const { baseType, optional, defaultValue, description } = unwrapZodType(field);
|
||||||
result[key] = { type: 'unknown' };
|
result[key] = { type: 'unknown' };
|
||||||
if (optional) result[key].optional = optional;
|
if (optional) result[key].optional = optional;
|
||||||
if (defaultValue) result[key].default = defaultValue;
|
if (defaultValue) result[key].default = defaultValue;
|
||||||
if (description) result[key].description = description;
|
if (description) result[key].description = description;
|
||||||
|
|
||||||
if (key === 'filters') {
|
if (key === 'filters') {
|
||||||
result[key].type = 'multi_string';
|
result[key].type = 'multi_string';
|
||||||
} else if (baseType instanceof z.ZodArray) {
|
} else if (baseType instanceof z.ZodArray) {
|
||||||
const elementType = unwrapZodType(baseType._def.type).baseType._def.typeName;
|
const elementType = unwrapZodType(baseType._def.type).baseType._def.typeName;
|
||||||
if (elementType === 'ZodNumber') result[key].type = 'multi_number';
|
if (elementType === 'ZodNumber') result[key].type = 'multi_number';
|
||||||
else result[key].type = 'multi_string';
|
else result[key].type = 'multi_string';
|
||||||
} else if (baseType instanceof z.ZodNumber) result[key].type = 'number';
|
} else if (baseType instanceof z.ZodNumber) result[key].type = 'number';
|
||||||
else if (baseType instanceof z.ZodBoolean) result[key].type = 'boolean';
|
else if (baseType instanceof z.ZodBoolean) result[key].type = 'boolean';
|
||||||
else if (baseType instanceof z.ZodString) result[key].type = 'string';
|
else if (baseType instanceof z.ZodString) result[key].type = 'string';
|
||||||
|
|
||||||
if (baseType.description && !result[key].description) result[key].description = baseType.description;
|
if (baseType.description && !result[key].description) result[key].description = baseType.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,21 +17,21 @@ export const FiltersPolicyOptsSchema = z.object({
|
||||||
filters: z.array(n.filter()),
|
filters: z.array(n.filter()),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface FiltersPolicyOpts extends z.TypeOf<typeof FiltersPolicyOptsSchema> { }
|
interface FiltersPolicyOpts extends z.TypeOf<typeof FiltersPolicyOptsSchema> {}
|
||||||
|
|
||||||
/** Options for `HashtagPolicy`. */
|
/** Options for `HashtagPolicy`. */
|
||||||
export const HashtagPolicyOptsSchema = z.object({
|
export const HashtagPolicyOptsSchema = z.object({
|
||||||
hashtags: z.array(z.string()).describe('Banned hashtags (case-insensitive)'),
|
hashtags: z.array(z.string()).describe('Banned hashtags (case-insensitive)'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface HashtagPolicyOpts extends z.TypeOf<typeof HashtagPolicyOptsSchema> { }
|
export interface HashtagPolicyOpts extends z.TypeOf<typeof HashtagPolicyOptsSchema> {}
|
||||||
|
|
||||||
/** Options for `WhitelistPolicy`. */
|
/** Options for `WhitelistPolicy`. */
|
||||||
export const WhitelistPolicyOptsSchema = z.object({
|
export const WhitelistPolicyOptsSchema = z.object({
|
||||||
pubkeys: z.array(z.string()).describe('Allowed pubkeys'),
|
pubkeys: z.array(z.string()).describe('Allowed pubkeys'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface WhitelistPolicyOpts extends z.TypeOf<typeof WhitelistPolicyOptsSchema> { }
|
export interface WhitelistPolicyOpts extends z.TypeOf<typeof WhitelistPolicyOptsSchema> {}
|
||||||
|
|
||||||
/** Options for `RegexPolicy`. */
|
/** Options for `RegexPolicy`. */
|
||||||
export const RegexPolicyOptsSchema = z.object({
|
export const RegexPolicyOptsSchema = z.object({
|
||||||
|
|
@ -48,7 +48,7 @@ export const KeywordPolicyOptsSchema = z.object({
|
||||||
keywords: z.array(z.string()).describe('Banned keywords (case-insensitive)'),
|
keywords: z.array(z.string()).describe('Banned keywords (case-insensitive)'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface KeywordPolicyOpts extends z.TypeOf<typeof KeywordPolicyOptsSchema> { }
|
export interface KeywordPolicyOpts extends z.TypeOf<typeof KeywordPolicyOptsSchema> {}
|
||||||
|
|
||||||
/** Options for `DomainPolicy`. */
|
/** Options for `DomainPolicy`. */
|
||||||
export const DomainPolicyOptsSchema = z.object({
|
export const DomainPolicyOptsSchema = z.object({
|
||||||
|
|
@ -66,7 +66,7 @@ export const PubkeyBanPolicyOptsSchema = z.object({
|
||||||
pubkeys: z.array(z.string()).describe('Banned pubkeys'),
|
pubkeys: z.array(z.string()).describe('Banned pubkeys'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface PubkeyBanPolicyOpts extends z.TypeOf<typeof PubkeyBanPolicyOptsSchema> { }
|
export interface PubkeyBanPolicyOpts extends z.TypeOf<typeof PubkeyBanPolicyOptsSchema> {}
|
||||||
|
|
||||||
/** Options for `OpenAIPolicy`. */
|
/** Options for `OpenAIPolicy`. */
|
||||||
export const OpenAIPolicyOptsSchema = z.object({
|
export const OpenAIPolicyOptsSchema = z.object({
|
||||||
|
|
@ -122,7 +122,7 @@ export const SizePolicyOptsSchema = z.object({
|
||||||
maxBytes: z.number().default(8 * 1024).describe('Max allowed message size in bytes'),
|
maxBytes: z.number().default(8 * 1024).describe('Max allowed message size in bytes'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface SizePolicyOpts extends Partial<z.TypeOf<typeof SizePolicyOptsSchema>> { }
|
export interface SizePolicyOpts extends Partial<z.TypeOf<typeof SizePolicyOptsSchema>> {}
|
||||||
|
|
||||||
/** Options for `AntiDuplicationPolicy`. */
|
/** Options for `AntiDuplicationPolicy`. */
|
||||||
export const AntiDuplicationPolicyOptsSchema = z.object({
|
export const AntiDuplicationPolicyOptsSchema = z.object({
|
||||||
|
|
@ -140,11 +140,11 @@ export const HellthreadPolicyOptsSchema = z.object({
|
||||||
limit: z.number().default(100).describe('Maximum number of mentions to allow per post'),
|
limit: z.number().default(100).describe('Maximum number of mentions to allow per post'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface HellthreadPolicyOpts extends Partial<z.TypeOf<typeof HellthreadPolicyOptsSchema>> { }
|
export interface HellthreadPolicyOpts extends Partial<z.TypeOf<typeof HellthreadPolicyOptsSchema>> {}
|
||||||
|
|
||||||
/** Options for `PowPolicy`. */
|
/** Options for `PowPolicy`. */
|
||||||
export const PowPolicyOptsSchema = z.object({
|
export const PowPolicyOptsSchema = z.object({
|
||||||
difficulty: z.number().default(1).describe('Number of bits of proof-of-work to require'),
|
difficulty: z.number().default(1).describe('Number of bits of proof-of-work to require'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface PowPolicyOpts extends Partial<z.TypeOf<typeof PowPolicyOptsSchema>> { }
|
export interface PowPolicyOpts extends Partial<z.TypeOf<typeof PowPolicyOptsSchema>> {}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue