Compare commits

...

98 Commits

Author SHA1 Message Date
374a8e411a 5.1.0 2025-09-12 19:30:15 +00:00
fe8b5ce7c0 feat(smartjson): Add JSONL stringify and ordering comparator; fix empty buffer handling; refactor Smartjson folding/enfolding 2025-09-12 19:30:15 +00:00
58322d23f4 5.0.21 2025-09-12 19:16:52 +00:00
2f012fc0ad fix(Smartjson): Cross-platform buffer/base64 handling, safer folding with cycle detection, parsing fixes, docs and dependency updates 2025-09-12 19:16:52 +00:00
9bb6e0b497 update description 2024-05-29 14:13:49 +02:00
5275e13360 5.0.20 2024-05-28 00:50:30 +02:00
03fe2174d1 fix(core): update 2024-05-28 00:50:30 +02:00
7df82ce64d 5.0.19 2024-04-17 20:19:18 +02:00
09b74e4053 fix(core): update 2024-04-17 20:19:17 +02:00
096cd74773 5.0.18 2024-04-17 20:11:19 +02:00
e9a45ac993 fix(core): update 2024-04-17 20:11:18 +02:00
b3856cb94a 5.0.17 2024-04-17 20:10:59 +02:00
e44d014733 fix(core): update 2024-04-17 20:10:59 +02:00
777d7ae1b3 update tsconfig 2024-04-14 17:45:36 +02:00
f1f6b5fa79 update npmextra.json: githost 2024-04-01 21:35:43 +02:00
65673e9710 update npmextra.json: githost 2024-04-01 19:58:33 +02:00
66f1094cfe update npmextra.json: githost 2024-03-30 21:47:32 +01:00
8e93b8cb73 5.0.16 2024-03-19 13:50:07 +01:00
e38ccd221a fix(core): update 2024-03-19 13:50:06 +01:00
969b892ed8 5.0.15 2024-03-19 13:30:15 +01:00
d1d679ac34 fix(core): update 2024-03-19 13:30:15 +01:00
8937dfb553 5.0.14 2024-03-03 10:35:27 +01:00
09d45b1f45 fix(core): update 2024-03-03 10:35:26 +01:00
a897f01ec6 5.0.13 2024-03-03 10:30:12 +01:00
f951e0e70d fix(core): update 2024-03-03 10:30:11 +01:00
e79a2514ef 5.0.12 2024-03-03 10:29:19 +01:00
d77c104915 fix(core): update 2024-03-03 10:29:18 +01:00
45593168b0 5.0.11 2024-02-25 01:43:07 +01:00
0227f22ca6 fix(core): update 2024-02-25 01:43:07 +01:00
cece00a9b0 5.0.10 2023-08-24 12:01:21 +02:00
e90ae7649f fix(core): update 2023-08-24 12:01:21 +02:00
70e6150708 5.0.9 2023-08-24 10:48:52 +02:00
52928e0b9b fix(core): update 2023-08-24 10:48:51 +02:00
00ae980fa7 5.0.8 2023-08-19 09:45:22 +02:00
2cd2180788 fix(core): update 2023-08-19 09:45:22 +02:00
2a37a42ed0 5.0.7 2023-08-19 09:45:01 +02:00
e88deb1b5f fix(core): update 2023-08-19 09:45:00 +02:00
05f8e6812b switch to new org scheme 2023-07-10 02:56:36 +02:00
3affd81165 5.0.6 2023-06-03 16:28:41 +02:00
7a8f959cf4 fix(core): update 2023-06-03 16:28:41 +02:00
4df1d41cc7 5.0.5 2022-11-08 11:19:20 +01:00
fb83e41eb3 fix(core): update 2022-11-08 11:19:20 +01:00
27b67b63cc 5.0.4 2022-10-26 10:56:02 +02:00
697afbaf98 fix(core): update 2022-10-26 10:56:01 +02:00
ab3ae7ec90 5.0.3 2022-10-26 10:52:15 +02:00
b2bb918ee4 fix(core): update 2022-10-26 10:52:14 +02:00
606ff73f58 5.0.2 2022-09-13 21:37:42 +02:00
7ea17c792d fix(core): update 2022-09-13 21:37:42 +02:00
4478b7588a 5.0.1 2022-06-26 11:20:39 +02:00
9bac6e5809 fix(core): update 2022-06-26 11:20:39 +02:00
cbc7e4ebce 5.0.0 2022-06-09 19:55:12 +02:00
097f54a74c BREAKING CHANGE(core): update 2022-06-09 19:55:12 +02:00
807624d157 4.0.7 2022-06-09 19:54:44 +02:00
9d7e2e03d9 fix(core): update 2022-06-09 19:54:43 +02:00
9686caff18 4.0.6 2021-01-28 02:21:24 +00:00
9260d653a1 fix(core): update 2021-01-28 02:21:23 +00:00
28cd849d0d 4.0.5 2020-10-09 09:41:16 +00:00
89a399bf42 fix(core): update 2020-10-09 09:41:15 +00:00
16048fe96d 4.0.4 2020-10-05 16:20:27 +00:00
04d60333a9 fix(core): update 2020-10-05 16:20:27 +00:00
c30f1f4ab9 4.0.3 2020-10-05 15:28:30 +00:00
fc91dd3171 fix(core): update 2020-10-05 15:28:29 +00:00
55f8951732 4.0.2 2020-10-05 12:06:14 +00:00
92f212e9d3 fix(core): update 2020-10-05 12:06:13 +00:00
1a22db26da 4.0.1 2020-10-05 11:26:07 +00:00
0d3bcab40f fix(core): update 2020-10-05 11:26:06 +00:00
e11ace6a23 4.0.0 2020-10-05 11:22:17 +00:00
f29b632bd2 BREAKING CHANGE(core): update 2020-10-05 11:22:16 +00:00
25f80b6b59 3.0.12 2020-10-05 11:13:17 +00:00
176ac34504 fix(core): update 2020-10-05 11:13:17 +00:00
2707542234 3.0.11 2020-10-03 11:20:59 +00:00
ee025d094c fix(core): update 2020-10-03 11:20:58 +00:00
9bc1c4bf93 3.0.10 2019-12-15 19:27:19 +00:00
5176a34575 3.0.9 2019-12-15 19:05:21 +00:00
fe9f9299fc fix(core): update 2019-12-15 19:05:20 +00:00
62cf2fd262 3.0.8 2019-08-25 16:03:35 +02:00
65e581d48b fix(core): update 2019-08-25 16:03:34 +02:00
4ff97294bb 3.0.7 2019-08-09 10:55:54 +02:00
2d5bd6a2f0 fix(core): update 2019-08-09 10:55:54 +02:00
a2649b7854 3.0.6 2019-08-09 10:55:17 +02:00
b901f8604b fix(core): update 2019-08-09 10:55:17 +02:00
d70310ce8b 3.0.5 2019-07-04 17:04:24 +02:00
e0e9ceed31 fix(core): update 2019-07-04 17:04:24 +02:00
e3add99efe 3.0.4 2019-07-04 17:03:15 +02:00
91d57c84f4 fix(core): update 2019-07-04 17:03:15 +02:00
37b32d5d5d 3.0.3 2019-05-05 19:10:07 +02:00
f6199740aa fix(core): update 2019-05-05 19:10:07 +02:00
215cf77205 3.0.2 2019-05-05 19:01:19 +02:00
6e2c86dbb4 fix(core): update 2019-05-05 19:01:19 +02:00
a5b9bbd376 3.0.1 2019-02-14 23:21:04 +01:00
f6a9810e71 fix(security): add snyk policy 2019-02-14 23:21:03 +01:00
32423f5475 3.0.0 2019-02-14 23:16:34 +01:00
5e945ddad6 BREAKING CHANGE(core): renamed Folable to Smartjson and added deterministic stringify 2019-02-14 23:16:34 +01:00
a9ad89d320 2.0.2 2018-09-06 00:11:46 +02:00
23e7cbd1f1 fix(core): update 2018-09-06 00:11:46 +02:00
bad3513fb5 2.0.1 2018-09-05 23:53:45 +02:00
7bd419e862 2.0.0 2018-07-24 00:38:54 +02:00
8575abf84d BREAKING CHANGE(scope): change scope 2018-07-24 00:38:54 +02:00
27 changed files with 9924 additions and 475 deletions

