-
Notifications
You must be signed in to change notification settings - Fork 479
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
48 changed files
with
6,321 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
|
||
dist/* | ||
**/dist/* | ||
!**/dist | ||
!**/dist/.gitkeep | ||
|
||
**/node_modules | ||
**/packages/**/*.ts | ||
**/src/serialize.ts | ||
**/src/types.ts | ||
**/*.code-workspace |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
allOf: | ||
- $ref: "nips/nip-01/messages/filter/schema.yaml" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
allOf: | ||
- $ref: "nips/nip-01/note/schema.yaml" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
allOf: | ||
- $ref: "nips/nip-01/secp256k1/schema.yaml" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
allOf: | ||
- $ref: "nips/nip-01/tag/schema.yaml" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
allOf: | ||
- $ref: "nips/nip-01/tag/a/schema.yaml" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
allOf: | ||
- $ref: "nips/nip-01/tag/e/schema.yaml" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
allOf: | ||
- $ref: "nips/nip-01/tag/p/schema.yaml" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
allOf: | ||
- $ref: "nips/nip-01/tag/a/schema.yaml" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
allOf: | ||
- $ref: "nips/nip-01/tag/d/schema.yaml" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
allOf: | ||
- $ref: "nips/nip-01/tag/e/schema.yaml" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
allOf: | ||
- $ref: "nips/nip-01/tag/p/schema.yaml" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
allOf: | ||
- $ref: "nips/nip-01/tag/t/schema.yaml" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# Makefile that converts all *.yaml in nips/ and @ -> dist/, | ||
# rewrites references to absolute paths, then dereferences them. | ||
|
||
NIPS_DIR := nips | ||
ALIASES_DIR := @ | ||
DIST_DIR := dist | ||
|
||
YAMLCONVERT := yaml-convert | ||
REWRITE_SCRIPT := node $(realpath scripts/rewriteRefs.js) | ||
DEREF_SCRIPT := node $(realpath scripts/deref.js) | ||
|
||
SCHEMA_YAMLS := $(shell find $(NIPS_DIR) -type f -name "*.yaml") | ||
ALIAS_YAMLS := $(shell find $(ALIASES_DIR) -type f -name "*.yaml") | ||
ALL_YAMLS := $(SCHEMA_YAMLS) $(ALIAS_YAMLS) | ||
|
||
JSON_SCHEMAS := $(patsubst %.yaml,$(DIST_DIR)/%.json,$(ALL_YAMLS)) | ||
|
||
all: convert_json rewrite_refs dereference_json | ||
|
||
$(DIST_DIR)/%.json: %.yaml | ||
@echo "Converting $< -> $@" | ||
@mkdir -p $(dir $@) | ||
@$(YAMLCONVERT) $< > $@ | ||
@sed -i '' 's/\.yaml/.json/g' $@ || sed -i 's/\.yaml/.json/g' $@ | ||
|
||
.PHONY: convert_json | ||
convert_json: FORCE $(JSON_SCHEMAS) | ||
|
||
.PHONY: rewrite_refs | ||
rewrite_refs: convert_json | ||
@echo "Rewriting references in dist/..." | ||
@cd $(DIST_DIR) && \ | ||
for f in $(patsubst $(DIST_DIR)/%,%,$(JSON_SCHEMAS)); do \ | ||
echo " -> rewriting $$f"; \ | ||
$(REWRITE_SCRIPT) $$f $$f; \ | ||
done | ||
|
||
.PHONY: dereference_json | ||
dereference_json: rewrite_refs | ||
@echo "Dereferencing JSON schemas in dist/..." | ||
@cd $(DIST_DIR) && \ | ||
for f in $(patsubst $(DIST_DIR)/%,%,$(JSON_SCHEMAS)); do \ | ||
echo " -> dereferencing $$f"; \ | ||
$(DEREF_SCRIPT) $$f $$f; \ | ||
done | ||
|
||
.PHONY: clean | ||
clean: | ||
@rm -rf $(DIST_DIR) | ||
|
||
.PHONY: FORCE | ||
FORCE: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# nostrability schemata | ||
|
||
A template for simplifying the validation of nostr events, their attriobutes and their respective tags using JSON-Schema standards. | ||
|
||
## Usage | ||
1. Download ZIP file (all languages) or include package (js only for now) | ||
2. Validate `.json` schemas against nostr events. | ||
|
||
## Use in your own pipline | ||
You shouldn't. You should write a wrapper or use one that already exists. Wrappers **must** use the following typings. | ||
``` | ||
type NSchemaResult [ boolean, NSchemaMessage[] ] | ||
interface NSchemaMessage { | ||
level: "info" | "warning" | "error" | ||
message: string | ||
} | ||
``` | ||
|
||
And provide the following interface: | ||
``` | ||
validate(event: NostrEvent): NSchemaResult | ||
validateMany(events: NostrEvent[]): NSchemaResult[] | ||
``` | ||
|
||
## Contribute | ||
|
||
### Setup | ||
1. Fork the repo. | ||
2. `npm/yarn/pnpm install` | ||
3. `npm/yarn/pnpm build` | ||
4. `npm/yarn/pnpm test` | ||
|
||
## Writing Schemas | ||
Familiarize yourself with the aliases section and the file structure. | ||
1. Create a new directory for your NIP, and a directory for each kind. | ||
...this is going to be annoying to write, so it should probbaly be automated. | ||
|
||
## FS Conventions | ||
This toolkit uses path conventions for build and testing schemata. | ||
|
||
`nips/nip-XY/kind-N/schema.yaml` | ||
|
||
Kinds are assumed to belong to a NIP, but if you are working with an experimental kind, you won't have a NIP. For these situations, simply place the kind into a nipless "nipless" | ||
|
||
`nips/nipless/kind-X` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
/** | ||
* build.js | ||
* | ||
* 1) Finds all .json in dist/@ and dist/nips. | ||
* 2) Builds named exports in dist/bundle/schemas.js with custom logic: | ||
* - If parent (or grandparent) is "tag": | ||
* * File named "_A" => "ATagSchema", "_P" => "PTagSchema", etc. | ||
* * Single letter like "a" => "aTagSchema" | ||
* * "schema.json" in "tag/" => "tagSchema" | ||
* - If file is dist/@/note.json => "noteSchema", etc. | ||
* - If file base is "schema" => skip it, to avoid "SchemaSchema". | ||
* - If file base starts with "schema." => remove "schema." => uppercase first letter of remainder. | ||
* - Remove invalid chars (like @, -). | ||
* 3) Outputs dist/bundle/schemas.js + .d.ts, runs esbuild => dist/bundle/schemas.bundle.js | ||
*/ | ||
|
||
import esbuild from 'esbuild'; | ||
import { | ||
readdirSync, | ||
statSync, | ||
writeFileSync, | ||
mkdirSync, | ||
existsSync | ||
} from 'fs'; | ||
import { resolve, join, basename, dirname } from 'path'; | ||
|
||
const aliasDir = resolve('dist/@'); | ||
const nipsDir = resolve('dist/nips'); | ||
const bundleDir = resolve('dist/bundle'); | ||
|
||
function getAllJsonFiles(dir) { | ||
if (!existsSync(dir)) return []; | ||
const entries = readdirSync(dir, { withFileTypes: true }); | ||
return entries.flatMap((ent) => { | ||
const full = join(dir, ent.name); | ||
if (ent.isDirectory()) { | ||
return getAllJsonFiles(full); | ||
} | ||
if (ent.name.endsWith('.json')) { | ||
return [full]; | ||
} | ||
return []; | ||
}); | ||
} | ||
|
||
/** | ||
* Remove invalid chars like "@", "-", etc. so we produce a valid JS identifier. | ||
*/ | ||
function sanitize(str) { | ||
return str.replace(/[^a-zA-Z0-9]/g, ''); | ||
} | ||
|
||
/** | ||
* "kind-3" => "kind3", "client-req" => "clientReq" | ||
*/ | ||
function camelCaseHyphens(str) { | ||
const parts = str.split('-').filter(Boolean); | ||
if (!parts.length) return ''; | ||
const [first, ...rest] = parts; | ||
const lowered = first.toLowerCase(); | ||
const appended = rest | ||
.map(s => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase()) | ||
.join(''); | ||
return lowered + appended; | ||
} | ||
|
||
/** | ||
* If file base is exactly "schema", we remove it (avoid "SchemaSchema"). | ||
* If it starts with "schema.", remove "schema." => uppercase the remainder's first letter. | ||
*/ | ||
function processBaseName(baseName) { | ||
if (baseName === 'schema') { | ||
return ''; | ||
} | ||
if (baseName.startsWith('schema.')) { | ||
const after = baseName.slice('schema.'.length); | ||
return after.charAt(0).toUpperCase() + after.slice(1); | ||
} | ||
return baseName; | ||
} | ||
|
||
/** | ||
* If the folder path includes "tag", we do special logic: | ||
* - If file is "_A" => "ATagSchema" | ||
* - If file is "p" => "pTagSchema" | ||
* - If file is "schema" => "tagSchema" | ||
*/ | ||
function handleTagCase(dirParts, baseName) { | ||
const lastDir = dirParts[dirParts.length - 1] || ''; | ||
const secondLast = dirParts[dirParts.length - 2] || ''; | ||
|
||
if (lastDir === 'tag' && baseName === 'schema') { | ||
return 'tagSchema'; | ||
} | ||
if (secondLast === 'tag') { | ||
if (baseName === 'schema') { | ||
let name = lastDir; | ||
if (name.startsWith('_') && name.length > 1) { | ||
name = name.charAt(1).toUpperCase() + name.slice(2); | ||
} | ||
return name + 'TagSchema'; | ||
} else { | ||
return ''; | ||
} | ||
} | ||
if (lastDir === 'tag' && baseName) { | ||
if (baseName.startsWith('_') && baseName.length > 1) { | ||
const letter = baseName.charAt(1).toUpperCase() + baseName.slice(2); | ||
return letter + 'TagSchema'; | ||
} | ||
return baseName + 'TagSchema'; | ||
} | ||
|
||
return ''; | ||
} | ||
|
||
/** | ||
* Generate exports for dist/nips | ||
*/ | ||
function generateNipsExports(filePath) { | ||
const distRoot = resolve('dist'); | ||
const relativePath = filePath.replace(distRoot, '').replace(/^[\\/]/, ''); | ||
|
||
const baseName = basename(filePath, '.json'); | ||
const processed = processBaseName(baseName); | ||
|
||
const dirParts = dirname(filePath).split(/[/\\]/).filter(Boolean); | ||
|
||
const tagResult = handleTagCase(dirParts, baseName); | ||
if (tagResult) { | ||
return { exportName: sanitize(tagResult), relativePath }; | ||
} | ||
|
||
const parent = dirParts[dirParts.length - 1] || ''; | ||
const parentCased = camelCaseHyphens(parent); | ||
let combined = parentCased + processed; | ||
if (!combined) { | ||
combined = 'Unnamed'; | ||
} | ||
combined += 'Schema'; | ||
|
||
return { exportName: sanitize(combined), relativePath }; | ||
} | ||
|
||
/** | ||
* For dist/@: | ||
* If we have direct parent '@' => e.g. dist/@/note.json => "noteSchema" in lowercase | ||
* If we have a "tag" subfolder => handleTagCase | ||
*/ | ||
function generateAliasExports(filePath) { | ||
const distRoot = resolve('dist'); | ||
const relativePath = filePath.replace(distRoot, '').replace(/^[\\/]/, ''); | ||
|
||
const baseName = basename(filePath, '.json'); | ||
const processed = processBaseName(baseName); | ||
|
||
const dirParts = dirname(filePath).split(/[/\\]/).filter(Boolean); | ||
|
||
const last = dirParts[dirParts.length - 1] || ''; | ||
if (last === '@') { | ||
const final = processed.toLowerCase() + 'Schema'; | ||
return { exportName: sanitize(final), relativePath }; | ||
} | ||
|
||
const tagResult = handleTagCase(dirParts, baseName); | ||
if (tagResult) { | ||
return { exportName: sanitize(tagResult), relativePath }; | ||
} | ||
|
||
const parent = dirParts[dirParts.length - 1] || ''; | ||
const parentCased = camelCaseHyphens(parent); | ||
let combined = parentCased + processed; | ||
if (!combined) { | ||
combined = 'Unnamed'; | ||
} | ||
combined += 'Schema'; | ||
|
||
return { exportName: sanitize(combined), relativePath }; | ||
} | ||
|
||
/** | ||
* If two exports share the same name, keep the first (nips). | ||
*/ | ||
function prioritizeExports(exports) { | ||
const seen = new Set(); | ||
return exports.filter(({ exportName }) => { | ||
if (seen.has(exportName)) return false; | ||
seen.add(exportName); | ||
return true; | ||
}); | ||
} | ||
|
||
function main() { | ||
const aliasFiles = getAllJsonFiles(aliasDir); | ||
const nipsFiles = getAllJsonFiles(nipsDir); | ||
|
||
const aliasExports = aliasFiles.map(generateAliasExports); | ||
const nipsExports = nipsFiles.map(generateNipsExports); | ||
|
||
const combined = prioritizeExports([...nipsExports, ...aliasExports]); | ||
|
||
const exportLines = combined.map(({ exportName, relativePath }) => | ||
`export { default as ${exportName} } from '../${relativePath}';` | ||
); | ||
|
||
const typeLines = combined.map(({ exportName }) => | ||
`declare const ${exportName}: unknown;\nexport { ${exportName} };` | ||
); | ||
|
||
if (!existsSync(bundleDir)) { | ||
mkdirSync(bundleDir, { recursive: true }); | ||
} | ||
|
||
const schemasFile = join(bundleDir, 'schemas.js'); | ||
const dtsFile = join(bundleDir, 'schemas.d.ts'); | ||
|
||
writeFileSync(schemasFile, exportLines.join('\n')); | ||
writeFileSync(dtsFile, typeLines.join('\n')); | ||
|
||
esbuild.build({ | ||
entryPoints: [schemasFile], | ||
outfile: join(bundleDir, 'schemas.bundle.js'), | ||
format: 'esm', | ||
platform: 'node', | ||
target: 'es2020', | ||
sourcemap: true, | ||
minify: true, | ||
}) | ||
.then(() => console.log('Schemas bundled successfully!')) | ||
.catch(() => process.exit(1)); | ||
} | ||
|
||
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
$id: "https://schemata.nostr.watch/note/kind/1" | ||
$schema: http://json-schema.org/draft-07/schema# | ||
title: kind1 | ||
allOf: | ||
- $ref: "@/note.yaml" |
Oops, something went wrong.