From 87841ebd778e6b48f7763006d22e3fb6f6b577c1 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Tue, 25 Nov 2025 14:00:48 -0400 Subject: [PATCH] [WIP] Publish the standard library to NPM Signed-off-by: Juan Cruz Viotti --- .npmignore | 9 ++++++++ Makefile | 7 +++++++ npm/cjs.test.js | 38 ++++++++++++++++++++++++++++++++++ npm/esm.test.mjs | 38 ++++++++++++++++++++++++++++++++++ npm/main.js | 23 +++++++++++++++++++++ npm/main.mjs | 26 ++++++++++++++++++++++++ package.json | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 194 insertions(+) create mode 100644 .npmignore create mode 100644 npm/cjs.test.js create mode 100644 npm/esm.test.mjs create mode 100644 npm/main.js create mode 100644 npm/main.mjs create mode 100644 package.json diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..4c27a08 --- /dev/null +++ b/.npmignore @@ -0,0 +1,9 @@ +* +!LICENSE +!README.markdown +!schemas +!schemas/** +!npm +!npm/** +npm/**/*.test.js +npm/**/*.test.mjs diff --git a/Makefile b/Makefile index c13338d..566b19c 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,8 @@ UNZIP ?= unzip GZIP ?= gzip MKDIRP ?= mkdir -p RMRF ?= rm -rf +NODE ?= node +NPM ?= npm include generated.mk @@ -37,6 +39,8 @@ lint: common .PHONY: test test: $(JSONSCHEMA) test ./test + $(NODE) npm/cjs.test.js + $(NODE) npm/esm.test.mjs # TODO: Add a `jsonschema pkg` command instead .PHONY: dist @@ -51,3 +55,6 @@ dist: $(TAR) -rf $@/sourcemeta-std-v$(VERSION).tar LICENSE $(GZIP) $@/sourcemeta-std-v$(VERSION).tar $(TAR) -tzf $@/sourcemeta-std-v$(VERSION).tar.gz + $(MKDIRP) $@/npm + $(NPM) version --no-git-tag-version --allow-same-version "$(VERSION)" + $(NPM) pack --pack-destination $@/npm diff --git a/npm/cjs.test.js b/npm/cjs.test.js new file mode 100644 index 0000000..04bde41 --- /dev/null +++ b/npm/cjs.test.js @@ -0,0 +1,38 @@ +const test = require('node:test'); +const assert = require('node:assert'); +const getSchema = require('..'); + +test('loads a valid schema', () => { + const schema = getSchema('2020-12/misc/schema-like'); + assert.strictEqual(typeof schema, 'object'); + assert.strictEqual(schema.$schema, 'https://json-schema.org/draft/2020-12/schema'); + assert.strictEqual(schema.title, 'JSON Schema Document'); +}); + +test('returns null for non-existent schema', () => { + const schema = getSchema('nonexistent/schema/path'); + assert.strictEqual(schema, null); +}); + +test('returns null for invalid input types', () => { + assert.strictEqual(getSchema(null), null); + assert.strictEqual(getSchema(undefined), null); + assert.strictEqual(getSchema(123), null); + assert.strictEqual(getSchema({}), null); +}); + +test('blocks directory traversal attempts', () => { + const schema = getSchema('../package'); + assert.strictEqual(schema, null); +}); + +test('blocks directory traversal with multiple levels', () => { + const schema = getSchema('../../etc/passwd'); + assert.strictEqual(schema, null); +}); + +test('loads schema from nested path', () => { + const schema = getSchema('2020-12/w3c/xmlschema/2001/hex-binary'); + assert.strictEqual(typeof schema, 'object'); + assert.strictEqual(schema.$schema, 'https://json-schema.org/draft/2020-12/schema'); +}); diff --git a/npm/esm.test.mjs b/npm/esm.test.mjs new file mode 100644 index 0000000..26e8756 --- /dev/null +++ b/npm/esm.test.mjs @@ -0,0 +1,38 @@ +import test from 'node:test'; +import assert from 'node:assert'; +import getSchema from '@sourcemeta/std'; + +test('loads a valid schema', () => { + const schema = getSchema('2020-12/misc/schema-like'); + assert.strictEqual(typeof schema, 'object'); + assert.strictEqual(schema.$schema, 'https://json-schema.org/draft/2020-12/schema'); + assert.strictEqual(schema.title, 'JSON Schema Document'); +}); + +test('returns null for non-existent schema', () => { + const schema = getSchema('nonexistent/schema/path'); + assert.strictEqual(schema, null); +}); + +test('returns null for invalid input types', () => { + assert.strictEqual(getSchema(null), null); + assert.strictEqual(getSchema(undefined), null); + assert.strictEqual(getSchema(123), null); + assert.strictEqual(getSchema({}), null); +}); + +test('blocks directory traversal attempts', () => { + const schema = getSchema('../package'); + assert.strictEqual(schema, null); +}); + +test('blocks directory traversal with multiple levels', () => { + const schema = getSchema('../../etc/passwd'); + assert.strictEqual(schema, null); +}); + +test('loads schema from nested path', () => { + const schema = getSchema('2020-12/w3c/xmlschema/2001/hex-binary'); + assert.strictEqual(typeof schema, 'object'); + assert.strictEqual(schema.$schema, 'https://json-schema.org/draft/2020-12/schema'); +}); diff --git a/npm/main.js b/npm/main.js new file mode 100644 index 0000000..e3fa04a --- /dev/null +++ b/npm/main.js @@ -0,0 +1,23 @@ +const fs = require('fs'); +const path = require('path'); + +function getSchema(schemaPath) { + if (!schemaPath || typeof schemaPath !== 'string') { + return null; + } + + if (schemaPath.includes('..')) { + return null; + } + + const absolutePath = path.join(__dirname, '..', 'schemas', `${schemaPath}.json`); + + try { + const content = fs.readFileSync(absolutePath, 'utf8'); + return JSON.parse(content); + } catch (error) { + return null; + } +} + +module.exports = getSchema; diff --git a/npm/main.mjs b/npm/main.mjs new file mode 100644 index 0000000..913db74 --- /dev/null +++ b/npm/main.mjs @@ -0,0 +1,26 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +function getSchema(schemaPath) { + if (!schemaPath || typeof schemaPath !== 'string') { + return null; + } + + if (schemaPath.includes('..')) { + return null; + } + + const absolutePath = path.join(__dirname, '..', 'schemas', `${schemaPath}.json`); + + try { + const content = fs.readFileSync(absolutePath, 'utf8'); + return JSON.parse(content); + } catch (error) { + return null; + } +} + +export default getSchema; diff --git a/package.json b/package.json new file mode 100644 index 0000000..d5f7d66 --- /dev/null +++ b/package.json @@ -0,0 +1,53 @@ +{ + "name": "@sourcemeta/std", + "version": "0.3.0", + "description": "A growing collection of hand-crafted high-quality schemas, From RFC-compliant Email Address schemas to ISO-compliant Currency Codes", + "main": "npm/main.js", + "module": "npm/main.mjs", + "exports": { + ".": { + "import": "./npm/main.mjs", + "require": "./npm/main.js" + } + }, + "license": "UNLICENSED", + "homepage": "https://github.com/sourcemeta/std", + "author": { + "email": "hello@sourcemeta.com", + "name": "Sourcemeta", + "url": "https://www.sourcemeta.com" + }, + "funding": "https://github.com/sponsors/sourcemeta", + "keywords": [ + "jsonschema", + "json", + "schema", + "json-schema", + "cli", + "$ref", + "dereference", + "reference", + "resolve", + "json-pointer", + "validator", + "validation", + "bundle", + "json-schema-validator", + "json-schema-validation", + "lint", + "format", + "draft", + "library" + ], + "bugs": { + "url": "https://github.com/sourcemeta/std/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/sourcemeta/std.git" + }, + "publishConfig": { + "provenance": true, + "access": "public" + } +}