View File

@@ -0,0 +1,66 @@
name: Default (not tags)
on:
push:
tags-ignore:
- '**'
env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
jobs:
security:
runs-on: ubuntu-latest
continue-on-error: true
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Install pnpm and npmci
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
- name: Run npm prepare
run: npmci npm prepare
- name: Audit production dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --prod
continue-on-error: true
- name: Audit development dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --dev
continue-on-error: true
test:
if: ${{ always() }}
needs: security
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build

View File

@@ -0,0 +1,124 @@
name: Default (tags)
on:
push:
tags:
- '*'
env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
jobs:
security:
runs-on: ubuntu-latest
continue-on-error: true
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Audit production dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --prod
continue-on-error: true
- name: Audit development dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --dev
continue-on-error: true
test:
if: ${{ always() }}
needs: security
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build
release:
needs: test
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Release
run: |
npmci node install stable
npmci npm publish
metadata:
needs: test
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
continue-on-error: true
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Code quality
run: |
npmci command npm install -g typescript
npmci npm install
- name: Trigger
run: npmci trigger
- name: Build docs and upload artifacts
run: |
npmci node install stable
npmci npm install
pnpm install -g @git.zone/tsdoc
npmci command tsdoc
continue-on-error: true

18
.gitignore vendored
View File

@@ -1,4 +1,20 @@
node_modules/ .nogit/
# artifacts
coverage/ coverage/
public/ public/
pages/ pages/
# installs
node_modules/
# caches
.yarn/
.cache/
.rpt2_cache
# builds
dist/
dist_*/
# custom

View File

@@ -1,63 +0,0 @@
# gitzone standard
image: hosttoday/ht-docker-node:npmts
stages:
- test
- release
- trigger
- pages
testLEGACY:
stage: test
script:
- npmci test legacy
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
allow_failure: true
testLTS:
stage: test
script:
- npmci test lts
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
testSTABLE:
stage: test
script:
- npmci test stable
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
release:
stage: release
script:
- npmci publish
only:
- tags
tags:
- docker
trigger:
stage: trigger
script:
- npmci trigger
only:
- tags
tags:
- docker
pages:
image: hosttoday/ht-docker-node:npmpage
stage: pages
script:
- npmci command npmpage --publish gitlab
only:
- tags
artifacts:
expire_in: 1 week
paths:
- public

11
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "npm test",
"name": "Run npm test",
"request": "launch",
"type": "node-terminal"
}
]
}

26
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,26 @@
{
"json.schemas": [
{
"fileMatch": ["/npmextra.json"],
"schema": {
"type": "object",
"properties": {
"npmci": {
"type": "object",
"description": "settings for npmci"
},
"gitzone": {
"type": "object",
"description": "settings for gitzone",
"properties": {
"projectType": {
"type": "string",
"enum": ["website", "element", "service", "npm", "wcc"]
}
}
}
}
}
}
]
}

View File

