@plasius/schema 1.0.9 β†’ 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,8 +2,6 @@ name: CD (Publish to npm)
2
2
 
3
3
  on:
4
4
  workflow_dispatch:
5
- release:
6
- types: [published]
7
5
 
8
6
  permissions:
9
7
  contents: write
@@ -12,7 +10,7 @@ permissions:
12
10
  jobs:
13
11
  publish:
14
12
  runs-on: ubuntu-latest
15
- environment: npm
13
+ environment: production
16
14
  steps:
17
15
  - name: Checkout
18
16
  uses: actions/checkout@v4
@@ -41,6 +39,11 @@ jobs:
41
39
  echo "New version: $NEW_VER"
42
40
  git push --follow-tags
43
41
 
42
+ # Expose tag (vX.Y.Z) and version (X.Y.Z) for later steps
43
+ VER_NO_V=${NEW_VER#v}
44
+ echo "tag=$NEW_VER" >> "$GITHUB_OUTPUT"
45
+ echo "version=$VER_NO_V" >> "$GITHUB_OUTPUT"
46
+
44
47
  NAME=$(node -p "require('./package.json').name")
45
48
  echo "name=$NAME" >> "$GITHUB_OUTPUT"
46
49
  if npm view "$NAME" version >/dev/null 2>&1; then
@@ -49,6 +52,21 @@ jobs:
49
52
  echo "flags=--access public" >> "$GITHUB_OUTPUT"
50
53
  fi
51
54
 
55
+ - name: Create GitHub Release from tag (first-party)
56
+ env:
57
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
58
+ run: |
59
+ set -euo pipefail
60
+ TAG="${{ steps.pkg.outputs.tag }}"
61
+ if gh release view "$TAG" >/dev/null 2>&1; then
62
+ echo "Release $TAG already exists; skipping creation."
63
+ else
64
+ gh release create "$TAG" \
65
+ --title "Release $TAG" \
66
+ --generate-notes \
67
+ --latest
68
+ fi
69
+
52
70
  - name: Publish
53
71
  env:
54
72
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -6,7 +6,7 @@
6
6
  "request": "launch",
7
7
  "name": "Debug Vitest",
8
8
  "program": "${workspaceFolder}/node_modules/vitest/vitest.mjs",
9
- "args": ["run", "--dir", "tests"],
9
+ "args": ["run"],
10
10
  "cwd": "${workspaceFolder}",
11
11
  "console": "integratedTerminal",
12
12
  "skipFiles": ["<node_internals>/**"]
package/CHANGELOG.md ADDED
@@ -0,0 +1,65 @@
1
+
2
+ # Changelog
3
+
4
+ All notable changes to this project will be documented in this file.
5
+
6
+ The format is based on **[Keep a Changelog](https://keepachangelog.com/en/1.1.0/)**, and this project adheres to **[Semantic Versioning](https://semver.org/spec/v2.0.0.html)**.
7
+
8
+ ---
9
+
10
+ ## [Unreleased]
11
+
12
+ - **Added**
13
+ - (placeholder) Add new validators, field helpers, or PII utilities here.
14
+
15
+ - **Changed**
16
+ - ./src/schema.ts Added comments defining functionality on all externally facing functions.
17
+
18
+ - **Fixed**
19
+ - ./src/schema.ts Validation no longer mutates the input, internal system fields are set only on result if not previously present.
20
+
21
+ - **Security**
22
+ - (placeholder)
23
+
24
+ ---
25
+
26
+ ## [1.0.0] - 2025-09-16
27
+
28
+ - **Added**
29
+ - Initial public release of `@plasius/schema`.
30
+ - Fluent field builder API: `field().string().required()`, `field().number().min()`, etc.
31
+ - Type inference utilities to derive TypeScript types from schema definitions.
32
+ - Built-in validators for common standards:
33
+ - ISO-3166 country codes
34
+ - ISO-4217 currency codes
35
+ - RFC 5322 email format
36
+ - E.164 phone format
37
+ - WHATWG URL format
38
+ - ISO 8601 date/time
39
+ - OWASP-guided text/name constraints
40
+ - UUID (RFC 4122) and SemVer 2.0.0
41
+ - PII annotations and helpers for redaction/masking before logging.
42
+ - Lightweight validation runner with success/error result types.
43
+
44
+ - **Changed**
45
+ - N/A (initial release)
46
+
47
+ - **Fixed**
48
+ - N/A (initial release)
49
+
50
+ ---
51
+
52
+ ## Release process (maintainers)
53
+
54
+ 1. Update `CHANGELOG.md` under **Unreleased** with user‑visible changes.
55
+ 2. Bump version in `package.json` following SemVer (major/minor/patch).
56
+ 3. Move entries from **Unreleased** to a new version section with the current date.
57
+ 4. Tag the release in Git (`vX.Y.Z`) and push tags.
58
+ 5. Publish to npm (via CI/CD or `npm publish`).
59
+
60
+ > Tip: Use Conventional Commits in PR titles/bodies to make changelog updates easier.
61
+
62
+ ---
63
+
64
+ [Unreleased]: https://github.com/Plasius-LTD/plasius-schema/compare/v1.0.0...HEAD
65
+ [1.0.0]: https://github.com/Plasius-LTD/plasius-schema/releases/tag/v1.0.0
package/README.md CHANGED
@@ -48,7 +48,7 @@ This ensures your local development environment matches the version used in CI/C
48
48
  We welcome contributions! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
49
49
 
50
50
  - [Code of Conduct](./CODE_OF_CONDUCT.md)
51
- - [Contributor License Agreement](./CLA.md)
51
+ - [Contributor License Agreement](./legal/CLA.md)
52
52
 
53
53
  ---
54
54
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plasius/schema",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "Entity schema definition & validation helpers for Plasius ecosystem",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
package/src/schema.ts CHANGED
@@ -500,15 +500,14 @@ export function createSchema<S extends SchemaShape>(
500
500
  if (typeof input !== "object" || input === null) {
501
501
  return { valid: false, errors: ["Input must be an object"] } as any;
502
502
  }
503
-
504
- if (!(input as any).type || !(input as any).version) {
505
- (input as any).type = entityType;
506
- (input as any).version = version;
507
- }
503
+ // Work on a non-mutating copy that includes system defaults for first-time objects
504
+ const working: Record<string, any> = { ...(input as any) };
505
+ if (working.type == null) working.type = entityType;
506
+ if (working.version == null) working.version = version;
508
507
 
509
508
  for (const key in schema._shape) {
510
509
  const def = schema._shape[key];
511
- const value = (input as any)[key];
510
+ const value = working[key];
512
511
 
513
512
  if (!def) {
514
513
  errors.push(`Field definition missing for: ${key}`);
@@ -553,6 +552,7 @@ export function createSchema<S extends SchemaShape>(
553
552
  result[key] = value;
554
553
  }
555
554
 
555
+
556
556
  if (errors.length === 0 && options.schemaValidator) {
557
557
  const castValue = result as Infer<S>;
558
558
  if (!options.schemaValidator(castValue)) {
@@ -570,7 +570,28 @@ export function createSchema<S extends SchemaShape>(
570
570
  // specific validator for a schema to allow conditional validation
571
571
  schemaValidator: options.schemaValidator!, // <== expose it here!
572
572
 
573
- // πŸ”— Validate composition (references) recursively
573
+ /**
574
+ * Recursively validates entity references defined in this schema.
575
+ *
576
+ * Traverses fields of type `ref` and arrays of `ref` and resolves each target
577
+ * entity using the provided `resolveEntity` function. When `autoValidate` is
578
+ * enabled (default) and the field's `refPolicy` is `eager`, the referenced
579
+ * entity's schema is fetched and validated via `validateComposition` up to
580
+ * `maxDepth` levels.
581
+ *
582
+ * Skips fields not listed in `onlyFields` when provided. Prevents cycles via
583
+ * a `visited` set in `validatorContext`.
584
+ *
585
+ * @param entity The root entity to validate (must include `type` and `id`).
586
+ * @param options Options controlling traversal and resolution behavior.
587
+ * @param options.resolveEntity Function to resolve a referenced entity by type and id.
588
+ * @param options.validatorContext Internal context (visited set) to prevent cycles.
589
+ * @param options.maxDepth Maximum depth for recursive validation (default: 5).
590
+ * @param options.onlyFields Optional whitelist of field names to validate.
591
+ * @param options.log Optional logger for traversal/debug output.
592
+ *
593
+ * @throws Error if a broken reference is encountered (target cannot be resolved).
594
+ */
574
595
  async validateComposition(entity, options) {
575
596
  const {
576
597
  resolveEntity,
@@ -643,6 +664,11 @@ export function createSchema<S extends SchemaShape>(
643
664
  }
644
665
  },
645
666
 
667
+ /**
668
+ * Returns the configured table name for this schema.
669
+ *
670
+ * @throws Error if no store/table name has been defined for this schema.
671
+ */
646
672
  tableName(): string {
647
673
  if (!store || store === "") {
648
674
  throw new Error("Store is not defined for this schema");
@@ -650,7 +676,15 @@ export function createSchema<S extends SchemaShape>(
650
676
  return store;
651
677
  },
652
678
 
653
- // πŸ”’ Auto-prepare for storage (encrypt/hash PII)
679
+ /**
680
+ * Transforms an input object for persistence by applying PII protection
681
+ * according to field annotations (e.g., encryption and hashing).
682
+ *
683
+ * @param input The raw entity data.
684
+ * @param encryptFn Function used to encrypt sensitive values.
685
+ * @param hashFn Function used to hash sensitive values.
686
+ * @returns A new object safe to store.
687
+ */
654
688
  prepareForStorage(
655
689
  input: Record<string, any>,
656
690
  encryptFn: (value: any) => string,
@@ -659,6 +693,14 @@ export function createSchema<S extends SchemaShape>(
659
693
  return piiPrepareForStorage(_shape, input, encryptFn, hashFn);
660
694
  },
661
695
 
696
+ /**
697
+ * Reverses storage transformations for read paths (e.g., decrypts values)
698
+ * according to PII annotations, returning a consumer-friendly object.
699
+ *
700
+ * @param stored Data retrieved from storage.
701
+ * @param decryptFn Function used to decrypt values that were encrypted on write.
702
+ * @returns A new object suitable for application consumption.
703
+ */
662
704
  prepareForRead(
663
705
  stored: Record<string, any>,
664
706
  decryptFn: (value: string) => any
@@ -666,7 +708,14 @@ export function createSchema<S extends SchemaShape>(
666
708
  return piiPrepareForRead(_shape, stored, decryptFn);
667
709
  },
668
710
 
669
- // πŸ” Sanitize for logging (redact/pseudonymize PII)
711
+ /**
712
+ * Produces a log-safe copy of the provided data by redacting or pseudonymizing
713
+ * PII fields in accordance with field annotations.
714
+ *
715
+ * @param data Arbitrary data to sanitize for logging.
716
+ * @param pseudonymFn Function producing stable pseudonyms for sensitive values.
717
+ * @returns A copy safe to emit to logs.
718
+ */
670
719
  sanitizeForLog(
671
720
  data: Record<string, any>,
672
721
  pseudonymFn: (value: any) => string
@@ -674,6 +723,10 @@ export function createSchema<S extends SchemaShape>(
674
723
  return piiSanitizeForLog(_shape, data, pseudonymFn);
675
724
  },
676
725
 
726
+ /**
727
+ * Returns a list of fields annotated with PII metadata for auditing purposes.
728
+ * Each entry includes classification, required action, and optional log policy.
729
+ */
677
730
  getPiiAudit(): Array<{
678
731
  field: string;
679
732
  classification: PIIClassification;
@@ -684,10 +737,21 @@ export function createSchema<S extends SchemaShape>(
684
737
  return piiGetPiiAudit(_shape);
685
738
  },
686
739
 
740
+ /**
741
+ * Produces a copy of stored data suitable for data deletion flows by scrubbing
742
+ * or blanking PII per field annotations.
743
+ *
744
+ * @param stored Data as persisted.
745
+ * @returns A copy with PII removed or neutralized for deletion.
746
+ */
687
747
  scrubPiiForDelete(stored: Record<string, any>): Record<string, any> {
688
748
  return piiScrubPiiForDelete(_shape, stored);
689
749
  },
690
750
 
751
+ /**
752
+ * Returns a normalized description of the schema suitable for documentation
753
+ * or UI rendering (type, optionality, enum values, PII flags, etc.).
754
+ */
691
755
  describe() {
692
756
  const description: Record<string, any> = {};
693
757
  for (const [key, def] of Object.entries(schema._shape)) {
@@ -715,19 +779,24 @@ export function createSchema<S extends SchemaShape>(
715
779
  return schema;
716
780
  }
717
781
 
718
- // πŸ”— Retrieve a previously registered schema globally
782
+ /**
783
+ * Retrieves a previously registered schema by its `entityType` from the
784
+ * in-process global schema registry.
785
+ */
719
786
  export function getSchemaForType(type: string): Schema<any> | undefined {
720
787
  return globalSchemaRegistry.get(type);
721
788
  }
722
789
 
723
- // πŸ”— Retrieve all registered schemas globally
790
+ /**
791
+ * Returns all schemas registered in the in-process global registry.
792
+ */
724
793
  export function getAllSchemas(): Schema<any>[] {
725
794
  return Array.from(globalSchemaRegistry.values());
726
795
  }
727
796
 
728
797
  /**
729
- * Renders a schema description to a simplified frontend-consumable format.
730
- * This can be used in UIs for schema explorers, documentation, or admin tools.
798
+ * Renders a schema into a simplified descriptor for front-end consumption.
799
+ * Intended for documentation and admin tooling rather than validation.
731
800
  */
732
801
  export function renderSchemaDescription(
733
802
  schema: Schema<any>