@@ -1,51 +0,0 @@
# smartjson
typed json handlers
## Availabililty
[![npm](https://pushrocks.gitlab.io/assets/repo-button-npm.svg)](https://www.npmjs.com/package/smartjson)
[![git](https://pushrocks.gitlab.io/assets/repo-button-git.svg)](https://GitLab.com/pushrocks/smartjson)
[![git](https://pushrocks.gitlab.io/assets/repo-button-mirror.svg)](https://github.com/pushrocks/smartjson)
[![docs](https://pushrocks.gitlab.io/assets/repo-button-docs.svg)](https://pushrocks.gitlab.io/smartjson/)
## Status for master
[![build status](https://GitLab.com/pushrocks/smartjson/badges/master/build.svg)](https://GitLab.com/pushrocks/smartjson/commits/master)
[![coverage report](https://GitLab.com/pushrocks/smartjson/badges/master/coverage.svg)](https://GitLab.com/pushrocks/smartjson/commits/master)
[![npm downloads per month](https://img.shields.io/npm/dm/smartjson.svg)](https://www.npmjs.com/package/smartjson)
[![Dependency Status](https://david-dm.org/pushrocks/smartjson.svg)](https://david-dm.org/pushrocks/smartjson)
[![bitHound Dependencies](https://www.bithound.io/github/pushrocks/smartjson/badges/dependencies.svg)](https://www.bithound.io/github/pushrocks/smartjson/master/dependencies/npm)
[![bitHound Code](https://www.bithound.io/github/pushrocks/smartjson/badges/code.svg)](https://www.bithound.io/github/pushrocks/smartjson)
[![TypeScript](https://img.shields.io/badge/TypeScript-2.x-blue.svg)](https://nodejs.org/dist/latest-v6.x/docs/api/)
[![node](https://img.shields.io/badge/node->=%206.x.x-blue.svg)](https://nodejs.org/dist/latest-v6.x/docs/api/)
[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)
## Usage
Use TypeScript for best in class instellisense.
### Classes
SmartJson makes it easy to fold and enfold classes into and from JSON
```javascript
import { Foldable, foldDec } from 'smartjson'
class AwesomeClass extends Foldable {
computedValue: string
@foldDec() // mark anotherValueToStore as foldable
anotherValueToStore: string = null
constructor () {
super() // this is important
}
}
let myAwesomeInstance = new AwesomeClass()
let foldedObject = myAwesomeInstance.foldToObject() // will return {anotherValueToStore: null}
myAwesomeInstance.enfoldFromObject({anotherValueToStore: 'hi'})
foldedObject = myAwesomeInstance.foldToObject() // will return {anotherValueToStore: 'hi'}
```
For further information read the linked docs at the top of this README.
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh)
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy.html)
[![repo-footer](https://pushrocks.gitlab.io/assets/repo-footer.svg)](https://push.rocks)

217
changelog.md Normal file
View File

@@ -0,0 +1,217 @@
# Changelog
## 2025-09-12 - 5.1.0 - feat(smartjson)
Add JSONL stringify and ordering comparator; fix empty buffer handling; refactor Smartjson folding/enfolding
- Export stringifyJsonL(items: any[]) and add README documentation for JSONL stringification
- stringify(obj, simpleOrderArray) now derives a stable-json comparator from simpleOrderArray when no custom cmp is provided, ensuring predictable key ordering
- Fix buffer handling: empty Uint8Array values are preserved (no longer serialized to an empty string) and encoding/decoding logic improved
- Refactor Smartjson.enfoldFromObject to safely use saveableProperties and avoid repeated property access
- Simplify Smartjson.foldToObject to delegate to an internal foldToObjectInternal with cycle detection and correct nested instance handling
- Add unit tests for empty buffers, JSONL parse/stringify, deepEqualJsonLStrings, and simpleOrderArray comparator behavior
## 2025-09-12 - 5.0.21 - fix(Smartjson)
Cross-platform buffer/base64 handling, safer folding with cycle detection, parsing fixes, docs and dependency updates
- bufferhandling: prefer Node Buffer for base64 encode/decode when available and fall back to browser APIs for cross-platform support
- parseJsonL: more robust parsing of JSON Lines (trim lines and use smartjson.parse to restore buffers/typed arrays)
- parseBase64: use smartstring.base64.decodeUri or decode fallback before parsing, then parse result via smartjson.parse
- Smartjson class: add TypeScript generics to enfold methods, introduce foldToObjectInternal and robust cycle detection using Set, properly handle arrays and deep-clone values
- Tests: updated tap import path to @git.zone/tstest/tapbundle
- package.json: bumped several devDependencies and dependency versions and added packageManager pin
- Documentation: expanded README with examples, API reference and usage guidance
- Repo config: added pnpm-workspace.yaml and .claude/settings.local.json
## 2024-05-29 - 5.0.20 - docs
Update package description.
- Update package description text.
## 2024-05-27 - 5.0.19 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2024-04-17 - 5.0.18 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2024-04-17 - 5.0.17 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2024-04-17 - 5.0.16 - build/core
Build/config updates and core fixes.
- Updated tsconfig configuration (2024-04-14).
- Updated npmextra.json githost entries (multiple updates in early April).
- Miscellaneous core updates and fixes.
## 2024-03-19 - 5.0.15 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2024-03-19 - 5.0.14 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2024-03-03 - 5.0.13 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2024-03-03 - 5.0.12 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2024-03-03 - 5.0.11 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2024-02-25 - 5.0.10 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2023-08-24 - 5.0.9 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2023-08-19 - 5.0.8 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2023-08-19 - 5.0.7 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2023-07-10 - 5.0.6 - infra/core
Organization and core updates.
- Switched to new organization scheme.
- Miscellaneous core updates and fixes.
## 2023-06-03 - 5.0.5 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2022-10-26 - 5.0.4 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2022-10-26 - 5.0.3 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2022-10-26 - 5.0.2 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2022-09-13 - 5.0.1 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2022-06-26 - 5.0.0 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2022-06-09 - 4.0.7 - core (BREAKING CHANGE)
Breaking change in core.
- BREAKING CHANGE: core-related update (see code/compat notes).
- Miscellaneous core updates.
## 2022-06-09 - 4.0.6 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2020-10-05 - 4.0.4 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2020-10-05 - 4.0.3 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2020-10-05 - 4.0.2 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2020-10-05 - 4.0.1 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2020-10-05 - 4.0.0 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2020-10-05 - 3.0.12 - core (BREAKING CHANGE)
Breaking change in core.
- BREAKING CHANGE: core-related update (see migration notes).
- Miscellaneous core updates.
## 2020-10-03 - 3.0.11 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2020-10-03 - 3.0.10 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2019-02-14 - 3.0.0 - security
Security-related update.
- Added snyk policy for security scanning.
## 2019-02-14 - 2.0.2 - core (BREAKING CHANGE)
Breaking change in core API.
- BREAKING CHANGE: renamed Folable to Smartjson and added deterministic stringify.
- Update consumers accordingly.
## 2018-09-05 - 2.0.1 - core
Core fixes.
- Miscellaneous core updates and fixes.
## 2018-07-23 - 1.0.1 - scope (BREAKING CHANGE)
Scope change.
- BREAKING CHANGE: changed package scope.
- See migration notes for scope changes.
## 2017-02-27 - 1.0.0 - docs/init
Initial release.
- Added README.
- Initial project files and setup.
## 2018-07-23 — 2019-12-15 - 2.0.0, 3.0.1, 3.0.9 - version-only releases
Version-only tags / non-descriptive releases.
- These releases are recorded as version bumps without additional changelog details.
- Affected tags: 2.0.0, 3.0.1, 3.0.9.

16
dist/index.d.ts vendored
View File

@@ -1,16 +0,0 @@
import 'typings-global';
export declare class Foldable {
saveableProperties: string[];
/**
* folds a class into an object
*/
foldToObject(): {};
/**
* enfolds data from an object
*/
enfoldFromObject(objectArg: any): void;
}
/**
* Decorator that marks a property as foldable
*/
export declare let foldDec: () => (target: any, key: string) => void;

39
dist/index.js vendored
View File

@@ -1,39 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
require("typings-global");
let lodash = require('lodash');
class Foldable {
/**
* folds a class into an object
*/
foldToObject() {
let newFoldedObject = {};
for (let keyName of this.saveableProperties) {
newFoldedObject[keyName] = lodash.cloneDeep(this[keyName]);
}
return newFoldedObject;
}
/**
* enfolds data from an object
*/
enfoldFromObject(objectArg) {
for (let keyName in objectArg) {
if (this.saveableProperties.indexOf(keyName) !== -1) {
this[keyName] = objectArg[keyName];
}
}
}
}
exports.Foldable = Foldable;
/**
* Decorator that marks a property as foldable
*/
exports.foldDec = () => {
return (target, key) => {
if (!target.saveableProperties) {
target.saveableProperties = [];
}
target.saveableProperties.push(key);
};
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLDBCQUF1QjtBQUN2QixJQUFJLE1BQU0sR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUE7QUFFOUI7SUFHRTs7T0FFRztJQUNILFlBQVk7UUFDVixJQUFJLGVBQWUsR0FBRyxFQUFFLENBQUE7UUFDeEIsR0FBRyxDQUFDLENBQUMsSUFBSSxPQUFPLElBQUksSUFBSSxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQztZQUM1QyxlQUFlLENBQUUsT0FBTyxDQUFFLEdBQUcsTUFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUUsT0FBTyxDQUFFLENBQUMsQ0FBQTtRQUNoRSxDQUFDO1FBQ0QsTUFBTSxDQUFDLGVBQWUsQ0FBQTtJQUN4QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxnQkFBZ0IsQ0FBQyxTQUFTO1FBQ3hCLEdBQUcsQ0FBQyxDQUFDLElBQUksT0FBTyxJQUFJLFNBQVMsQ0FBQyxDQUFDLENBQUM7WUFDOUIsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ3BELElBQUksQ0FBRSxPQUFPLENBQUUsR0FBRyxTQUFTLENBQUUsT0FBTyxDQUFFLENBQUE7WUFDeEMsQ0FBQztRQUNILENBQUM7SUFDSCxDQUFDO0NBQ0Y7QUF4QkQsNEJBd0JDO0FBRUQ7O0dBRUc7QUFDUSxRQUFBLE9BQU8sR0FBRztJQUNuQixNQUFNLENBQUMsQ0FBQyxNQUFXLEVBQUUsR0FBVztRQUM5QixFQUFFLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUM7WUFBQyxNQUFNLENBQUMsa0JBQWtCLEdBQUcsRUFBRSxDQUFBO1FBQUMsQ0FBQztRQUNsRSxNQUFNLENBQUMsa0JBQWtCLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFBO0lBQ3JDLENBQUMsQ0FBQTtBQUNILENBQUMsQ0FBQSJ9

32
npmextra.json Normal file
View File

@@ -0,0 +1,32 @@
{
"npmci": {
"npmAccessLevel": "public"
},
"gitzone": {
"projectType": "npm",
"module": {
"githost": "code.foss.global",
"gitscope": "push.rocks",
"gitrepo": "smartjson",
"description": "A library for handling typed JSON data, providing functionalities for parsing, stringifying, and working with JSON objects, including support for encoding and decoding buffers.",
"npmPackagename": "@push.rocks/smartjson",
"license": "MIT",
"keywords": [
"JSON handling",
"typed JSON",
"JSON parsing",
"JSON stringifying",
"base64 encoding",
"buffer handling",
"data serialization",
"data deserialization",
"TypeScript support",
"data encoding",
"data decoding"
]
}
},
"tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
}
}

View File

@@ -1,28 +1,65 @@
{ {
"name": "smartjson", "name": "@push.rocks/smartjson",
"version": "1.0.1", "version": "5.1.0",
"description": "typed json handlers", "private": false,
"main": "dist/index.js", "description": "A library for handling typed JSON data, providing functionalities for parsing, stringifying, and working with JSON objects, including support for encoding and decoding buffers.",
"typings": "dist/index.d.ts", "main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts",
"scripts": { "scripts": {
"test": "(npmts)" "test": "(tstest test/ --web)",
"build": "(tsbuild --web --allowimplicitany)",
"buildDocs": "tsdoc"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+ssh://git@gitlab.com/pushrocks/smartjson.git" "url": "https://code.foss.global/push.rocks/smartjson.git"
}, },
"author": "Lossless GmbH", "author": "Lossless GmbH",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://gitlab.com/pushrocks/smartjson/issues" "url": "https://gitlab.com/pushrocks/smartjson/issues"
}, },
"homepage": "https://gitlab.com/pushrocks/smartjson#README", "homepage": "https://code.foss.global/push.rocks/smartjson",
"devDependencies": { "devDependencies": {
"smartchai": "^1.0.3", "@git.zone/tsbuild": "^2.6.8",
"typings-test": "^1.0.3" "@git.zone/tsrun": "^1.3.3",
"@git.zone/tstest": "^2.3.8",
"@types/node": "^22"
}, },
"dependencies": { "dependencies": {
"lodash": "^4.17.4", "@push.rocks/smartenv": "^5.0.13",
"typings-global": "^1.0.14" "@push.rocks/smartstring": "^4.1.0",
} "fast-json-stable-stringify": "^2.1.0",
"lodash.clonedeep": "^4.5.0"
},
"files": [
"ts/**/*",
"ts_web/**/*",
"dist/**/*",
"dist_*/**/*",
"dist_ts/**/*",
"dist_ts_web/**/*",
"assets/**/*",
"cli.js",
"npmextra.json",
"readme.md"
],
"browserslist": [
"last 1 chrome versions"
],
"type": "module",
"keywords": [
"JSON handling",
"typed JSON",
"JSON parsing",
"JSON stringifying",
"base64 encoding",
"buffer handling",
"data serialization",
"data deserialization",
"TypeScript support",
"data encoding",
"data decoding"
],
"packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748"
} }

8563
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

3
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,3 @@
onlyBuiltDependencies:
- esbuild
- puppeteer

1
readme.hints.md Normal file
View File

@@ -0,0 +1 @@

376
readme.md Normal file
View File

@@ -0,0 +1,376 @@
# @push.rocks/smartjson
**🚀 Typed JSON handling for modern Node.js and TypeScript applications**
A powerful library for working with JSON in TypeScript, providing type-safe serialization, advanced buffer handling, deep object comparison, and support for complex class instances. Perfect for applications that need reliable JSON manipulation with full TypeScript support.
## Installation
```bash
# Using npm
npm install @push.rocks/smartjson --save
# Using yarn
yarn add @push.rocks/smartjson
# Using pnpm (recommended)
pnpm add @push.rocks/smartjson
```
## Features
**Type-Safe JSON Operations** - Full TypeScript support with proper typing
🎯 **Class Instance Serialization** - Fold and unfold class instances to/from JSON
🔐 **Buffer & Binary Support** - Seamless handling of Buffers and Typed Arrays
📊 **JSON Lines Support** - Parse and compare JSONL data streams
🎨 **Pretty Printing** - Beautiful formatted JSON output
**Stable Stringification** - Consistent key ordering for reliable comparisons
🔍 **Deep Equality Checks** - Compare complex objects and JSON structures
🌐 **Base64 Encoding** - Built-in base64 JSON encoding/decoding
## Quick Start
```typescript
import * as smartjson from '@push.rocks/smartjson';
// Parse JSON with automatic buffer handling
const parsed = smartjson.parse('{"name":"example","data":{"type":"Buffer","data":[1,2,3]}}');
// Stringify with stable key ordering
const json = smartjson.stringify({ z: 1, a: 2, m: 3 });
// Result: '{"a":2,"m":3,"z":1}'
// Pretty print for human readability
const pretty = smartjson.stringifyPretty({ hello: 'world', count: 42 });
// Result:
// {
// "hello": "world",
// "count": 42
// }
```
## Core Functions
### JSON Parsing and Stringification
```typescript
// Standard parsing with automatic Buffer detection
const obj = smartjson.parse('{"hello":"world"}');
// Stable stringification (consistent key ordering)
const jsonStr = smartjson.stringify({ name: 'test', id: 1 });
// Pretty printing for debugging
const prettyJson = smartjson.stringifyPretty({
nested: {
data: 'value'
}
});
```
### Base64 JSON Encoding
Encode JSON data as base64 for safe transmission:
```typescript
const myData = {
message: 'Hello World',
timestamp: Date.now()
};
// Encode to base64
const encoded = smartjson.stringifyBase64(myData);
console.log(encoded); // Base64 string
// Decode back to object
const decoded = smartjson.parseBase64(encoded);
console.log(decoded); // Original object
```
### JSON Lines (JSONL) Support
Perfect for streaming data and log processing:
```typescript
// Parse JSON Lines format
const jsonLines = `{"event":"start","time":1234}
{"event":"data","value":42}
{"event":"end","time":5678}`;
const events = smartjson.parseJsonL(jsonLines);
// Result: Array of parsed objects
// Produce JSONL from objects
const jsonlOut = smartjson.stringifyJsonL([
{ event: 'start', time: 1234 },
{ event: 'data', value: 42 },
{ event: 'end', time: 5678 }
]);
// Compare JSON Lines data
const jsonL1 = `{"id":1}\n{"id":2}`;
const jsonL2 = `{"id":1}\n{"id":2}`;
const isEqual = smartjson.deepEqualJsonLStrings(jsonL1, jsonL2); // true
```
## Advanced Class Serialization
### Creating Serializable Classes
Transform class instances to JSON and back while preserving type safety:
```typescript
import { Smartjson, foldDec } from '@push.rocks/smartjson';
class User extends Smartjson {
@foldDec() public username: string;
@foldDec() public email: string;
@foldDec() public settings: UserSettings;
// Properties without @foldDec won't be serialized
private internalId: string;
constructor(username: string, email: string) {
super();
this.username = username;
this.email = email;
this.settings = new UserSettings();
this.internalId = Math.random().toString();
}
}
class UserSettings extends Smartjson {
@foldDec() public theme: 'light' | 'dark' = 'light';
@foldDec() public notifications: boolean = true;
}
// Create and serialize
const user = new User('john_doe', 'john@example.com');
user.settings.theme = 'dark';
// Convert to JSON
const jsonString = user.foldToJson();
console.log(jsonString);
// {"username":"john_doe","email":"john@example.com","settings":{"theme":"dark","notifications":true}}
// Restore from JSON with correct typing
const restoredUser = User.enfoldFromJson(jsonString);
console.log(restoredUser instanceof User); // true
console.log(restoredUser.settings instanceof UserSettings); // true
```
### Working with Nested Objects
```typescript
class Company extends Smartjson {
@foldDec() public name: string;
@foldDec() public employees: Employee[] = [];
addEmployee(employee: Employee) {
this.employees.push(employee);
}
}
class Employee extends Smartjson {
@foldDec() public name: string;
@foldDec() public role: string;
@foldDec() public salary: number;
constructor(name: string, role: string, salary: number) {
super();
this.name = name;
this.role = role;
this.salary = salary;
}
}
const company = new Company();
company.name = 'TechCorp';
company.addEmployee(new Employee('Alice', 'Developer', 100000));
company.addEmployee(new Employee('Bob', 'Designer', 90000));
// Serialize entire object graph
const json = company.foldToJson();
// Deserialize with all nested objects properly instantiated
const restored = Company.enfoldFromJson(json);
```
## Buffer and Binary Data Handling
SmartJson seamlessly handles binary data in JSON:
```typescript
// Automatic Buffer handling
const dataWithBuffer = {
name: 'BinaryData',
buffer: Buffer.from('Hello World'),
typedArray: new Uint8Array([1, 2, 3, 4, 5])
};
// Stringify (buffers are automatically encoded)
const jsonStr = smartjson.stringify(dataWithBuffer);
// Parse (buffers are automatically restored)
const restored = smartjson.parse(jsonStr);
console.log(restored.buffer); // Buffer
console.log(restored.typedArray); // Uint8Array
```
## Deep Comparison
Compare complex objects with automatic normalization:
```typescript
// Deep object comparison
const obj1 = {
nested: {
array: [1, 2, { deep: 'value' }],
flag: true
},
name: 'test'
};
const obj2 = {
name: 'test', // Different order
nested: {
flag: true, // Different order
array: [1, 2, { deep: 'value' }]
}
};
const isEqual = smartjson.deepEqualObjects(obj1, obj2); // true
```
## Real-World Examples
### API Response Caching
```typescript
class CachedAPIResponse extends Smartjson {
@foldDec() public data: any;
@foldDec() public timestamp: number;
@foldDec() public endpoint: string;
isExpired(maxAge: number = 3600000): boolean {
return Date.now() - this.timestamp > maxAge;
}
static fromAPICall(endpoint: string, data: any): CachedAPIResponse {
const response = new CachedAPIResponse();
response.endpoint = endpoint;
response.data = data;
response.timestamp = Date.now();
return response;
}
}
// Store API response
const apiData = await fetch('/api/users');
const cached = CachedAPIResponse.fromAPICall('/api/users', await apiData.json());
localStorage.setItem('cached_users', cached.foldToJson());
// Retrieve and check
const stored = localStorage.getItem('cached_users');
if (stored) {
const cached = CachedAPIResponse.enfoldFromJson(stored);
if (!cached.isExpired()) {
return cached.data; // Use cached data
}
}
```
### Configuration Management
```typescript
class AppConfig extends Smartjson {
@foldDec() public apiUrl: string;
@foldDec() public features: Map<string, boolean> = new Map();
@foldDec() public limits: {
maxUploadSize: number;
maxConcurrentRequests: number;
};
enableFeature(name: string) {
this.features.set(name, true);
}
save() {
fs.writeFileSync('config.json', this.foldToJson());
}
static load(): AppConfig {
const json = fs.readFileSync('config.json', 'utf-8');
return AppConfig.enfoldFromJson(json);
}
}
```
## API Reference
### Core Functions
- `parse(jsonString: string): any` - Parse JSON with automatic buffer handling
- `stringify(obj: any, simpleOrderArray?: string[], options?: Options): string` - Convert to JSON with stable ordering
- `stringifyPretty(obj: any): string` - Pretty print JSON with 2-space indentation
- `stringifyBase64(obj: any): string` - Encode JSON as base64
- `parseBase64(base64String: string): any` - Decode base64 JSON
- `parseJsonL(jsonLinesString: string): any[]` - Parse JSON Lines format
- `stringifyJsonL(items: any[]): string` - Stringify array to JSON Lines
- `deepEqualObjects(obj1: any, obj2: any): boolean` - Deep comparison of objects
- `deepEqualJsonLStrings(jsonL1: string, jsonL2: string): boolean` - Compare JSON Lines strings
### Smartjson Class
- `Smartjson.enfoldFromObject<T>(obj: any): T` - Create instance from plain object
- `Smartjson.enfoldFromJson<T>(json: string): T` - Create instance from JSON string
- `instance.foldToObject(): any` - Convert instance to plain object
- `instance.foldToJson(): string` - Convert instance to JSON string
### Decorators
- `@foldDec()` - Mark class property for serialization
## Performance Tips
1. **Use stable stringification** for consistent hashing and comparison
2. **Enable pretty printing** only for debugging (it's slower)
3. **Cache base64 encodings** when repeatedly sending the same data
4. **Use JSON Lines** for streaming large datasets
5. **Avoid circular references** in objects being serialized
## Migration Guide
If you're migrating from native JSON:
```typescript
// Before
JSON.parse(jsonString);
JSON.stringify(object);
// After
smartjson.parse(jsonString); // Adds buffer support
smartjson.stringify(object); // Adds stable ordering & buffer support
```
## Browser Support
This library supports modern browsers and Node.js environments. For older browsers, ensure you have appropriate polyfills for `TextEncoder` and `TextDecoder`.
## License and Legal Information
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
### Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
### Company Information
Task Venture Capital GmbH
Registered at District court Bremen HRB 35230 HB, Germany
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

107
test/test.both.ts Normal file
View File

@@ -0,0 +1,107 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as smartjson from '../ts/index.js';
class SomeClass extends smartjson.Smartjson {
@smartjson.foldDec() thisis: string = 'test';
constructor() {
super();
console.log(this.saveableProperties);
}
}
let mySomeClass: SomeClass;
tap.test('should create a Foldable extended instance', async () => {
mySomeClass = new SomeClass();
expect(mySomeClass).toBeInstanceOf(SomeClass);
expect(mySomeClass).toBeInstanceOf(smartjson.Smartjson);
});
tap.test('should create a folded object', async () => {
let foldedObject = mySomeClass.foldToObject();
expect(foldedObject).property('thisis').toEqual('test');
});
tap.test('should enfold from object', async () => {
const mySomeClass2 = SomeClass.enfoldFromObject({ thisis: 'test2' });
expect(mySomeClass2).property('thisis').toEqual('test2');
});
tap.test('should products stable jsons', async () => {
const jsonString = smartjson.stringify({
a: 1,
f: 6,
b: 3,
c: 3,
e: 5,
d: 4,
});
console.log(jsonString);
});
tap.test('should work with base64', async () => {
const someObject = {
hi: 'there',
thisIs: 'awesome',
};
const base64Json = smartjson.stringifyBase64(someObject);
console.log(base64Json);
const decodedObject = smartjson.parseBase64(base64Json);
expect(decodedObject).toEqual(someObject);
});
tap.test('stringify should handle plain string', async () => {
const stringifiedString = smartjson.stringify('hello');
console.log(stringifiedString);
expect(stringifiedString).toEqual('"hello"');
expect(smartjson.parse(stringifiedString)).toEqual('hello');
});
tap.test('should work with buffers', async () => {
const someObject = {
myBuffer: new TextEncoder().encode('hello')
};
console.log(someObject.myBuffer);
const stringified = smartjson.stringify(someObject);
console.log(stringified);
const decoded = smartjson.parse(stringified);
console.log(decoded.myBuffer);
let text = new TextDecoder().decode(decoded.myBuffer);
expect(text).toEqual('hello');
});
tap.test('should handle empty buffers', async () => {
const someObject = { empty: new Uint8Array([]) };
const json = smartjson.stringify(someObject);
const parsed = smartjson.parse(json);
expect(parsed.empty).toBeInstanceOf(Uint8Array);
expect(parsed.empty.length).toEqual(0);
});
tap.test('should parse and stringify JSONL', async () => {
const items = [
{ id: 1, name: 'a' },
{ id: 2, name: 'b' }
];
const jsonl = smartjson.stringifyJsonL(items);
const parsed = smartjson.parseJsonL(jsonl);
expect(parsed).toEqual(items);
});
tap.test('should deep-compare JSONL strings', async () => {
const a = '{"id":2,"name":"b"}\n{"id":1,"name":"a"}';
const b = '{"id":2,"name":"b"}\n{"id":1,"name":"a"}';
expect(smartjson.deepEqualJsonLStrings(a, b)).toEqual(true);
});
tap.test('should respect simpleOrderArray comparator', async () => {
const obj = { c: 3, a: 1, b: 2 };
const ordered = smartjson.stringify(obj, ['b', 'a']);
// ensure keys b, a come before c
expect(ordered.indexOf('"b"')).toBeLessThan(ordered.indexOf('"a"'));
expect(ordered.indexOf('"a"')).toBeLessThan(ordered.indexOf('"c"'));
});
tap.start();

1
test/test.d.ts vendored
View File

@@ -1 +0,0 @@
import 'typings-test';

View File

@@ -1,42 +0,0 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
require("typings-test");
const smartchai_1 = require("smartchai");
const index_1 = require("../dist/index");
class SomeClass extends index_1.Foldable {
constructor() {
super();
this.thisis = 'test';
console.log(this.saveableProperties);
}
}
__decorate([
index_1.foldDec(),
__metadata("design:type", String)
], SomeClass.prototype, "thisis", void 0);
let mySomeClass;
describe('smartjson', function () {
it('should create a Foldable extended instance', function () {
mySomeClass = new SomeClass();
smartchai_1.expect(mySomeClass).to.be.instanceof(SomeClass);
smartchai_1.expect(mySomeClass).to.be.instanceof(index_1.Foldable);
});
it('should create a folded object', function () {
let foldedObject = mySomeClass.foldToObject();
smartchai_1.expect(foldedObject).property('thisis').to.equal('test');
});
it('should enfold from object', function () {
mySomeClass.enfoldFromObject({ thisis: 'test2' });
smartchai_1.expect(mySomeClass).property('thisis').to.equal('test2');
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7QUFBQSx3QkFBcUI7QUFDckIseUNBQWtDO0FBRWxDLHlDQUFpRDtBQUVqRCxlQUFnQixTQUFRLGdCQUFRO0lBRzlCO1FBQ0UsS0FBSyxFQUFFLENBQUE7UUFGVCxXQUFNLEdBQVcsTUFBTSxDQUFBO1FBR3JCLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLGtCQUFrQixDQUFDLENBQUE7SUFDdEMsQ0FBQztDQUNGO0FBTEM7SUFEQyxlQUFPLEVBQUU7O3lDQUNhO0FBT3pCLElBQUksV0FBc0IsQ0FBQTtBQUUxQixRQUFRLENBQUMsV0FBVyxFQUFFO0lBQ3BCLEVBQUUsQ0FBQyw0Q0FBNEMsRUFBRTtRQUMvQyxXQUFXLEdBQUcsSUFBSSxTQUFTLEVBQUUsQ0FBQTtRQUM3QixrQkFBTSxDQUFDLFdBQVcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLFNBQVMsQ0FBQyxDQUFBO1FBQy9DLGtCQUFNLENBQUMsV0FBVyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsZ0JBQVEsQ0FBQyxDQUFBO0lBQ2hELENBQUMsQ0FBQyxDQUFBO0lBRUYsRUFBRSxDQUFDLCtCQUErQixFQUFFO1FBQ2xDLElBQUksWUFBWSxHQUFHLFdBQVcsQ0FBQyxZQUFZLEVBQUUsQ0FBQTtRQUM3QyxrQkFBTSxDQUFDLFlBQVksQ0FBQyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFBO0lBQzFELENBQUMsQ0FBQyxDQUFBO0lBRUYsRUFBRSxDQUFDLDJCQUEyQixFQUFFO1FBQzlCLFdBQVcsQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUMsQ0FBQyxDQUFBO1FBQy9DLGtCQUFNLENBQUMsV0FBVyxDQUFDLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUE7SUFDMUQsQ0FBQyxDQUFDLENBQUE7QUFDSixDQUFDLENBQUMsQ0FBQSJ9

View File

@@ -1,33 +0,0 @@
import 'typings-test'
import { expect } from 'smartchai'
import { Foldable, foldDec } from '../dist/index'
class SomeClass extends Foldable {
@foldDec()
thisis: string = 'test'
constructor () {
super()
console.log(this.saveableProperties)
}
}
let mySomeClass: SomeClass
describe('smartjson', function () {
it('should create a Foldable extended instance', function () {
mySomeClass = new SomeClass()
expect(mySomeClass).to.be.instanceof(SomeClass)
expect(mySomeClass).to.be.instanceof(Foldable)
})
it('should create a folded object', function () {
let foldedObject = mySomeClass.foldToObject()
expect(foldedObject).property('thisis').to.equal('test')
})
it('should enfold from object', function () {
mySomeClass.enfoldFromObject({thisis: 'test2'})
expect(mySomeClass).property('thisis').to.equal('test2')
})
})

8
ts/00_commitinfo_data.ts Normal file
View File

@@ -0,0 +1,8 @@
/**
* autocreated commitinfo by @push.rocks/commitinfo
*/
export const commitinfo = {
name: '@push.rocks/smartjson',
version: '5.1.0',
description: 'A library for handling typed JSON data, providing functionalities for parsing, stringifying, and working with JSON objects, including support for encoding and decoding buffers.'
}

130
ts/bufferhandling.ts Normal file
View File

@@ -0,0 +1,130 @@
import * as plugins from './smartjson.plugins.js';
// Define interfaces and types for better type checking and readability
interface IBufferLike {
type: 'Buffer';
data: string | any[]; // `any[]` for array data representation
}
interface IEncodedBuffer {
type: 'EncodedBuffer',
data: string;
}
type TParseReviver = (this: any, key: string, value: any) => any;
type TParseReplacer = (this: any, key: string, value: any) => any;
// Utility functions to handle base64 encoding/decoding in a cross-platform way
function base64Encode(data: Uint8Array): string {
// Prefer Node's Buffer when available
if (typeof Buffer !== 'undefined') {
// @ts-ignore Buffer might not exist in browser builds
return Buffer.from(data).toString('base64');
}
// Fallback for browsers
return btoa(String.fromCharCode(...data));
}
function base64Decode(str: string): Uint8Array {
// Prefer Node's Buffer when available
if (typeof Buffer !== 'undefined') {
// @ts-ignore Buffer might not exist in browser builds
const buf = Buffer.from(str, 'base64');
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
}
// Fallback for browsers
return new Uint8Array(Array.from(atob(str)).map((char) => char.charCodeAt(0)));
}
// Main functionality with cross-platform support
function stringify(value: any, space?: string | number): string {
return JSON.stringify(value, replacer, space);
}
function parse(text: string): any {
return JSON.parse(text, reviver);
}
const replacer: TParseReplacer = (key, value) => {
// Check if value is IBufferLike
if (isBufferLike(value)) {
let bufferData: Uint8Array;
// Handle IBufferLike objects with a .data property
if ('data' in value && isArray(value.data)) {
bufferData = new Uint8Array(value.data);
}
// Handle Uint8Array directly
else if (value instanceof Uint8Array) {
bufferData = value;
} else {
// If not a recognized format, return value as is
return value;
}
// Encode the bufferData (Uint8Array) to base64
const base64Data = 'base64:' + base64Encode(bufferData);
return {
type: 'EncodedBuffer',
data: base64Data,
};
}
// Return value unchanged if not buffer-like
return value;
};
const reviver: TParseReviver = (key, value) => {
if (isEncodedBuffer(value)) {
if (isString(value.data) && value.data.startsWith('base64:')) {
// Correctly slice the 'base64:' prefix before decoding
const base64Data = value.data.slice(7); // Skip 'base64:' prefix
const buffer = base64Decode(base64Data);
// Assuming the rest of your application can work directly with Uint8Array,
// otherwise, you might need to convert it to another format
return buffer;
}
}
return value;
};
function isEncodedBuffer(x: any): x is IEncodedBuffer {
return isObject(x) && (x as any).type === 'EncodedBuffer' && isString((x as any).data);
}
function isBufferLike(x: any): x is IBufferLike | Uint8Array {
return (
(isObject(x) &&
((x as any).type === 'Buffer' &&
(isArray((x as any).data) || isString((x as any).data)))) ||
x instanceof Uint8Array
);
}
/**
* We use this function to check if a value is an array
* @param x
* @returns
*/
function isArray(x: any): x is any[] {
return Array.isArray(x);
}
/**
* We use this function to check if a value is a string
* @param x
* @returns
*/
function isString(x: any): x is string {
return typeof x === 'string';
}
/**
* We use this function to check if a value is an object
* @param x
*/
function isObject(x: any): x is object {
return typeof x === 'object' && x !== null;
}
export { stringify, parse, replacer, reviver };

View File

@@ -1,38 +1,168 @@
import 'typings-global' import * as plugins from './smartjson.plugins.js';
let lodash = require('lodash') import * as bufferhandling from './bufferhandling.js';
export class Foldable { interface JsonObject {
saveableProperties: string[] [key: string]: any;
}
/**
* allows you to parse a json
*/
export const parse = bufferhandling.parse;
export const parseJsonL = (jsonlData: string): JsonObject[] => {
const lines = jsonlData.split('\n');
const parsedData: JsonObject[] = lines.reduce((acc, line) => {
const trimmed = line.trim();
if (trimmed.length > 0) {
acc.push(parse(trimmed));
}
return acc;
}, [] as JsonObject[]);
return parsedData;
}
export const stringifyJsonL = (items: any[]): string => {
return items.map((item) => stringify(item)).join('\n');
}
/**
*
* @param objArg
* @param optionsArg
*/
export const stringify = (
objArg: any,
simpleOrderArray?: string[],
optionsArg: plugins.IStableJsonTypes['Options'] = {}
): string => {
const bufferedJson = bufferhandling.stringify(objArg);
objArg = JSON.parse(bufferedJson);
// derive a simple comparator from simpleOrderArray if provided and no custom cmp supplied
let options = { ...optionsArg };
if (simpleOrderArray && !options.cmp) {
const order = new Map<string, number>();
simpleOrderArray.forEach((key, idx) => order.set(key, idx));
options.cmp = (a, b) => {
const aIdx = order.has(a.key) ? (order.get(a.key) as number) : Number.POSITIVE_INFINITY;
const bIdx = order.has(b.key) ? (order.get(b.key) as number) : Number.POSITIVE_INFINITY;
if (aIdx !== bIdx) return aIdx - bIdx;
// fallback to lexicographic order for stable behavior
return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
};
}
let returnJson = plugins.stableJson(objArg, options);
return returnJson;
};
export const stringifyPretty = (objectArg: any) => {
const stringified = stringify(objectArg);
const object = JSON.parse(stringified);
return JSON.stringify(object, null, 2);
}
export const stringifyBase64 = (...args: Parameters<typeof stringify>): string => {
const stringifiedResult = stringify(...args);
return plugins.smartstring.base64.encodeUri(stringifiedResult);
};
export const parseBase64 = (base64JsonStringArg: string) => {
const base64 = plugins.smartstring.base64 as any;
const decodeFn: (input: string) => string = base64.decodeUri || base64.decode;
const simpleStringified = decodeFn(base64JsonStringArg);
return parse(simpleStringified);
};
export class Smartjson {
/**
* enfolds data from an object
*/
public static enfoldFromObject<T extends typeof Smartjson>(this: T, objectArg: any): InstanceType<T> {
const newInstance = new this() as InstanceType<T>;
const saveables: string[] = (newInstance as any).saveableProperties || [];
for (const keyName in objectArg) {
if (saveables.indexOf(keyName) !== -1) {
(newInstance as any)[keyName] = objectArg[keyName];
}
}
return newInstance;
}
/**
* enfold from json
*/
public static enfoldFromJson<T extends typeof Smartjson>(this: T, jsonArg: string): InstanceType<T> {
const objectFromJson = parse(jsonArg);
return this.enfoldFromObject(objectFromJson);
}
// ========
// INSTANCE
// ========
public saveableProperties: string[];
/** /**
* folds a class into an object * folds a class into an object
*/ */
foldToObject () { public foldToObject() {
let newFoldedObject = {} const trackSet = new Set<Smartjson>();
for (let keyName of this.saveableProperties) { trackSet.add(this);
newFoldedObject[ keyName ] = lodash.cloneDeep(this[ keyName ]) return this.foldToObjectInternal(trackSet);
}
private foldToObjectInternal(trackSet: Set<Smartjson>) {
const result: { [key: string]: any } = {};
const foldValue = (val: any): any => {
if (val instanceof Smartjson) {
if (trackSet.has(val)) {
throw new Error('cycle detected');
}
trackSet.add(val);
return val.foldToObjectInternal(trackSet);
}
if (Array.isArray(val)) {
return val.map((item) => foldValue(item));
}
return plugins.lodashCloneDeep(val);
};
const props: string[] = (this as any).saveableProperties || [];
for (const keyName of props) {
const value = this[keyName];
result[keyName] = foldValue(value);
} }
return newFoldedObject return result;
} }
/** /**
* enfolds data from an object * folds a class into an object
*/ */
enfoldFromObject(objectArg) { public foldToJson() {
for (let keyName in objectArg) { const foldedObject = this.foldToObject();
if (this.saveableProperties.indexOf(keyName) !== -1) { return stringify(foldedObject);
this[ keyName ] = objectArg[ keyName ]
}
}
} }
} }
/** /**
* Decorator that marks a property as foldable * Decorator that marks a property as foldable
*/ */
export let foldDec = () => { export const foldDec = () => {
return (target: any, key: string) => { return (target: any, key: string) => {
if (!target.saveableProperties) { target.saveableProperties = [] } if (!target.saveableProperties) {
target.saveableProperties.push(key) target.saveableProperties = [];
} }
target.saveableProperties.push(key);
};
};
export const deepEqualObjects = (object1: any, object2: any): boolean => {
const object1String = stringify(object1);
const object2String = stringify(object2);
return object1String === object2String;
};
export const deepEqualJsonLStrings = (jsonLString1: string, jsonLString2: string): boolean => {
const firstArray = parseJsonL(jsonLString1);
const secondArray = parseJsonL(jsonLString2);
return deepEqualObjects(firstArray, secondArray);
} }

31
ts/smartjson.plugins.ts Normal file
View File

@@ -0,0 +1,31 @@
// @pushrocks scope
import * as smartenv from '@push.rocks/smartenv';
import * as smartstring from '@push.rocks/smartstring';
export { smartenv, smartstring };
// third party scope
import lodashCloneDeep from 'lodash.clonedeep';
import stableJson2 from 'fast-json-stable-stringify';
const stableJson = stableJson2 as any;
export { lodashCloneDeep, stableJson };
export interface IStableJsonTypes {
Comparator: (
a: IStableJsonTypes['CompareDescriptor'],
b: IStableJsonTypes['CompareDescriptor']
) => number;
CompareDescriptor: {
key: string;
value: any;
};
Options: {
cmp?: (
a: IStableJsonTypes['CompareDescriptor'],
b: IStableJsonTypes['CompareDescriptor']
) => number;
cycles?: boolean;
};
}

View File

@@ -1,5 +1,14 @@
{ {
"compilerOptions": { "compilerOptions": {
"experimentalDecorators": true "experimentalDecorators": true,
} "useDefineForClassFields": false,
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"verbatimModuleSyntax": true
},
"exclude": [
"dist_*/**/*.d.ts"
]
} }

View File

@@ -1,3 +0,0 @@
{
"extends": "tslint-config-standard"
}

190
yarn.lock
View File

@@ -1,190 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/chai-as-promised@0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/chai-as-promised/-/chai-as-promised-0.0.29.tgz#43d52892aa998e185a3de3e2477edb8573be1d77"
dependencies:
"@types/chai" "*"
"@types/promises-a-plus" "*"
"@types/chai-string@^1.1.30":
version "1.1.30"
resolved "https://registry.yarnpkg.com/@types/chai-string/-/chai-string-1.1.30.tgz#4d8744b31a5a2295fc01c981ed1e2d4c8a070f0a"
dependencies:
"@types/chai" "*"
"@types/chai@*", "@types/chai@^3.4.35":
version "3.4.35"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-3.4.35.tgz#e8d65f83492d2944f816fc620741821c28a8c900"
"@types/mocha@^2.2.31":
version "2.2.39"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.39.tgz#f68d63db8b69c38e9558b4073525cf96c4f7a829"
"@types/promises-a-plus@*":
version "0.0.27"
resolved "https://registry.yarnpkg.com/@types/promises-a-plus/-/promises-a-plus-0.0.27.tgz#c64651134614c84b8f5d7114ce8901d36a609780"
assertion-error@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c"
balanced-match@^0.4.1:
version "0.4.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
brace-expansion@^1.0.0:
version "1.1.6"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9"
dependencies:
balanced-match "^0.4.1"
concat-map "0.0.1"
chai-as-promised@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-6.0.0.tgz#1a02a433a6f24dafac63b9c96fa1684db1aa8da6"
dependencies:
check-error "^1.0.2"
chai-string@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/chai-string/-/chai-string-1.3.0.tgz#df6139f294391b1035be5606f60a843b3a5041e7"
chai@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/chai/-/chai-3.5.0.tgz#4d02637b067fe958bdbfdd3a40ec56fef7373247"
dependencies:
assertion-error "^1.0.1"
deep-eql "^0.1.3"
type-detect "^1.0.0"
check-error@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
deep-eql@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2"
dependencies:
type-detect "0.1.1"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
glob@^7.0.0:
version "7.1.1"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.2"
once "^1.3.0"
path-is-absolute "^1.0.0"
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
interpret@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c"
lodash@^4.17.4:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
minimatch@^3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774"
dependencies:
brace-expansion "^1.0.0"
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
dependencies:
wrappy "1"
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
path-parse@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
rechoir@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
dependencies:
resolve "^1.1.6"
resolve@^1.1.6:
version "1.3.2"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.2.tgz#1f0442c9e0cbb8136e87b9305f932f46c7f28235"
dependencies:
path-parse "^1.0.5"
semver@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
shelljs@^0.7.4:
version "0.7.6"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.6.tgz#379cccfb56b91c8601e4793356eb5382924de9ad"
dependencies:
glob "^7.0.0"
interpret "^1.0.0"
rechoir "^0.6.2"
smartchai@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/smartchai/-/smartchai-1.0.3.tgz#de6d010bb8b5aef24cb70b31a5f5334e8c41b72f"
dependencies:
"@types/chai" "^3.4.35"
"@types/chai-as-promised" "0.0.29"
"@types/chai-string" "^1.1.30"
chai "^3.5.0"
chai-as-promised "^6.0.0"
chai-string "^1.3.0"
type-detect@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822"
type-detect@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2"
typings-global@*, typings-global@^1.0.14:
version "1.0.14"
resolved "https://registry.yarnpkg.com/typings-global/-/typings-global-1.0.14.tgz#ab682720a03d6b9278869fb5c30c30d7dc61d12c"
dependencies:
semver "^5.3.0"
shelljs "^0.7.4"
typings-test@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/typings-test/-/typings-test-1.0.3.tgz#fbab895eb3f0c44842e73db059f65946b971e369"
dependencies:
"@types/mocha" "^2.2.31"
typings-global "*"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"