├── .eslintrc.js
├── .github
├── ion-test-driver-issue.md
├── no-response.yml
└── workflows
│ ├── build.yml
│ ├── codeql-analysis.yml
│ ├── ion-test-driver.yml
│ └── release.yml
├── .gitignore
├── .gitmodules
├── .nojekyll
├── .npmignore
├── .prettierignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Gruntfile.js
├── LICENSE
├── NOTICE
├── README.md
├── _config.yml
├── browser
└── index.html
├── package.json
├── src
├── AbstractWriter.ts
├── BigIntSerde.ts
├── ComparisonResult.ts
├── IntSize.ts
├── Ion.ts
├── IonBinary.ts
├── IonBinaryReader.ts
├── IonBinaryWriter.ts
├── IonCatalog.ts
├── IonConstants.ts
├── IonDecimal.ts
├── IonImport.ts
├── IonLocalSymbolTable.ts
├── IonLowLevelBinaryWriter.ts
├── IonParserBinaryRaw.ts
├── IonParserTextRaw.ts
├── IonPrettyTextWriter.ts
├── IonReader.ts
├── IonSharedSymbolTable.ts
├── IonSpan.ts
├── IonSubstituteSymbolTable.ts
├── IonSymbol.ts
├── IonSymbolIndex.ts
├── IonSymbolToken.ts
├── IonSymbols.ts
├── IonSystemSymbolTable.ts
├── IonText.ts
├── IonTextReader.ts
├── IonTextWriter.ts
├── IonTimestamp.ts
├── IonType.ts
├── IonTypes.ts
├── IonUnicode.ts
├── IonWriteable.ts
├── IonWriter.ts
├── SignAndMagnitudeInt.ts
├── dom
│ ├── Blob.ts
│ ├── Boolean.ts
│ ├── Clob.ts
│ ├── Decimal.ts
│ ├── Float.ts
│ ├── FromJsConstructor.ts
│ ├── Integer.ts
│ ├── JsValueConversion.ts
│ ├── List.ts
│ ├── Lob.ts
│ ├── Null.ts
│ ├── README.md
│ ├── SExpression.ts
│ ├── Sequence.ts
│ ├── String.ts
│ ├── Struct.ts
│ ├── Symbol.ts
│ ├── Timestamp.ts
│ ├── Value.ts
│ └── index.ts
├── events
│ ├── EventStreamError.ts
│ ├── IonEvent.ts
│ └── IonEventStream.ts
└── util.ts
├── test-driver
├── package.json
├── src
│ ├── Cli.ts
│ ├── CliCommonArgs.ts
│ ├── CliCompareArgs.ts
│ ├── CliError.ts
│ ├── Compare.ts
│ ├── ComparisonContext.ts
│ ├── ComparisonReport.ts
│ ├── OutputFormat.ts
│ └── Process.ts
└── tsconfig.json
├── test
├── AbstractWriter.ts
├── IntSize.ts
├── IonAnnotations.ts
├── IonBinaryReader.ts
├── IonBinaryTimestamp.ts
├── IonBinaryWriter.ts
├── IonCatalog.ts
├── IonDecimal.ts
├── IonImport.ts
├── IonLocalSymbolTable.ts
├── IonLowLevelBinaryWriter.ts
├── IonParserBinaryRaw.ts
├── IonParserTextRaw.ts
├── IonReaderConsistency.ts
├── IonReaderStepOutThrows.ts
├── IonText.ts
├── IonTextReader.ts
├── IonTextWriter.ts
├── IonTimestamp.ts
├── IonUnicode.ts
├── IonWriteable.ts
├── IonWriterUndefinedParameters.ts
├── dom
│ ├── Value.ts
│ ├── dom.ts
│ ├── equivalence.ts
│ ├── json.ts
│ └── propertyShadowing.ts
├── dump.ts
├── exampleValues.ts
├── iontests.ts
├── mocha.opts
├── mochaSupport.ts
├── spans.ts
├── textNulls.ts
├── util.ts
└── writeSymbolTokens.ts
├── tsconfig.amd.json
├── tsconfig.es6.json
├── tsconfig.json
├── tsconfig.lint.json
└── typedoc.json
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: "@typescript-eslint/parser",
4 | parserOptions: {
5 | project: "./tsconfig.lint.json",
6 | },
7 | plugins: [
8 | "@typescript-eslint",
9 | ],
10 | extends: [
11 | "plugin:@typescript-eslint/recommended",
12 | "plugin:@typescript-eslint/recommended-requiring-type-checking",
13 | "prettier", // This config turns off ESLint rules that are handled by Prettier.
14 | ],
15 | rules: {
16 | "@typescript-eslint/no-unused-vars": ["off"], // Not really needed, TypeScript strict checks can be used instead.
17 | // TODO Enable and fix other useful rules like `eqeqeq`.
18 |
19 | // Temporary disabled recommended rules that failed after TSLint -> ESLint migration.
20 | // TODO Enable rules back one by one and fix errors.
21 | "@typescript-eslint/ban-ts-comment": ["off"],
22 | "@typescript-eslint/ban-types": ["off"],
23 | "@typescript-eslint/explicit-module-boundary-types" : ["off"],
24 | "@typescript-eslint/no-empty-function" : ["off"],
25 | "@typescript-eslint/no-empty-interface" : ["off"],
26 | "@typescript-eslint/no-explicit-any" : ["off"],
27 | "@typescript-eslint/no-inferrable-types" : ["off"],
28 | "@typescript-eslint/no-namespace" : ["off"],
29 | "@typescript-eslint/no-non-null-assertion" : ["off"],
30 | "@typescript-eslint/no-this-alias" : ["off"],
31 | "@typescript-eslint/no-unnecessary-type-assertion": ["off"],
32 | "@typescript-eslint/no-unsafe-assignment" : ["off"],
33 | "@typescript-eslint/no-unsafe-call" : ["off"],
34 | "@typescript-eslint/no-unsafe-member-access" : ["off"],
35 | "@typescript-eslint/no-unsafe-return" : ["off"],
36 | "@typescript-eslint/prefer-regexp-exec": ["off"],
37 | "@typescript-eslint/restrict-plus-operands": ["off"],
38 | "@typescript-eslint/restrict-template-expressions": ["off"],
39 | "@typescript-eslint/unbound-method": ["off"],
40 | "prefer-const" : ["off"],
41 | },
42 | overrides: [{
43 | files: ["./test/**.*"],
44 | rules: {
45 | "@typescript-eslint/no-empty-function": ["off"],
46 | }
47 | }]
48 | };
49 |
--------------------------------------------------------------------------------
/.github/ion-test-driver-issue.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Ion-test-driver issue.
3 | labels: bug
4 | ---
5 | Ion-test-driver complained that behavior changed for the commit {{ env.GITHUB_PR_SHA }} created by `{{ payload.sender.login }}`.
6 | Refer to the [workflow]({{ env.GITHUB_WORKFLOW_URL }}) for more details.
7 |
--------------------------------------------------------------------------------
/.github/no-response.yml:
--------------------------------------------------------------------------------
1 | # Configuration for probot-no-response - https://github.com/probot/no-response
2 |
3 | # Number of days of inactivity before an Issue is closed for lack of response
4 | daysUntilClose: 14
5 | # Label requiring a response
6 | responseRequiredLabel: more-information-needed
7 | # Comment to post when closing an Issue for lack of response. Set to `false` to disable
8 | closeComment: >
9 | This issue has been automatically closed because there has been no response
10 | to our request for more information from the original author. With only the
11 | information that is currently in the issue, we don't have enough information
12 | to take action. Please reach out if you have or find the answers we need so
13 | that we can investigate further.
14 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Node.js CI
2 |
3 | on:
4 | push:
5 | branches: [ master, v5.0.0-development ]
6 | pull_request:
7 | branches: [ master, v5.0.0-development ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | strategy:
15 | fail-fast: false
16 | matrix:
17 | node-version: [14.x, 16.x, 18.x]
18 |
19 | steps:
20 | - uses: actions/checkout@v2
21 | with:
22 | submodules: recursive
23 | - name: Use Node.js ${{ matrix.node-version }}
24 | uses: actions/setup-node@v3
25 | with:
26 | node-version: ${{ matrix.node-version }}
27 | - run: npm install
28 | - run: npm run lint
29 |
30 |
31 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ master ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ master ]
20 | schedule:
21 | - cron: '43 4 * * 6'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'javascript' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v2
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v1
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 | # If this step fails, then you should remove it and run the build manually (see below)
55 | - name: Autobuild
56 | uses: github/codeql-action/autobuild@v1
57 |
58 | # ℹ️ Command-line programs to run using the OS shell.
59 | # 📚 https://git.io/JvXDl
60 |
61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62 | # and modify them (or add more) to build your code if your project
63 | # uses a compiled language
64 |
65 | #- run: |
66 | # make bootstrap
67 | # make release
68 |
69 | - name: Perform CodeQL Analysis
70 | uses: github/codeql-action/analyze@v1
71 |
--------------------------------------------------------------------------------
/.github/workflows/ion-test-driver.yml:
--------------------------------------------------------------------------------
1 | name: ion-test-driver
2 |
3 | on: [pull_request]
4 |
5 | jobs:
6 | ion-test-driver:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout ion-js
10 | uses: actions/checkout@master
11 | with:
12 | repository: amazon-ion/ion-js
13 | ref: master
14 | path: ion-js
15 |
16 | - name: Checkout ion-test-driver
17 | uses: actions/checkout@master
18 | with:
19 | repository: amazon-ion/ion-test-driver
20 | ref: master
21 | path: ion-test-driver
22 |
23 | - name: Set up python3 env
24 | run: python3 -m venv ion-test-driver/venv && . ion-test-driver/venv/bin/activate
25 |
26 | - name: Pip install
27 | run: pip3 install -r ion-test-driver/requirements.txt && pip3 install -e ion-test-driver
28 |
29 | - name: Get main branch HEAD sha
30 | run: cd ion-js && echo `git rev-parse --short=7 HEAD` && echo "main=`git rev-parse --short=7 HEAD`" >> $GITHUB_ENV
31 |
32 | - name: Get current commit sha
33 | run: cd ion-js && echo `git rev-parse --short=7 ${{ github.event.pull_request.head.sha }}`
34 | && echo "cur=`git rev-parse --short=7 ${{ github.event.pull_request.head.sha }}`" >> $GITHUB_ENV
35 |
36 | - name: Run ion-test-driver
37 | run: python3 ion-test-driver/amazon/iontest/ion_test_driver.py -o output
38 | -i ion-js,${{ github.event.pull_request.head.repo.html_url }},${{ github.event.pull_request.head.sha }}
39 | --replace ion-js,https://github.com/amazon-ion/ion-js.git,$main
40 |
41 | - name: Upload result
42 | uses: actions/upload-artifact@v2
43 | with:
44 | name: ion-test-driver-result.ion
45 | path: output/results/ion-test-driver-results.ion
46 |
47 | - name: Showing result
48 | run: cat output/results/ion-test-driver-results.ion
49 |
50 | - name: Analyze two implementations
51 | continue-on-error: true
52 | id: result-diff
53 | run: python3 ion-test-driver/amazon/iontest/ion_test_driver.py -R
54 | ion-js,$main ion-js,$cur output/results/ion-test-driver-results.ion
55 |
56 | - name: Upload analysis report
57 | uses: actions/upload-artifact@v2
58 | with:
59 | name: analysis-report.ion
60 | path: result.ion
61 |
62 | - name: Showing report
63 | run: cat result.ion
64 |
65 | - name: Check if ion-test-driver fails
66 | if: ${{ steps.result-diff.outcome == 'failure' }}
67 | run: echo 'Implementation behavior changed, Refer to the analysis report in the previous step for the reason.' && exit 1
68 |
69 | open-issue:
70 | runs-on: ubuntu-latest
71 | needs: ion-test-driver
72 | if: ${{ failure() }}
73 | steps:
74 | - uses: actions/checkout@master
75 | - name: Open an issue
76 | uses: JasonEtco/create-an-issue@v2
77 | env:
78 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
79 | GITHUB_WORKFLOW_URL: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}
80 | GITHUB_PR_SHA: ${{ github.event.pull_request.head.sha }}
81 | with:
82 | assignees: ${{ github.event.sender.login }}
83 | filename: .github/ion-test-driver-issue.md
84 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: ion-js release
2 |
3 | on:
4 | release:
5 | types: [created]
6 |
7 | jobs:
8 | build:
9 | if: startsWith(github.ref, 'refs/tags/v')
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v3
13 | with:
14 | submodules: recursive
15 | - uses: actions/setup-node@v3
16 | with:
17 | node-version: '16.x'
18 | registry-url: 'https://registry.npmjs.org'
19 | - run: npm install
20 | - name: grunt release
21 | run: ./node_modules/.bin/grunt release
22 | - uses: actions/upload-artifact@v3
23 | with:
24 | name: dist
25 | path: dist/
26 | - uses: actions/upload-artifact@v3
27 | with:
28 | name: docs
29 | path: docs/
30 | publish-to-github:
31 | if: startsWith(github.ref, 'refs/tags/v')
32 | runs-on: ubuntu-latest
33 | needs: build
34 | permissions:
35 | checks: write
36 | contents: write
37 | steps:
38 | - name: set env
39 | run: echo "RELEASE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
40 | - uses: actions/checkout@v3
41 | - uses: actions/download-artifact@v4.1.7
42 | with:
43 | name: dist
44 | path: dist/
45 | - name: create zip file
46 | run: zip -r "ion-js.$RELEASE_TAG-dist.zip" ./dist
47 | - name: upload zip file to github release
48 | env:
49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50 | run: gh release upload "$RELEASE_TAG" "ion-js.$RELEASE_TAG-dist.zip"
51 | publish-to-npm:
52 | if: startsWith(github.ref, 'refs/tags/v')
53 | runs-on: ubuntu-latest
54 | needs: build
55 | steps:
56 | - name: set env
57 | run: echo "RELEASE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
58 | - uses: actions/checkout@v3
59 | - uses: actions/download-artifact@v4.1.7
60 | - run: ls */* | cat
61 | - uses: actions/setup-node@v3
62 | with:
63 | node-version: '16.x'
64 | registry-url: 'https://registry.npmjs.org'
65 | - run: npm install
66 | - name: npm publish
67 | # skip npm publishing if running in a fork
68 | if: github.repository == 'amazon-ion/ion-js'
69 | env:
70 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
71 | run: npm publish
72 | update-docs:
73 | if: startsWith(github.ref, 'refs/tags/v')
74 | runs-on: ubuntu-latest
75 | needs: build
76 | permissions:
77 | contents: write
78 | steps:
79 | - name: set env
80 | run: echo "RELEASE_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
81 | - uses: actions/checkout@v3
82 | with:
83 | ref: gh-pages
84 | - uses: actions/download-artifact@v4.1.7
85 | with:
86 | name: docs
87 | path: docs/
88 | - name: create documentation pull request
89 | env:
90 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
91 | run: |
92 | rm -rf api browser
93 | cp -R ./docs/api .
94 | cp -R ./docs/browser .
95 | git add ./api ./browser
96 | git config user.name github-actions
97 | git config user.email github-actions@github.com
98 | git commit -m "adds documentation for $RELEASE_TAG"
99 | git checkout -b "generated-docs-$RELEASE_TAG"
100 | git push --set-upstream origin "generated-docs-$RELEASE_TAG"
101 | REVIEWER="$(gh release view "$RELEASE_TAG" --json author --jq '.author.login')"
102 | gh pr create --fill --base gh-pages -r "$REVIEWER"
103 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .tscache/
2 | dist/
3 | test-driver/dist
4 | node_modules/
5 | **/.baseDir.ts
6 | lcov.info
7 | package-lock.json
8 | browser/scripts
9 | docs/
10 | coverage-final.json
11 |
12 | ### Emacs ###
13 | # -*- mode: gitignore; -*-
14 | *~
15 | \#*\#
16 | /.emacs.desktop
17 | /.emacs.desktop.lock
18 | *.elc
19 | auto-save-list
20 | tramp
21 | .\#*
22 |
23 | # Org-mode
24 | .org-id-locations
25 | *_archive
26 |
27 | # flymake-mode
28 | *_flymake.*
29 |
30 | # eshell files
31 | /eshell/history
32 | /eshell/lastdir
33 |
34 | # elpa packages
35 | /elpa/
36 |
37 | # reftex files
38 | *.rel
39 |
40 | # AUCTeX auto folder
41 | /auto/
42 |
43 | # cask packages
44 | .cask/
45 |
46 | # Flycheck
47 | flycheck_*.el
48 |
49 | # server auth directory
50 | /server/
51 |
52 | # projectiles files
53 | .projectile
54 |
55 | # directory configuration
56 | .dir-locals.el
57 |
58 | ### Intellij ###
59 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
60 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
61 |
62 | # User-specific stuff:
63 | .idea/
64 | .idea/**/workspace.xml
65 | .idea/**/tasks.xml
66 |
67 | # Sensitive or high-churn files:
68 | .idea/**/dataSources/
69 | .idea/**/dataSources.ids
70 | .idea/**/dataSources.xml
71 | .idea/**/dataSources.local.xml
72 | .idea/**/sqlDataSources.xml
73 | .idea/**/dynamic.xml
74 | .idea/**/uiDesigner.xml
75 |
76 | # Gradle:
77 | .idea/**/gradle.xml
78 | .idea/**/libraries
79 |
80 | # Mongo Explorer plugin:
81 | .idea/**/mongoSettings.xml
82 |
83 | ## File-based project format:
84 | *.iws
85 |
86 | ## Plugin-specific files:
87 |
88 | # IntelliJ
89 | /out/
90 |
91 | # mpeltonen/sbt-idea plugin
92 | .idea_modules/
93 |
94 | # JIRA plugin
95 | atlassian-ide-plugin.xml
96 |
97 | # Crashlytics plugin (for Android Studio and IntelliJ)
98 | com_crashlytics_export_strings.xml
99 | crashlytics.properties
100 | crashlytics-build.properties
101 | fabric.properties
102 |
103 | ### Intellij Patch ###
104 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
105 |
106 | ## Ignoring iml files breaks when using IntelliJ modules. We do not at the moment.
107 | *.iml
108 | .idea
109 |
110 | # modules.xml
111 | # .idea/misc.xml
112 | # *.ipr
113 |
114 |
115 | ### Vim ###
116 | # swap
117 | [._]*.s[a-v][a-z]
118 | [._]*.sw[a-p]
119 | [._]s[a-v][a-z]
120 | [._]sw[a-p]
121 | # session
122 | Session.vim
123 | # temporary
124 | .netrwhist
125 | # auto-generated tag files
126 | tags
127 |
128 | .nyc_output/
129 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "ion-tests"]
2 | path = ion-tests
3 | url = https://github.com/amazon-ion/ion-tests.git
4 |
--------------------------------------------------------------------------------
/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amazon-ion/ion-js/3bee48d30648b29751903652d8e271231bd4ea9b/.nojekyll
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # See https://docs.npmjs.com/misc/developers#keeping-files-out-of-your-package
2 | # Use `npm pack` to create a tgz file. View the contents of the .tgz to test your .npmignore
3 | *.map
4 | .baseDir*
5 | .github/
6 | .gitmodules
7 | .idea
8 | .nojekyll
9 | .nyc_output/
10 | .prettierignore
11 | .travis.yml
12 | .tscache
13 | Gruntfile.js
14 | _config.yml
15 | browser
16 | docs
17 | ion-tests
18 | src
19 | test
20 | test-driver
21 | tests
22 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | src/.baseDir.ts
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
6 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check [existing open](https://github.com/amazon-ion/ion-js/issues), or [recently closed](https://github.com/amazon-ion/ion-js/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *master* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/amazon-ion/ion-js/labels/help%20wanted) issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](https://github.com/amazon-ion/ion-js/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.
62 |
63 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Amazon Ion JavaScript
2 | Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-minimal
--------------------------------------------------------------------------------
/browser/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Ion JS in your browser
9 |
10 | and then open the JS console
11 | on your browser. The global variable ion
may be used using the API.
12 |
13 |
14 |
15 | A more complete and user friendly tutorial is forthcoming!
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ion-js",
3 | "version": "5.3.0-SNAPSHOT",
4 | "description": "A JavaScript implementation of the Ion data interchange format",
5 | "main": "dist/commonjs/es6/Ion.js",
6 | "types": "dist/commonjs/es6/Ion.d.ts",
7 | "exports": {
8 | "module": "./dist/es6/es6/Ion.js",
9 | "default": "./dist/commonjs/es6/Ion.js"
10 | },
11 | "scripts": {
12 | "commit": "git-cz",
13 | "prepare": "grunt release",
14 | "lint": "grunt lint && prettier --check 'src/**/*.ts'",
15 | "lint:fix": "prettier --write 'src/**/*.ts'",
16 | "test": "nyc mocha",
17 | "release": "grunt release",
18 | "test-driver": "cd test-driver && npm install",
19 | "build-test-driver": "cd test-driver && npm run build"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "https://github.com/amazon-ion/ion-js.git"
24 | },
25 | "keywords": [
26 | "ion",
27 | "JSON",
28 | "data format"
29 | ],
30 | "author": "The Ion Team (https://amazon-ion.github.io/ion-docs/index.html)",
31 | "license": "Apache-2.0",
32 | "bugs": {
33 | "url": "https://github.com/amazon-ion/ion-js/issues"
34 | },
35 | "homepage": "https://github.com/amazon-ion/ion-js#readme",
36 | "config": {
37 | "commitizen": {
38 | "path": "node_modules/cz-conventional-changelog"
39 | }
40 | },
41 | "nyc": {
42 | "extends": "@istanbuljs/nyc-config-typescript",
43 | "all": true,
44 | "include": [
45 | "src/**.ts"
46 | ],
47 | "exclude": [
48 | "src/.**"
49 | ],
50 | "check-coverage": false
51 | },
52 | "devDependencies": {
53 | "@babel/cli": "^7.18.10",
54 | "@babel/core": "^7.19.1",
55 | "@babel/plugin-transform-object-assign": "^7.18.6",
56 | "@babel/plugin-transform-runtime": "^7.19.1",
57 | "@babel/polyfill": "^7.8.7",
58 | "@babel/preset-env": "^7.10.0",
59 | "@babel/runtime": "^7.10.0",
60 | "@istanbuljs/nyc-config-typescript": "^0.1.3",
61 | "@types/chai": "^4.2.11",
62 | "@types/mocha": "^5.2.7",
63 | "@types/node": "^12.12.42",
64 | "@typescript-eslint/eslint-plugin": "^4.33.0",
65 | "@typescript-eslint/parser": "^4.33.0",
66 | "babelify": "^10.0.0",
67 | "chai": "^4.3.6",
68 | "commitizen": "^4.2.5",
69 | "cz-conventional-changelog": "^3.2.0",
70 | "eslint": "^7.32.0",
71 | "eslint-config-prettier": "^8.5.0",
72 | "grunt": "^1.5.3",
73 | "grunt-babel": "^8.0.0",
74 | "grunt-browserify": "^5.3.0",
75 | "grunt-cli": "^1.3.2",
76 | "grunt-contrib-clean": "^2.0.0",
77 | "grunt-contrib-copy": "^1.0.0",
78 | "grunt-contrib-jshint": "^2.1.0",
79 | "grunt-contrib-uglify": "^5.2.2",
80 | "grunt-eslint": "^23.0.0",
81 | "grunt-shell": "^3.0.1",
82 | "grunt-ts": "^6.0.0-beta.22",
83 | "grunt-typedoc": "^0.2.4",
84 | "mocha": "^6.2.3",
85 | "mocha-typescript": "^1.1.17",
86 | "nyc": "^14.1.1",
87 | "prettier": "2.1.2",
88 | "semantic-release": "^17.2.3",
89 | "source-map-support": "^0.5.19",
90 | "ts-node": "^8.10.1",
91 | "typedoc": "^0.20.37",
92 | "typescript": "Mixed-in classes with functions returning `this` have TS2526 error is DTS file. Refernce issue: https://github.com/microsoft/TypeScript/issues/52687. Upgrade to a latest typescript version once this issue is resolved",
93 | "typescript": "^3.9.10",
94 | "yargs": "^17.5.1"
95 | },
96 | "browserslist": [
97 | "Chrome >= 80.0",
98 | "Firefox >= 80.0",
99 | "Opera >= 63.0",
100 | "Edge >= 80.0",
101 | "Safari >= 14.1",
102 | "Samsung >= 12.0"
103 | ]
104 | }
105 |
--------------------------------------------------------------------------------
/src/AbstractWriter.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import { Reader } from "./IonReader";
17 | import { IonType } from "./IonType";
18 | import { IonTypes } from "./IonTypes";
19 | import { Writer } from "./IonWriter";
20 |
21 | // TS workaround that avoids the need to copy all Writer method signatures into AbstractWriter
22 | export interface AbstractWriter extends Writer {}
23 |
24 | export abstract class AbstractWriter implements Writer {
25 | protected _annotations: string[] = [];
26 |
27 | addAnnotation(annotation: string): void {
28 | if (!this._isString(annotation)) {
29 | throw new Error("Annotation must be of type string.");
30 | }
31 | this._annotations.push(annotation);
32 | }
33 |
34 | setAnnotations(annotations: string[]): void {
35 | if (annotations === undefined || annotations === null) {
36 | throw new Error("Annotations were undefined or null.");
37 | } else if (!this._validateAnnotations(annotations)) {
38 | throw new Error("Annotations must be of type string[].");
39 | } else {
40 | this._annotations = annotations;
41 | }
42 | }
43 |
44 | // This method is not part of the Writer interface, but subclasses of AbstractWriter must implement it
45 | // in order to use the default implementations of writeValue() and writeValues()
46 | protected abstract _isInStruct(): boolean;
47 |
48 | writeValues(reader: Reader): void {
49 | this._writeValues(reader);
50 | }
51 |
52 | writeValue(reader: Reader): void {
53 | this._writeValue(reader);
54 | }
55 |
56 | protected _clearAnnotations(): void {
57 | this._annotations = [];
58 | }
59 |
60 | private _writeValues(reader: Reader): void {
61 | let type: IonType | null = reader.type();
62 | if (type === null) {
63 | type = reader.next();
64 | }
65 | while (type !== null) {
66 | this._writeValue(reader);
67 | type = reader.next();
68 | }
69 | }
70 |
71 | private _writeValue(reader: Reader): void {
72 | const type: IonType | null = reader.type();
73 | if (type === null) {
74 | return;
75 | }
76 |
77 | if (this._isInStruct()) {
78 | const fieldName = reader.fieldName();
79 | if (fieldName === null) {
80 | throw new Error(
81 | "Cannot call writeValue() when the Writer is in a Struct but the Reader is not."
82 | );
83 | }
84 | this.writeFieldName(fieldName);
85 | }
86 |
87 | this.setAnnotations(reader.annotations());
88 |
89 | if (reader.isNull()) {
90 | this.writeNull(type);
91 | return;
92 | }
93 |
94 | switch (type) {
95 | case IonTypes.BOOL:
96 | this.writeBoolean(reader.booleanValue());
97 | break;
98 | case IonTypes.INT:
99 | this.writeInt(reader.bigIntValue());
100 | break;
101 | case IonTypes.FLOAT:
102 | this.writeFloat64(reader.numberValue());
103 | break;
104 | case IonTypes.DECIMAL:
105 | this.writeDecimal(reader.decimalValue());
106 | break;
107 | case IonTypes.TIMESTAMP:
108 | this.writeTimestamp(reader.timestampValue());
109 | break;
110 | case IonTypes.SYMBOL:
111 | this.writeSymbol(reader.stringValue());
112 | break;
113 | case IonTypes.STRING:
114 | this.writeString(reader.stringValue());
115 | break;
116 | case IonTypes.CLOB:
117 | this.writeClob(reader.uInt8ArrayValue());
118 | break;
119 | case IonTypes.BLOB:
120 | this.writeBlob(reader.uInt8ArrayValue());
121 | break;
122 | case IonTypes.LIST:
123 | this.stepIn(IonTypes.LIST);
124 | break;
125 | case IonTypes.SEXP:
126 | this.stepIn(IonTypes.SEXP);
127 | break;
128 | case IonTypes.STRUCT:
129 | this.stepIn(IonTypes.STRUCT);
130 | break;
131 | default:
132 | throw new Error(
133 | "Unrecognized type " + (type !== null ? type.name : type)
134 | );
135 | }
136 | if (type.isContainer) {
137 | reader.stepIn();
138 | this._writeValues(reader);
139 | this.stepOut();
140 | reader.stepOut();
141 | }
142 | }
143 |
144 | private _validateAnnotations(input: string[]): boolean {
145 | if (!Array.isArray(input)) {
146 | return false;
147 | }
148 | for (let i = 0; i < input.length; i++) {
149 | if (!this._isString(input[i])) {
150 | return false;
151 | }
152 | }
153 | return true;
154 | }
155 |
156 | private _isString(input: string): boolean {
157 | return typeof input === "string";
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/BigIntSerde.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | // TODO: Add fast paths for BigInt values that can fit in a standard Number.
17 | /**
18 | * This class provides logic to convert BigInt values to and from the UInt, Int, VarUInt, and VarInt primitives from the
19 | * Ion spec.
20 | */
21 | export class BigIntSerde {
22 | private static readonly SERIALIZED_BIGINT_SIZES_TO_PRECOMPUTE = 64;
23 | private static readonly BITS_PER_BYTE = 8n;
24 | private static readonly BYTE_MAX_VALUE = BigInt(0xff);
25 | private static readonly SIZE_THRESHOLDS = BigIntSerde.calculateSizeThresholds();
26 |
27 | /**
28 | * Encodes the specified BigInt value as a sign-and-magnitude integer.
29 | *
30 | * @param value The integer to encode.
31 | * @param isNegative Whether or not the integer is negative. This cannot be inferred when `value` is zero,
32 | * as the BigInt data type cannot natively represent negative zero.
33 | * @return A byte array containing the encoded Int.
34 | */
35 | public static toSignedIntBytes(
36 | value: bigint,
37 | isNegative: boolean
38 | ): Uint8Array {
39 | let bytes: Uint8Array = this.toUnsignedIntBytes(value);
40 | if (bytes[0] >= 128) {
41 | const extendedBytes: Uint8Array = new Uint8Array(bytes.length + 1);
42 | extendedBytes.set(bytes, 1);
43 | bytes = extendedBytes;
44 | }
45 | if (isNegative) {
46 | bytes[0] += 0x80; // Flip the sign bit to indicate that it's negative.
47 | }
48 | return bytes;
49 | }
50 |
51 | /**
52 | * Reads the provided byte array as a big-endian, unsigned integer.
53 | *
54 | * @param bytes A byte array containing an encoded UInt.
55 | * @return A BigInt value representing the encoded UInt.
56 | */
57 | public static fromUnsignedBytes(bytes: Uint8Array): bigint {
58 | let magnitude = 0n;
59 | for (let m = 0; m < bytes.length; m++) {
60 | const byte = BigInt(bytes[m]);
61 | magnitude = magnitude << this.BITS_PER_BYTE;
62 | magnitude = magnitude | byte;
63 | }
64 | return magnitude;
65 | }
66 |
67 | /**
68 | * Encodes the specified BigInt value as a big-endian, unsigned integer.
69 | * @param value The BigInt value to encode.
70 | * @return A byte array containing the encoded UInt.
71 | */
72 | public static toUnsignedIntBytes(value: bigint): Uint8Array {
73 | // Remove the sign if negative
74 | if (value < 0n) {
75 | value = -value;
76 | }
77 |
78 | const sizeInBytes = this.getUnsignedIntSizeInBytes(value);
79 | const bytes = new Uint8Array(sizeInBytes);
80 | for (let m = sizeInBytes - 1; m >= 0; m--) {
81 | const lastByte = Number(value & this.BYTE_MAX_VALUE);
82 | value = value >> this.BITS_PER_BYTE;
83 | bytes[m] = lastByte;
84 | }
85 |
86 | return bytes;
87 | }
88 |
89 | // Determines how many bytes will be needed to store the UInt encoding of the given BigInt value.
90 | static getUnsignedIntSizeInBytes(value: bigint): number {
91 | // TODO: Profile on real data sets to see if binary search is preferable to a low-to-high linear search.
92 | for (let m = 0; m < this.SIZE_THRESHOLDS.length; m++) {
93 | const threshold = this.SIZE_THRESHOLDS[m];
94 | if (value <= threshold) {
95 | return m + 1;
96 | }
97 | }
98 |
99 | let sizeInBytes = this.SIZE_THRESHOLDS.length;
100 | let threshold = this.calculateSizeThreshold(sizeInBytes);
101 | while (value > threshold) {
102 | // TODO: Make larger jumps, then refine the search.
103 | sizeInBytes++;
104 | threshold = this.calculateSizeThreshold(sizeInBytes);
105 | }
106 |
107 | return sizeInBytes;
108 | }
109 |
110 | // Called once during initialization. Creates an array of thresholds that can be referred to to determine how many
111 | // bytes will be needed to store the UInt encoding of a given BigInt value.
112 | private static calculateSizeThresholds(): Array {
113 | const thresholds: Array = [];
114 | for (let m = 1; m <= this.SERIALIZED_BIGINT_SIZES_TO_PRECOMPUTE; m++) {
115 | thresholds.push(this.calculateSizeThreshold(m));
116 | }
117 | return thresholds;
118 | }
119 |
120 | private static calculateSizeThreshold(numberOfBytes: number): bigint {
121 | const exponent = BigInt(numberOfBytes) * this.BITS_PER_BYTE;
122 | const threshold = 2n ** exponent;
123 | return threshold - 1n;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/ComparisonResult.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | /** Comparison result types for the comparison report
17 | * EQUALS: Indicates the input streams are equal
18 | * NOT_EQUAL: Indicates the input streams are not equal
19 | * ERROR: For all the cases where an error occurs while reading or writing input streams
20 | */
21 | export enum ComparisonResultType {
22 | EQUAL = "EQUAL",
23 | NOT_EQUAL = "NOT_EQUAL",
24 | ERROR = "ERROR",
25 | }
26 |
27 | /**
28 | * comparison result with event index and message
29 | */
30 | export class ComparisonResult {
31 | message: string;
32 | result: ComparisonResultType;
33 | actualIndex: number;
34 | expectedIndex: number;
35 |
36 | constructor(
37 | result: ComparisonResultType = ComparisonResultType.EQUAL,
38 | message: string = "",
39 | actualIndex: number = 0,
40 | expectedIndex: number = 0
41 | ) {
42 | this.result = result;
43 | this.message = message;
44 | this.actualIndex = actualIndex;
45 | this.expectedIndex = expectedIndex;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/IntSize.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | /**
17 | * Represents whether a given Ion int value will fit safely within a Number or if it requires a BigInt.
18 | */
19 | export enum IntSize {
20 | /** The Ion int value fits safely in a number. */
21 | Number,
22 | /** The Ion int value requies a BigInt. */
23 | BigInt,
24 | }
25 |
26 | export default IntSize;
27 |
--------------------------------------------------------------------------------
/src/Ion.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import IntSize from "./IntSize";
17 | import { BinaryReader } from "./IonBinaryReader";
18 | import { BinaryWriter } from "./IonBinaryWriter";
19 | import { Catalog } from "./IonCatalog";
20 | import { IVM } from "./IonConstants";
21 | import { defaultLocalSymbolTable } from "./IonLocalSymbolTable";
22 | import { PrettyTextWriter } from "./IonPrettyTextWriter";
23 | import { Reader } from "./IonReader";
24 | import { BinarySpan, StringSpan } from "./IonSpan";
25 | import { TextReader } from "./IonTextReader";
26 | import { TextWriter } from "./IonTextWriter";
27 | import { decodeUtf8 } from "./IonUnicode";
28 | import { Writeable } from "./IonWriteable";
29 | import { Writer } from "./IonWriter";
30 |
31 | /**
32 | * Indicates whether the provided buffer contains binary Ion data.
33 | *
34 | * @param buffer The buffer of data to inspect.
35 | * @returns True if the provided buffer begins with a binary Ion version marker, false otherwise.
36 | */
37 | function isBinary(buffer: Uint8Array): boolean {
38 | if (buffer.length < 4) {
39 | return false;
40 | }
41 | for (let i = 0; i < 4; i++) {
42 | if (buffer[i] !== IVM.binary[i]) {
43 | return false;
44 | }
45 | }
46 | return true;
47 | }
48 |
49 | /** Octet buffer input types for the Ion reader interface. */
50 | export type ReaderOctetBuffer = ArrayBufferLike | ArrayLike;
51 |
52 | /** All buffer input types for the Ion reader interface. */
53 | export type ReaderBuffer = ReaderOctetBuffer | string;
54 |
55 | /**
56 | * Create an {@link Reader} over Ion data in a {@link ReaderBuffer}.
57 | *
58 | * @param buf The Ion data to be used by the reader. Typically a string, UTF-8 encoded buffer (text), or raw
59 | * binary buffer.
60 | * @param catalog Optional catalog to be used by reader to resolve shared symbol table references.
61 | */
62 | export function makeReader(buf: ReaderBuffer, catalog?: Catalog): Reader {
63 | if (typeof buf === "string") {
64 | return new TextReader(new StringSpan(buf as string), catalog);
65 | }
66 | const bufArray = new Uint8Array(buf as ReaderOctetBuffer);
67 | if (isBinary(bufArray)) {
68 | return new BinaryReader(new BinarySpan(bufArray), catalog);
69 | } else {
70 | return new TextReader(new StringSpan(decodeUtf8(bufArray)), catalog);
71 | }
72 | }
73 |
74 | /** Creates a new Ion Text Writer. */
75 | export function makeTextWriter(): Writer {
76 | // TODO #384 make LST an optional parameter
77 | return new TextWriter(new Writeable());
78 | }
79 |
80 | /** Creates a new Ion Text Writer with pretty printing of the text. */
81 | export function makePrettyWriter(indentSize?: number): Writer {
82 | // TODO #384 make LST an optional parameter
83 | return new PrettyTextWriter(new Writeable(), indentSize);
84 | }
85 |
86 | /** Creates a new Ion Binary Writer. */
87 | export function makeBinaryWriter(): Writer {
88 | // TODO #384 make LST an optional parameter
89 | const localSymbolTable = defaultLocalSymbolTable();
90 | return new BinaryWriter(localSymbolTable, new Writeable());
91 | }
92 |
93 | // Used by the dump*() functions to write each of a sequence of values to the provided Writer.
94 | function _writeAllTo(writer: Writer, values: any[]): Uint8Array {
95 | for (const value of values) {
96 | dom.Value.from(value).writeTo(writer);
97 | }
98 | writer.close();
99 | return writer.getBytes();
100 | }
101 |
102 | /**
103 | * Returns a binary Ion representation of the provided values.
104 | * @param values Values to encode in Ion.
105 | */
106 | export function dumpBinary(...values: any[]): Uint8Array {
107 | return _writeAllTo(makeBinaryWriter(), values);
108 | }
109 |
110 | /**
111 | * Returns a compact text Ion representation of the provided values.
112 | * @param values Values to encode in Ion.
113 | */
114 | export function dumpText(...values: any[]): string {
115 | return decodeUtf8(_writeAllTo(makeTextWriter(), values));
116 | }
117 |
118 | /**
119 | * Returns a text Ion representation of the provided values that is generously spaced for
120 | * easier human readability.
121 | * @param values Values to encode in Ion.
122 | */
123 | export function dumpPrettyText(...values: any[]): string {
124 | return decodeUtf8(_writeAllTo(makePrettyWriter(), values));
125 | }
126 |
127 | export { Reader, ReaderScalarValue } from "./IonReader";
128 | export { Writer } from "./IonWriter";
129 | export { Catalog } from "./IonCatalog";
130 | export { Decimal } from "./IonDecimal";
131 | export { defaultLocalSymbolTable } from "./IonLocalSymbolTable";
132 | export { IntSize };
133 | export { IonType } from "./IonType";
134 | export { IonTypes } from "./IonTypes";
135 | export { SharedSymbolTable } from "./IonSharedSymbolTable";
136 | export { TimestampPrecision, Timestamp } from "./IonTimestamp";
137 | export { toBase64 } from "./IonText";
138 | export { decodeUtf8 } from "./IonUnicode";
139 |
140 | import * as dom from "./dom";
141 | export { dom };
142 |
143 | // Re-export dom convenience methods for easy access via 'ion'
144 | export { load, loadAll } from "./dom";
145 |
146 | // Events exports and Comparison Result export
147 | export { IonEvent, IonEventType, IonEventFactory } from "./events/IonEvent";
148 | export { IonEventStream } from "./events/IonEventStream";
149 | export { EventStreamError } from "./events/EventStreamError";
150 | export { ComparisonResult, ComparisonResultType } from "./ComparisonResult";
151 |
--------------------------------------------------------------------------------
/src/IonBinary.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | /** @file Constants and enums for the Ion Binary format */
17 |
18 | export const NIBBLE_MASK = 0xf;
19 | export const BYTE_MASK = 0xff;
20 | export const TYPE_SHIFT = 4;
21 | export const BYTE_SHIFT = 8;
22 |
23 | export const LEN_MASK = 0xf;
24 | export const LEN_VAR = 14; // 0xe
25 | export const LEN_NULL = 15; // 0xf
26 |
27 | export const TB_NULL = 0;
28 | export const TB_BOOL = 1;
29 | export const TB_INT = 2;
30 | export const TB_NEG_INT = 3;
31 | export const TB_FLOAT = 4;
32 | export const TB_DECIMAL = 5;
33 | export const TB_TIMESTAMP = 6;
34 | export const TB_SYMBOL = 7;
35 | export const TB_STRING = 8;
36 | export const TB_CLOB = 9;
37 | export const TB_BLOB = 10; // 0xa
38 | export const TB_LIST = 11; // 0xb
39 | export const TB_SEXP = 12; // 0xc
40 | export const TB_STRUCT = 13; // 0xd
41 | export const TB_ANNOTATION = 14; // 0xe
42 |
--------------------------------------------------------------------------------
/src/IonCatalog.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import { SharedSymbolTable } from "./IonSharedSymbolTable";
17 | import { getSystemSymbolTable } from "./IonSystemSymbolTable";
18 |
19 | interface SymbolTableIndex {
20 | [name: string]: SharedSymbolTable[];
21 | }
22 |
23 | function byVersion(x: SharedSymbolTable, y: SharedSymbolTable): number {
24 | return x.version - y.version;
25 | }
26 |
27 | /**
28 | * A catalog holds available shared symbol tables and always includes the system symbol table.
29 | * @see https://amazon-ion.github.io/ion-docs/docs/symbols.html#the-catalog
30 | */
31 | export class Catalog {
32 | private symbolTables: SymbolTableIndex;
33 |
34 | /** Creates a catalog containing only the system symbol table. */
35 | constructor() {
36 | this.symbolTables = {};
37 | this.add(getSystemSymbolTable());
38 | }
39 |
40 | /** Adds a new shared symbol table to this catalog. */
41 | add(symbolTable: SharedSymbolTable): void {
42 | if (symbolTable.name === undefined || symbolTable.name === null) {
43 | throw new Error("SymbolTable name must be defined.");
44 | }
45 | const versions = this.symbolTables[symbolTable.name];
46 | if (versions === undefined) {
47 | this.symbolTables[symbolTable.name] = [];
48 | }
49 | this.symbolTables[symbolTable.name][symbolTable.version] = symbolTable;
50 | }
51 |
52 | /**
53 | * Returns a symbol table by name and version.
54 | *
55 | * @return The symbol table or `null` if it does not exist in the {Catalog}.
56 | */
57 | getVersion(name: string, version: number): SharedSymbolTable | null {
58 | const tables: SharedSymbolTable[] = this.symbolTables[name];
59 | if (!tables) {
60 | return null;
61 | }
62 | let table = tables[version];
63 | if (!table) {
64 | table = tables[tables.length];
65 | }
66 | return table ? table : null;
67 | }
68 |
69 | /**
70 | * Retrieves the latest version of a symbol table by name.
71 | *
72 | * @return The symbol table or `null` if it does not exist in the {Catalog}.
73 | */
74 | getTable(name: string): SharedSymbolTable | null {
75 | const versions = this.symbolTables[name];
76 | if (versions === undefined) {
77 | return null;
78 | }
79 | return versions[versions.length - 1];
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/IonConstants.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | export const EOF = -1;
17 |
18 | export const IVM = {
19 | text: "$ion_1_0",
20 | binary: new Uint8Array([0xe0, 0x01, 0x00, 0xea]),
21 | sid: 2,
22 | };
23 |
--------------------------------------------------------------------------------
/src/IonImport.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import { SharedSymbolTable } from "./IonSharedSymbolTable";
17 |
18 | /**
19 | * A shared symbol table import.
20 | *
21 | * Import order in shared symbol tables is important, so each import also
22 | * references a parent (previous) import (except for the implicit system symbol
23 | * table import, which has no parent). Optionally, the number of symbols to
24 | * import from a given symbol table may be specified as the "length" of the
25 | * import.
26 | *
27 | * @see https://amazon-ion.github.io/ion-docs/symbols.html#imports
28 | */
29 | export class Import {
30 | private readonly _offset: number;
31 | private readonly _length: number;
32 | private readonly _parent: Import | null;
33 | private readonly _symbolTable: SharedSymbolTable;
34 |
35 | constructor(
36 | parent: Import | null,
37 | symbolTable: SharedSymbolTable,
38 | length?: number | null
39 | ) {
40 | this._parent = parent;
41 | this._symbolTable = symbolTable;
42 | this._offset = this.parent ? this.parent.offset + this.parent.length : 1;
43 | this._length = length || this.symbolTable.numberOfSymbols;
44 | }
45 |
46 | get parent(): Import | null {
47 | return this._parent;
48 | }
49 |
50 | get offset(): number {
51 | return this._offset;
52 | }
53 |
54 | get length(): number {
55 | return this._length;
56 | }
57 |
58 | get symbolTable(): SharedSymbolTable {
59 | return this._symbolTable;
60 | }
61 |
62 | getSymbolText(symbolId: number): string | undefined {
63 | if (this.parent === undefined) {
64 | throw new Error("Illegal parent state.");
65 | }
66 | if (this.parent !== null) {
67 | const parentSymbol = this.parent.getSymbolText(symbolId);
68 | if (parentSymbol) {
69 | return parentSymbol;
70 | }
71 | }
72 |
73 | const index: number = symbolId - this.offset;
74 | if (index >= 0 && index < this.length) {
75 | return this.symbolTable.getSymbolText(index);
76 | }
77 | return undefined;
78 | }
79 |
80 | getSymbolId(symbolText: string): number | undefined {
81 | let symbolId;
82 | if (this.parent !== null) {
83 | symbolId = this.parent.getSymbolId(symbolText);
84 | if (symbolId) {
85 | return symbolId;
86 | }
87 | }
88 |
89 | symbolId = this.symbolTable.getSymbolId(symbolText);
90 | if (symbolId !== null && symbolId !== undefined && symbolId < this.length) {
91 | return symbolId + this.offset;
92 | }
93 |
94 | return undefined;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/IonLocalSymbolTable.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import { Import } from "./IonImport";
17 | import { SymbolIndex } from "./IonSymbolIndex";
18 | import { getSystemSymbolTableImport } from "./IonSystemSymbolTable";
19 |
20 | /**
21 | * A local symbol table defines all the symbols which aren't included in the system
22 | * symbol table or from a shared symbol table via an import.
23 | */
24 | export class LocalSymbolTable {
25 | private readonly _import: Import;
26 | private readonly offset: number;
27 | private index: SymbolIndex = Object.create(null);
28 |
29 | constructor(theImport: Import | null, symbols: (string | null)[] = []) {
30 | if (theImport === null) {
31 | this._import = getSystemSymbolTableImport();
32 | } else {
33 | this._import = theImport;
34 | }
35 | this.offset = this._import.offset + this._import.length;
36 |
37 | for (const symbol_ of symbols) {
38 | this.assignSymbolId(symbol_);
39 | }
40 | }
41 |
42 | private _symbols: (string | null)[] = [];
43 |
44 | get symbols(): (string | null)[] {
45 | return this._symbols;
46 | }
47 |
48 | get maxId(): number {
49 | return this.offset + this._symbols.length - 1;
50 | }
51 |
52 | get import(): Import {
53 | return this._import;
54 | }
55 |
56 | getSymbolId(symbol_: string): number {
57 | return this._import.getSymbolId(symbol_) || this.index[symbol_];
58 | }
59 |
60 | addSymbol(symbol_: string | null): number {
61 | if (symbol_ !== null) {
62 | const existingSymbolId = this.getSymbolId(symbol_);
63 | if (existingSymbolId !== undefined) {
64 | return existingSymbolId;
65 | }
66 | }
67 | const symbolId = this.offset + this.symbols.length;
68 | this.symbols.push(symbol_);
69 | if (symbol_ !== null) {
70 | this.index[symbol_] = symbolId;
71 | }
72 | return symbolId;
73 | }
74 |
75 | // Used during table initialization. Unlike `addSymbol`, `assignSymbolId` will not discard strings that are already
76 | // in the symbol table. Ignoring duplicate symbols during table construction can cause symbol IDs that have already
77 | // been used to encode data to become invalid. For example, if a stream uses symbols "foo", "bar", "baz" to encode
78 | // its data even though "baz" was also defined in an imported table, discarding "baz" will cause data already encoded
79 | // with that ID to become corrupted.
80 | private assignSymbolId(symbol: string | null): number {
81 | // Push the text onto the end of our array of strings no matter what
82 | const symbolId = this.offset + this.symbols.length;
83 | this.symbols.push(symbol);
84 | // If this text isn't already in our index, go ahead and add it.
85 | if (symbol !== null && this.getSymbolId(symbol) === undefined) {
86 | this.index[symbol] = symbolId;
87 | }
88 | // Return the string's index in the symbol table, even if it isn't the lowest one.
89 | return symbolId;
90 | }
91 |
92 | getSymbolText(symbolId: number): string | null {
93 | if (symbolId > this.maxId) {
94 | throw new Error(
95 | "Symbol $" + symbolId.toString() + " greater than maxID."
96 | );
97 | }
98 | const importedSymbol: string | undefined = this.import.getSymbolText(
99 | symbolId
100 | );
101 | if (importedSymbol !== undefined) {
102 | return importedSymbol;
103 | }
104 | const index = symbolId - this.offset;
105 | return this.symbols[index];
106 | }
107 |
108 | numberOfSymbols(): number {
109 | return this._symbols.length;
110 | }
111 | }
112 |
113 | export function defaultLocalSymbolTable(): LocalSymbolTable {
114 | return new LocalSymbolTable(getSystemSymbolTableImport());
115 | }
116 |
--------------------------------------------------------------------------------
/src/IonLowLevelBinaryWriter.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import { BigIntSerde } from "./BigIntSerde";
17 | import { Writeable } from "./IonWriteable";
18 |
19 | /**
20 | * Values in the Ion binary format are serialized as a sequence of low-level fields. This
21 | * writer is responsible for emitting those fields in the proper format.
22 | * @see https://amazon-ion.github.io/ion-docs/binary.html#basic-field-formats
23 | */
24 | export class LowLevelBinaryWriter {
25 | private readonly writeable: Writeable;
26 |
27 | constructor(writeable: Writeable) {
28 | this.writeable = writeable;
29 | }
30 |
31 | static getSignedIntSize(value: number): number {
32 | if (value === 0) {
33 | return 1;
34 | }
35 | const numberOfSignBits = 1;
36 | const magnitude = Math.abs(value);
37 | const numberOfMagnitudeBits = Math.ceil(Math.log2(magnitude + 1));
38 | const numberOfBits = numberOfMagnitudeBits + numberOfSignBits;
39 | return Math.ceil(numberOfBits / 8);
40 | }
41 |
42 | static getUnsignedIntSize(value: number | bigint): number {
43 | if (typeof value === "bigint") {
44 | return BigIntSerde.getUnsignedIntSizeInBytes(value);
45 | }
46 | if (value === 0) {
47 | return 1;
48 | }
49 | const numberOfBits = Math.floor(Math.log2(value)) + 1;
50 | const numberOfBytes = Math.ceil(numberOfBits / 8);
51 | return numberOfBytes;
52 | }
53 |
54 | static getVariableLengthSignedIntSize(value: number): number {
55 | const absoluteValue: number = Math.abs(value);
56 | if (absoluteValue === 0) {
57 | return 1;
58 | }
59 | const valueBits: number = Math.floor(Math.log2(absoluteValue)) + 1;
60 | const trailingStopBits: number = Math.floor(valueBits / 7);
61 | const leadingStopBit = 1;
62 | const signBit = 1;
63 | return Math.ceil(
64 | (valueBits + trailingStopBits + leadingStopBit + signBit) / 8
65 | );
66 | }
67 |
68 | static getVariableLengthUnsignedIntSize(value: number): number {
69 | if (value === 0) {
70 | return 1;
71 | }
72 | const valueBits: number = Math.floor(Math.log2(value)) + 1;
73 | const stopBits: number = Math.ceil(valueBits / 7);
74 | return Math.ceil((valueBits + stopBits) / 8);
75 | }
76 |
77 | writeSignedInt(originalValue: number): void {
78 | // TODO this should flip to different modes based on the length calculation because bit shifting will drop to 32 bits.
79 | const length = LowLevelBinaryWriter.getSignedIntSize(originalValue);
80 | let value: number = Math.abs(originalValue);
81 | const tempBuf = new Uint8Array(length);
82 | // Trailing bytes
83 | let i: number = tempBuf.length;
84 | while (value >= 128) {
85 | tempBuf[--i] = value & 0xff;
86 | value >>>= 8;
87 | }
88 |
89 | tempBuf[--i] = value & 0xff;
90 |
91 | // Sign bit
92 | if (1 / originalValue < 0) {
93 | tempBuf[0] |= 0x80;
94 | }
95 |
96 | this.writeable.writeBytes(tempBuf);
97 | }
98 |
99 | writeUnsignedInt(originalValue: number | bigint): void {
100 | if (typeof originalValue === "bigint") {
101 | const encodedBytes = BigIntSerde.toUnsignedIntBytes(originalValue);
102 | this.writeable.writeBytes(encodedBytes);
103 | return;
104 | }
105 |
106 | const length = LowLevelBinaryWriter.getUnsignedIntSize(originalValue);
107 | const tempBuf = new Uint8Array(length);
108 | let value: number = originalValue;
109 | let i: number = tempBuf.length;
110 |
111 | while (value > 0) {
112 | // JavaScript bitwise operators treat operands as 32-bit sequences,
113 | // so we avoid using >>> in order to support values requiring more than 32 bits
114 | tempBuf[--i] = value % 256;
115 | value = Math.trunc(value / 256);
116 | }
117 |
118 | this.writeable.writeBytes(tempBuf);
119 | }
120 |
121 | writeVariableLengthSignedInt(originalValue: number): void {
122 | const tempBuf = new Uint8Array(
123 | LowLevelBinaryWriter.getVariableLengthSignedIntSize(originalValue)
124 | );
125 | let value: number = Math.abs(originalValue);
126 | let i = tempBuf.length - 1;
127 |
128 | while (value >= 64) {
129 | tempBuf[i--] = value & 0x7f;
130 | value >>>= 7;
131 | }
132 |
133 | // Leading byte
134 | tempBuf[i] = value;
135 |
136 | // Sign bit
137 | if (1 / originalValue < 0) {
138 | tempBuf[i] |= 0x40;
139 | }
140 |
141 | // Stop bit
142 | tempBuf[tempBuf.length - 1] |= 0x80;
143 |
144 | this.writeable.writeBytes(tempBuf);
145 | }
146 |
147 | writeVariableLengthUnsignedInt(originalValue: number): void {
148 | const tempBuf = new Uint8Array(
149 | LowLevelBinaryWriter.getVariableLengthUnsignedIntSize(originalValue)
150 | );
151 | let value: number = originalValue;
152 | let i = tempBuf.length;
153 |
154 | tempBuf[--i] = (value & 0x7f) | 0x80;
155 | value >>>= 7;
156 |
157 | while (value > 0) {
158 | tempBuf[--i] = value & 0x7f;
159 | value >>>= 7;
160 | }
161 |
162 | this.writeable.writeBytes(tempBuf);
163 | }
164 |
165 | writeByte(byte: number): void {
166 | this.writeable.writeByte(byte);
167 | }
168 |
169 | writeBytes(bytes: Uint8Array): void {
170 | this.writeable.writeBytes(bytes);
171 | }
172 |
173 | getBytes(): Uint8Array {
174 | return this.writeable.getBytes();
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/src/IonSharedSymbolTable.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | /**
17 | * A shared symbol table.
18 | * @see https://amazon-ion.github.io/ion-docs/docs/symbols.html#shared-symbol-tables
19 | */
20 | export class SharedSymbolTable {
21 | protected readonly _idsByText: Map;
22 |
23 | constructor(
24 | private readonly _name: string,
25 | private readonly _version: number,
26 | private readonly _symbols: string[]
27 | ) {
28 | this._idsByText = new Map();
29 | this._numberOfSymbols = this._symbols.length;
30 | // Iterate through the symbol array in reverse order so if the same string appears more than
31 | // once the smaller symbol ID is stored.
32 | for (let m = _symbols.length - 1; m >= 0; m--) {
33 | this._idsByText.set(_symbols[m], m);
34 | }
35 | }
36 |
37 | protected _numberOfSymbols: number;
38 |
39 | get numberOfSymbols(): number {
40 | return this._numberOfSymbols;
41 | }
42 |
43 | get name(): string {
44 | return this._name;
45 | }
46 |
47 | get version(): number {
48 | return this._version;
49 | }
50 |
51 | getSymbolText(symbolId: number): string | undefined {
52 | if (symbolId < 0) {
53 | throw new Error(
54 | `Index ${symbolId} is out of bounds for the SharedSymbolTable name=${this.name}, version=${this.version}`
55 | );
56 | }
57 | if (symbolId >= this.numberOfSymbols) {
58 | return undefined;
59 | }
60 | return this._symbols[symbolId];
61 | }
62 |
63 | getSymbolId(text: string): number | undefined {
64 | return this._idsByText.get(text);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/IonSubstituteSymbolTable.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import { SharedSymbolTable } from "./IonSharedSymbolTable";
17 |
18 | /**
19 | * A special case of shared symbol table whose entries are all undefined. Used in certain cases
20 | * when an import cannot be satisfied by the current catalog.
21 | * @see https://amazon-ion.github.io/ion-docs/symbols.html#imports
22 | */
23 | export class SubstituteSymbolTable extends SharedSymbolTable {
24 | constructor(length: number) {
25 | if (length < 0) {
26 | throw new Error(
27 | "Cannot instantiate a SubstituteSymbolTable with a negative length. (" +
28 | length +
29 | ")"
30 | );
31 | }
32 | super("_substitute", -1, []);
33 | this._numberOfSymbols = length;
34 | }
35 |
36 | getSymbolText(symbolId: number): string | undefined {
37 | if (symbolId < 0) {
38 | throw new Error(
39 | `Index ${symbolId} is out of bounds for the SharedSymbolTable name=${this.name}, version=${this.version}`
40 | );
41 | }
42 | return undefined;
43 | }
44 |
45 | getSymbolId(text: string): number | undefined {
46 | return undefined;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/IonSymbol.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import { asAscii } from "./IonText";
17 |
18 | export class Symbol {
19 | sid: number;
20 | name: string;
21 |
22 | constructor(id: number, val: string) {
23 | this.sid = id;
24 | this.name = val;
25 | }
26 |
27 | toString(): string {
28 | const s =
29 | "sym::{id:" + asAscii(this.sid) + ',val:"' + asAscii(this.name) + '"';
30 | return s;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/IonSymbolIndex.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | /**
17 | * An interface representing a map of symbol names to symbol ids.
18 | */
19 | export interface SymbolIndex {
20 | [name: string]: number;
21 | }
22 |
--------------------------------------------------------------------------------
/src/IonSymbolToken.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | /**
17 | * An Ion symbol token (used to represent field names, annotations,
18 | * and symbol values) providing both the symbol text and the assigned
19 | * symbol ID.
20 | */
21 | export class SymbolToken {
22 | private static _UNKNOWN_SYMBOL_ID = -1;
23 |
24 | constructor(
25 | private text: string | null,
26 | private sid: number = SymbolToken._UNKNOWN_SYMBOL_ID
27 | ) {}
28 |
29 | /**
30 | * Returns the text of this symbol, or null if the text is unknown.
31 | */
32 | public getText(): string | null {
33 | return this.text;
34 | }
35 |
36 | /**
37 | * Returns the symbol ID (sid) of this symbol, or -1 if the sid is unknown.
38 | */
39 | public getSid(): number {
40 | return this.sid;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/IonSymbols.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import { Catalog } from "./IonCatalog";
17 | import { Import } from "./IonImport";
18 | import { LocalSymbolTable } from "./IonLocalSymbolTable";
19 | import { Reader } from "./IonReader";
20 | import { SharedSymbolTable } from "./IonSharedSymbolTable";
21 | import { SubstituteSymbolTable } from "./IonSubstituteSymbolTable";
22 | import { getSystemSymbolTableImport } from "./IonSystemSymbolTable";
23 | import { IonTypes } from "./Ion";
24 |
25 | export const ion_symbol_table = "$ion_symbol_table";
26 | export const ion_symbol_table_sid = 3;
27 |
28 | const empty_struct = {};
29 |
30 | function load_imports(reader: Reader, catalog: Catalog): Import {
31 | let import_: Import = getSystemSymbolTableImport();
32 |
33 | reader.stepIn(); // into the array
34 | while (reader.next()) {
35 | reader.stepIn(); // into the struct of 1 import
36 |
37 | let name: string | null = null;
38 | let version: number | null = 1;
39 | let maxId: number | null = null;
40 |
41 | while (reader.next()) {
42 | switch (reader.fieldName()) {
43 | case "name":
44 | name = reader.stringValue();
45 | break;
46 | case "version":
47 | version = reader.numberValue();
48 | break;
49 | case "max_id":
50 | maxId = reader.numberValue();
51 | }
52 | }
53 |
54 | if (version === null || version < 1) {
55 | version = 1;
56 | }
57 |
58 | if (name && name !== "$ion") {
59 | let symbolTable: SharedSymbolTable | null = catalog.getVersion(
60 | name,
61 | version!
62 | );
63 | if (!symbolTable) {
64 | if (maxId === undefined) {
65 | throw new Error(
66 | `No exact match found when trying to import symbol table ${name} version ${version}`
67 | );
68 | } else {
69 | symbolTable = catalog.getTable(name);
70 | }
71 | }
72 |
73 | if (!symbolTable) {
74 | symbolTable = new SubstituteSymbolTable(maxId!);
75 | }
76 |
77 | import_ = new Import(import_, symbolTable!, maxId);
78 | }
79 |
80 | reader.stepOut(); // out of one import struct
81 | }
82 | reader.stepOut(); // out of the array of imports
83 |
84 | return import_;
85 | }
86 |
87 | function load_symbols(reader: Reader): (string | null)[] {
88 | const symbols: (string | null)[] = [];
89 |
90 | reader.stepIn();
91 | while (reader.next()) {
92 | symbols.push(reader.stringValue());
93 | }
94 | reader.stepOut();
95 |
96 | return symbols;
97 | }
98 |
99 | /**
100 | * Constructs a {LocalSymbolTable} from the given Ion {Reader}.
101 | *
102 | * @param catalog The catalog to resolve imported shared symbol tables from.
103 | * @param reader The Ion {Reader} over the local symbol table in its serialized form.
104 | * @param currentSymbolTable Current local symbol table for the reader.
105 | */
106 | export function makeSymbolTable(
107 | catalog: Catalog,
108 | reader: Reader,
109 | currentSymbolTable: LocalSymbolTable
110 | ): LocalSymbolTable {
111 | let import_: Import | null = null;
112 | let symbols: (string | null)[] = [];
113 | let foundSymbols: boolean = false;
114 | let foundImports: boolean = false;
115 | let foundLstAppend: boolean = false;
116 |
117 | reader.stepIn();
118 | while (reader.next()) {
119 | switch (reader.fieldName()) {
120 | case "imports":
121 | if (foundImports) {
122 | throw new Error("Multiple import fields found.");
123 | }
124 | let ion_type = reader.type();
125 | if (
126 | ion_type === IonTypes.SYMBOL &&
127 | reader.stringValue() === ion_symbol_table
128 | ) {
129 | // this is a local symbol table append
130 | import_ = currentSymbolTable.import;
131 | let symbols_ = symbols;
132 | symbols = currentSymbolTable.symbols;
133 | symbols.push(...symbols_);
134 | foundLstAppend = true;
135 | } else if (ion_type === IonTypes.LIST) {
136 | import_ = load_imports(reader, catalog);
137 | } else {
138 | throw new Error(
139 | `Expected import field name to be a list or symbol found ${ion_type}`
140 | );
141 | }
142 | foundImports = true;
143 | break;
144 | case "symbols":
145 | if (foundSymbols) {
146 | throw new Error("Multiple symbol fields found.");
147 | }
148 | if (foundLstAppend) {
149 | symbols.push(...load_symbols(reader));
150 | } else {
151 | symbols = load_symbols(reader);
152 | }
153 | foundSymbols = true;
154 | break;
155 | }
156 | }
157 | reader.stepOut();
158 |
159 | return new LocalSymbolTable(import_, symbols);
160 | }
161 |
--------------------------------------------------------------------------------
/src/IonSystemSymbolTable.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | /**
17 | * @file Helper methods for obtaining the system symbol table or its Import.
18 | * @see https://amazon-ion.github.io/ion-docs/docs/symbols.html#system-symbols
19 | */
20 | import { Import } from "./IonImport";
21 | import { SharedSymbolTable } from "./IonSharedSymbolTable";
22 |
23 | const systemSymbolTable: SharedSymbolTable = new SharedSymbolTable("$ion", 1, [
24 | "$ion",
25 | "$ion_1_0",
26 | "$ion_symbol_table",
27 | "name",
28 | "version",
29 | "imports",
30 | "symbols",
31 | "max_id",
32 | "$ion_shared_symbol_table",
33 | ]);
34 |
35 | export function getSystemSymbolTable(): SharedSymbolTable {
36 | return systemSymbolTable;
37 | }
38 |
39 | export function getSystemSymbolTableImport(): Import {
40 | return new Import(null, getSystemSymbolTable());
41 | }
42 |
--------------------------------------------------------------------------------
/src/IonType.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | /** Ion value type enumeration class. */
17 | export class IonType {
18 | constructor(
19 | /** The binary type ID for this Ion Type. */
20 | public readonly binaryTypeId: number,
21 | /** The textual name of this type. */
22 | public readonly name: string,
23 | /** Whether or not this type is a scalar value. */
24 | public readonly isScalar: boolean,
25 | /** Whether or not this type is a `clob` or `blob`. */
26 | public readonly isLob: boolean,
27 | /** Whether or not this type is an `int`, `float`, or `decimal`. */
28 | public readonly isNumeric: boolean,
29 | /** Whether or not this type is a `list`, `sexp`, or `struct`. */
30 | public readonly isContainer: boolean
31 | ) {}
32 | }
33 |
--------------------------------------------------------------------------------
/src/IonTypes.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import { IonType } from "./IonType";
17 |
18 | /** Enumeration of the Ion types. */
19 | export const IonTypes = {
20 | NULL: new IonType(0, "null", true, false, false, false),
21 | BOOL: new IonType(1, "bool", true, false, false, false),
22 | // note that INT is actually 0x2 **and** 0x3 in the Ion binary encoding
23 | INT: new IonType(2, "int", true, false, true, false),
24 | FLOAT: new IonType(4, "float", true, false, true, false),
25 | DECIMAL: new IonType(5, "decimal", true, false, false, false),
26 | TIMESTAMP: new IonType(6, "timestamp", true, false, false, false),
27 | SYMBOL: new IonType(7, "symbol", true, false, false, false),
28 | STRING: new IonType(8, "string", true, false, false, false),
29 | CLOB: new IonType(9, "clob", true, true, false, false),
30 | BLOB: new IonType(10, "blob", true, true, false, false),
31 | LIST: new IonType(11, "list", false, false, false, true),
32 | SEXP: new IonType(12, "sexp", false, false, false, true),
33 | STRUCT: new IonType(13, "struct", false, false, false, true),
34 | };
35 |
--------------------------------------------------------------------------------
/src/IonUnicode.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | const JS_DECODER_MAX_BYTES = 512;
17 |
18 | // Check whether this runtime supports the `TextDecoder` feature
19 | let textDecoder;
20 | // @ts-expect-error: Typescript will complain about TextDecoder being undefined
21 | if (typeof TextDecoder !== "undefined") {
22 | // @ts-expect-error: Typescript will complain about TextDecoder being undefined
23 | textDecoder = new TextDecoder("utf8", { fatal: true });
24 | } else {
25 | textDecoder = null;
26 | }
27 |
28 | /**
29 | * @file Constants and helper methods for Unicode.
30 | * @see https://amazon-ion.github.io/ion-docs/stringclob.html
31 | * @see http://www.unicode.org/versions/Unicode5.0.0/
32 | */
33 | export function encodeUtf8(s: string): Uint8Array {
34 | let i = 0,
35 | c;
36 | const bytes = new Uint8Array(s.length * 4);
37 |
38 | for (let ci = 0; ci < s.length; ci++) {
39 | c = s.charCodeAt(ci);
40 | if (c < 128) {
41 | bytes[i++] = c;
42 | continue;
43 | }
44 | if (c < 2048) {
45 | bytes[i++] = (c >> 6) | 192;
46 | } else {
47 | if (c > 0xd7ff && c < 0xdc00) {
48 | if (++ci >= s.length) {
49 | throw new Error("UTF-8 encode: incomplete surrogate pair");
50 | }
51 | const c2 = s.charCodeAt(ci);
52 | if (c2 < 0xdc00 || c2 > 0xdfff) {
53 | throw new Error(
54 | "UTF-8 encode: second surrogate character 0x" +
55 | c2.toString(16) +
56 | " at index " +
57 | ci +
58 | " out of range"
59 | );
60 | }
61 | c = 0x10000 + ((c & 0x03ff) << 10) + (c2 & 0x03ff);
62 | bytes[i++] = (c >> 18) | 240;
63 | bytes[i++] = ((c >> 12) & 63) | 128;
64 | } else {
65 | bytes[i++] = (c >> 12) | 224;
66 | }
67 | bytes[i++] = ((c >> 6) & 63) | 128;
68 | }
69 | bytes[i++] = (c & 63) | 128;
70 | }
71 | return bytes.subarray(0, i);
72 | }
73 |
74 | export function decodeUtf8(bytes: Uint8Array): string {
75 | // for bytes > 512 use TextDecoder method - decode()
76 | if (bytes.length > JS_DECODER_MAX_BYTES && textDecoder != null) {
77 | return textDecoder.decode(bytes);
78 | }
79 | let i = 0,
80 | s = "",
81 | c;
82 | while (i < bytes.length) {
83 | c = bytes[i++];
84 | if (c > 127) {
85 | if (c > 191 && c < 224) {
86 | if (i >= bytes.length) {
87 | throw new Error("UTF-8 decode: incomplete 2-byte sequence");
88 | }
89 | c = ((c & 31) << 6) | (bytes[i++] & 63);
90 | } else if (c > 223 && c < 240) {
91 | if (i + 1 >= bytes.length) {
92 | throw new Error("UTF-8 decode: incomplete 3-byte sequence");
93 | }
94 | c = ((c & 15) << 12) | ((bytes[i++] & 63) << 6) | (bytes[i++] & 63);
95 | } else if (c > 239 && c < 248) {
96 | if (i + 2 >= bytes.length) {
97 | throw new Error("UTF-8 decode: incomplete 4-byte sequence");
98 | }
99 | c =
100 | ((c & 7) << 18) |
101 | ((bytes[i++] & 63) << 12) |
102 | ((bytes[i++] & 63) << 6) |
103 | (bytes[i++] & 63);
104 | } else {
105 | throw new Error(
106 | "UTF-8 decode: unknown multibyte start 0x" +
107 | c.toString(16) +
108 | " at index " +
109 | (i - 1)
110 | );
111 | }
112 | }
113 | if (c <= 0xffff) {
114 | s += String.fromCharCode(c);
115 | } else if (c <= 0x10ffff) {
116 | c -= 0x10000;
117 | s += String.fromCharCode((c >> 10) | 0xd800);
118 | s += String.fromCharCode((c & 0x3ff) | 0xdc00);
119 | } else {
120 | throw new Error(
121 | "UTF-8 decode: code point 0x" + c.toString(16) + " exceeds UTF-16 reach"
122 | );
123 | }
124 | }
125 | return s;
126 | }
127 |
--------------------------------------------------------------------------------
/src/IonWriteable.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | /**
17 | * A byte array builder.
18 | *
19 | * This implementation attempts to minimize append and allocate operations by writing into
20 | * a pre-allocated array, although additional arrays are allocated as necessary.
21 | */
22 | export class Writeable {
23 | private bufferSize: number;
24 | private buffers: Uint8Array[];
25 | private index: number;
26 | private clean: boolean;
27 |
28 | constructor(bufferSize?: number) {
29 | this.bufferSize = bufferSize ? bufferSize : 4096;
30 | this.buffers = [new Uint8Array(this.bufferSize)];
31 | this.index = 0;
32 | this.clean = false;
33 | }
34 |
35 | get currentBuffer() {
36 | return this.buffers[this.buffers.length - 1];
37 | }
38 |
39 | get totalSize() {
40 | let size = 0;
41 | for (let i = 0; i < this.buffers.length - 1; i++) {
42 | size += this.buffers[i].length;
43 | }
44 | return size + this.index;
45 | }
46 |
47 | writeByte(byte: number) {
48 | this.clean = false;
49 | this.currentBuffer[this.index] = byte;
50 | this.index++;
51 | if (this.index === this.bufferSize) {
52 | this.buffers.push(new Uint8Array(this.bufferSize));
53 | this.index = 0;
54 | }
55 | }
56 |
57 | writeBytes(buf: Uint8Array, offset?: number, length?: number): void {
58 | if (offset === undefined) {
59 | offset = 0;
60 | }
61 |
62 | const writeLength =
63 | length !== undefined
64 | ? Math.min(buf.length - offset, length)
65 | : buf.length - offset;
66 | if (writeLength < this.currentBuffer.length - this.index - 1) {
67 | this.currentBuffer.set(
68 | buf.subarray(offset, offset + writeLength),
69 | this.index
70 | );
71 | this.index += writeLength;
72 | } else {
73 | this.buffers[this.buffers.length - 1] = this.currentBuffer.slice(
74 | 0,
75 | this.index
76 | );
77 | this.buffers.push(buf.subarray(offset, length));
78 | this.buffers.push(new Uint8Array(this.bufferSize));
79 | this.clean = false;
80 | this.index = 0;
81 | }
82 | }
83 |
84 | getBytes(): Uint8Array {
85 | if (this.clean) {
86 | return this.buffers[0];
87 | }
88 | const buffer = new Uint8Array(this.totalSize);
89 | let tempLength = 0;
90 | for (let i = 0; i < this.buffers.length - 1; i++) {
91 | buffer.set(this.buffers[i], tempLength);
92 | tempLength += this.buffers[i].length;
93 | }
94 | buffer.set(this.currentBuffer.subarray(0, this.index), tempLength);
95 | this.buffers = [buffer, new Uint8Array(this.bufferSize)];
96 | this.index = 0;
97 | this.clean = true;
98 | return buffer;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/SignAndMagnitudeInt.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | /**
17 | * Represents a signed, arbitrarily sized integer.
18 | *
19 | * BigInts cannot represent negative zero. This class should be used in situations where negative zero is
20 | * a supported value, such as when decoding binary Ion Int/VarInt.
21 | *
22 | * https://amazon-ion.github.io/ion-docs/docs/binary.html#uint-and-int-fields
23 | *
24 | */
25 | export default class SignAndMagnitudeInt {
26 | constructor(
27 | public readonly _magnitude: bigint,
28 | public readonly _isNegative = _magnitude < 0n
29 | ) {}
30 |
31 | get magnitude(): bigint {
32 | return this._magnitude;
33 | }
34 |
35 | get isNegative(): boolean {
36 | return this._isNegative;
37 | }
38 |
39 | public static fromNumber(value: number): SignAndMagnitudeInt {
40 | const isNegative = value < 0 || Object.is(value, -0);
41 | const absoluteValue = Math.abs(value);
42 | const magnitude = BigInt(absoluteValue);
43 | return new SignAndMagnitudeInt(magnitude, isNegative);
44 | }
45 |
46 | public equals(other: SignAndMagnitudeInt): boolean {
47 | return (
48 | this._magnitude === other._magnitude &&
49 | this._isNegative === other._isNegative
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/dom/Blob.ts:
--------------------------------------------------------------------------------
1 | import { IonTypes, toBase64, Writer } from "../Ion";
2 | import { Lob } from "./Lob";
3 |
4 | /**
5 | * Represents a blob[1] value in an Ion stream.
6 | *
7 | * [1] https://amazon-ion.github.io/ion-docs/docs/spec.html#blob
8 | */
9 | export class Blob extends Lob(IonTypes.BLOB) {
10 | /**
11 | * Constructor.
12 | * @param data Raw, unsigned bytes to represent as a blob.
13 | * @param annotations An optional array of strings to associate with `data`.
14 | */
15 | constructor(data: Uint8Array, annotations: string[] = []) {
16 | super(data, annotations);
17 | }
18 |
19 | /**
20 | * Converts this Blob to a base64-encoded string when being serialized with `JSON.stringify()`.
21 | */
22 | toJSON() {
23 | return toBase64(this);
24 | }
25 |
26 | writeTo(writer: Writer): void {
27 | writer.setAnnotations(this.getAnnotations());
28 | writer.writeBlob(this);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/dom/Boolean.ts:
--------------------------------------------------------------------------------
1 | import { IonTypes, Writer } from "../Ion";
2 | import {
3 | FromJsConstructor,
4 | FromJsConstructorBuilder,
5 | Primitives,
6 | } from "./FromJsConstructor";
7 | import { _NativeJsBoolean } from "./JsValueConversion";
8 | import { Value } from "./Value";
9 |
10 | const _fromJsConstructor: FromJsConstructor = new FromJsConstructorBuilder()
11 | .withPrimitives(Primitives.Boolean)
12 | .withClassesToUnbox(_NativeJsBoolean)
13 | .build();
14 |
15 | /**
16 | * Represents a boolean[1] value in an Ion stream.
17 | *
18 | * Because this class extends Javascript's (big-B) Boolean data type, it is subject to the same
19 | * surprising behavior when used for control flow.
20 | *
21 | * From the Mozilla Developer Network documentation[2]:
22 | *
23 | * > Any object of which the value is not undefined or null, including a Boolean object
24 | * whose value is false, evaluates to true when passed to a conditional statement.
25 | *
26 | * ```javascript
27 | * var b = false;
28 | * if (b) {
29 | * // this code will NOT be executed
30 | * }
31 | *
32 | * b = new Boolean(false);
33 | * if (b) {
34 | * // this code WILL be executed
35 | * }
36 | * ```
37 | *
38 | * [1] https://amazon-ion.github.io/ion-docs/docs/spec.html#bool
39 | * [2] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean#Description
40 | */
41 | export class Boolean extends Value(
42 | _NativeJsBoolean,
43 | IonTypes.BOOL,
44 | _fromJsConstructor
45 | ) {
46 | /**
47 | * Constructor.
48 | * @param value The boolean value of the new instance.
49 | * @param annotations An optional array of strings to associate with `value`.
50 | */
51 | constructor(value: boolean, annotations: string[] = []) {
52 | super(value);
53 | this._setAnnotations(annotations);
54 | }
55 |
56 | booleanValue(): boolean {
57 | return this.valueOf() as boolean;
58 | }
59 |
60 | writeTo(writer: Writer): void {
61 | writer.setAnnotations(this.getAnnotations());
62 | writer.writeBoolean(this.booleanValue());
63 | }
64 |
65 | _valueEquals(
66 | other: any,
67 | options: {
68 | epsilon?: number | null;
69 | ignoreAnnotations?: boolean;
70 | ignoreTimestampPrecision?: boolean;
71 | onlyCompareIon?: boolean;
72 | } = {
73 | epsilon: null,
74 | ignoreAnnotations: false,
75 | ignoreTimestampPrecision: false,
76 | onlyCompareIon: true,
77 | }
78 | ): boolean {
79 | let isSupportedType: boolean = false;
80 | let valueToCompare: any = null;
81 | // if the provided value is an ion.dom.Boolean instance.
82 | if (other instanceof Boolean) {
83 | isSupportedType = true;
84 | valueToCompare = other.booleanValue();
85 | } else if (!options.onlyCompareIon) {
86 | // We will consider other Boolean-ish types
87 | if (typeof other === "boolean" || other instanceof _NativeJsBoolean) {
88 | isSupportedType = true;
89 | valueToCompare = other.valueOf();
90 | }
91 | }
92 |
93 | if (!isSupportedType) {
94 | return false;
95 | }
96 |
97 | if (this.booleanValue() !== valueToCompare) {
98 | return false;
99 | }
100 | return true;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/dom/Clob.ts:
--------------------------------------------------------------------------------
1 | import { IonTypes, Writer } from "../Ion";
2 | import { Lob } from "./Lob";
3 |
4 | /**
5 | * Represents a clob[1] value in an Ion stream.
6 | *
7 | * [1] https://amazon-ion.github.io/ion-docs/docs/spec.html#clob
8 | */
9 | export class Clob extends Lob(IonTypes.CLOB) {
10 | /**
11 | * Constructor.
12 | * @param bytes Raw, unsigned bytes to represent as a clob.
13 | * @param annotations An optional array of strings to associate with `bytes`.
14 | */
15 | constructor(bytes: Uint8Array, annotations: string[] = []) {
16 | super(bytes, annotations);
17 | }
18 |
19 | writeTo(writer: Writer): void {
20 | writer.setAnnotations(this.getAnnotations());
21 | writer.writeClob(this);
22 | }
23 |
24 | toJSON() {
25 | // Because the text encoding of the bytes stored in this Clob is unknown,
26 | // we write each byte out as a Unicode escape (e.g. 127 -> 0x7f -> \u007f)
27 | // unless it happens to fall within the ASCII range.
28 | // See the Ion cookbook's guide to down-converting to JSON for details:
29 | // https://amazon-ion.github.io/ion-docs/guides/cookbook.html#down-converting-to-json
30 | let encodedText = "";
31 | for (const byte of this) {
32 | if (byte >= 32 && byte <= 126) {
33 | encodedText += String.fromCharCode(byte);
34 | continue;
35 | }
36 | const hex = byte.toString(16);
37 | if (hex.length == 1) {
38 | encodedText += "\\u000" + hex;
39 | } else {
40 | encodedText += "\\u00" + hex;
41 | }
42 | }
43 | return encodedText;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/dom/Decimal.ts:
--------------------------------------------------------------------------------
1 | import * as ion from "../Ion";
2 | import { IonTypes } from "../Ion";
3 | import { Writer } from "../Ion";
4 | import {
5 | FromJsConstructor,
6 | FromJsConstructorBuilder,
7 | } from "./FromJsConstructor";
8 | import { Value } from "./Value";
9 | import { Float } from "./Float";
10 |
11 | const _fromJsConstructor: FromJsConstructor = new FromJsConstructorBuilder()
12 | .withClasses(ion.Decimal)
13 | .build();
14 |
15 | /**
16 | * Represents a decimal[1] value in an Ion stream.
17 | *
18 | * [1] https://amazon-ion.github.io/ion-docs/docs/spec.html#decimal
19 | */
20 | export class Decimal extends Value(
21 | Number,
22 | IonTypes.DECIMAL,
23 | _fromJsConstructor
24 | ) {
25 | private readonly _decimalValue: ion.Decimal;
26 | private readonly _numberValue: number;
27 |
28 | /**
29 | * Constructor.
30 | * @param value The Ion decimal value to represent as a decimal.
31 | * @param annotations An optional array of strings to associate with `value`.
32 | */
33 | constructor(value: ion.Decimal, annotations?: string[]);
34 |
35 | /**
36 | * Constructor.
37 | * @param value The text Ion value to be parsed as a decimal.
38 | * @param annotations An optional array of strings to associate with `value`.
39 | */
40 | constructor(value: string, annotations?: string[]);
41 |
42 | /**
43 | * Constructor.
44 | * @param value The number value to represent as a decimal.
45 | * @param annotations An optional array of strings to associate with `value`.
46 | */
47 | constructor(value: number, annotations?: string[]);
48 |
49 | // This is the unified implementation of the above signatures and is not visible to users.
50 | constructor(
51 | value: ion.Decimal | string | number,
52 | annotations: string[] = []
53 | ) {
54 | if (typeof value === "string") {
55 | let numberValue = Number(value);
56 | super(numberValue);
57 | this._decimalValue = new ion.Decimal(value);
58 | this._numberValue = numberValue;
59 | } else if (value instanceof ion.Decimal) {
60 | super(value.numberValue());
61 | this._decimalValue = value;
62 | this._numberValue = value.numberValue();
63 | } else if (typeof value === "number") {
64 | // if value is a number type
65 | super(value);
66 | this._decimalValue = new ion.Decimal("" + value);
67 | this._numberValue = value;
68 | } else {
69 | throw new Error(
70 | "Decimal value can only be created from number, ion.Decimal or string"
71 | );
72 | }
73 | this._setAnnotations(annotations);
74 | }
75 |
76 | numberValue(): number {
77 | return this._numberValue;
78 | }
79 |
80 | decimalValue(): ion.Decimal {
81 | return this._decimalValue;
82 | }
83 |
84 | toString(): string {
85 | return this._decimalValue.toString();
86 | }
87 |
88 | valueOf(): number {
89 | return this._numberValue;
90 | }
91 |
92 | writeTo(writer: Writer): void {
93 | writer.setAnnotations(this.getAnnotations());
94 | writer.writeDecimal(this.decimalValue());
95 | }
96 |
97 | _valueEquals(
98 | other: any,
99 | options: {
100 | epsilon?: number | null;
101 | ignoreAnnotations?: boolean;
102 | ignoreTimestampPrecision?: boolean;
103 | onlyCompareIon?: boolean;
104 | coerceNumericType: boolean;
105 | } = {
106 | epsilon: null,
107 | ignoreAnnotations: false,
108 | ignoreTimestampPrecision: false,
109 | onlyCompareIon: true,
110 | coerceNumericType: false,
111 | }
112 | ): boolean {
113 | let isSupportedType: boolean = false;
114 | let valueToCompare: any = null;
115 | // if the provided value is an ion.dom.Decimal instance.
116 | if (other instanceof Decimal) {
117 | isSupportedType = true;
118 | valueToCompare = other.decimalValue();
119 | } else if (options.coerceNumericType === true && other instanceof Float) {
120 | isSupportedType = true;
121 | valueToCompare = new ion.Decimal(other.toString());
122 | } else if (!options.onlyCompareIon) {
123 | // We will consider other Decimal-ish types
124 | if (other instanceof ion.Decimal) {
125 | // expectedValue is a non-DOM Decimal
126 | isSupportedType = true;
127 | valueToCompare = other;
128 | } else if (other instanceof Number || typeof other === "number") {
129 | isSupportedType = true;
130 | // calling numberValue() on ion.Decimal is lossy and could result in imprecise comparisons
131 | // hence converting number to ion.Decimal for comparison even though it maybe expensive
132 | valueToCompare = new ion.Decimal(other.toString());
133 | }
134 | }
135 |
136 | if (!isSupportedType) {
137 | return false;
138 | }
139 |
140 | return this.decimalValue().equals(valueToCompare);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/dom/Float.ts:
--------------------------------------------------------------------------------
1 | import { IonTypes, Writer } from "../Ion";
2 | import {
3 | FromJsConstructor,
4 | FromJsConstructorBuilder,
5 | Primitives,
6 | } from "./FromJsConstructor";
7 | import { Value } from "./Value";
8 | import { Decimal } from "./Decimal";
9 | import * as ion from "../Ion";
10 |
11 | const _fromJsConstructor: FromJsConstructor = new FromJsConstructorBuilder()
12 | .withPrimitives(Primitives.Number)
13 | .withClassesToUnbox(Number)
14 | .build();
15 |
16 | /**
17 | * Represents a float[1] value in an Ion stream.
18 | *
19 | * [1] https://amazon-ion.github.io/ion-docs/docs/spec.html#float
20 | */
21 | export class Float extends Value(Number, IonTypes.FLOAT, _fromJsConstructor) {
22 | /**
23 | * Constructor.
24 | * @param value The numeric value to represent as a float.
25 | * @param annotations An optional array of strings to associate with `value`.
26 | */
27 | constructor(value: number, annotations: string[] = []) {
28 | super(value);
29 | this._setAnnotations(annotations);
30 | }
31 |
32 | public numberValue(): number {
33 | return +this.valueOf();
34 | }
35 |
36 | writeTo(writer: Writer): void {
37 | writer.setAnnotations(this.getAnnotations());
38 | writer.writeFloat64(this.numberValue());
39 | }
40 |
41 | _valueEquals(
42 | other: any,
43 | options: {
44 | epsilon?: number | null;
45 | ignoreAnnotations?: boolean;
46 | ignoreTimestampPrecision?: boolean;
47 | onlyCompareIon?: boolean;
48 | coerceNumericType: boolean;
49 | } = {
50 | epsilon: null,
51 | ignoreAnnotations: false,
52 | ignoreTimestampPrecision: false,
53 | onlyCompareIon: true,
54 | coerceNumericType: false,
55 | }
56 | ): boolean {
57 | let isSupportedType: boolean = false;
58 | let valueToCompare: any = null;
59 | // if the provided value is an ion.dom.Float instance.
60 | if (other instanceof Float) {
61 | isSupportedType = true;
62 | valueToCompare = other.numberValue();
63 | } else if (options.coerceNumericType === true && other instanceof Decimal) {
64 | // if other is Decimal convert both values to Decimal for comparison.
65 | let thisValue = new ion.Decimal(other.toString());
66 | return thisValue!.equals(other.decimalValue());
67 | } else if (!options.onlyCompareIon) {
68 | // We will consider other Float-ish types
69 | if (other instanceof Number || typeof other === "number") {
70 | isSupportedType = true;
71 | valueToCompare = other.valueOf();
72 | }
73 | }
74 |
75 | if (!isSupportedType) {
76 | return false;
77 | }
78 |
79 | let result: boolean = Object.is(this.numberValue(), valueToCompare);
80 |
81 | if (options.epsilon != null) {
82 | if (
83 | result ||
84 | Math.abs(this.numberValue() - valueToCompare) <= options.epsilon
85 | ) {
86 | return true;
87 | }
88 | }
89 | return result;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/dom/FromJsConstructor.ts:
--------------------------------------------------------------------------------
1 | import { dom, IonTypes } from "../Ion";
2 | import { _hasValue } from "../util";
3 | import { Constructor, Value } from "./Value";
4 |
5 | // Converts the provided Iterable into a Set. If no iterable is provided, returns an empty set.
6 | function _newSet(values?: Iterable): Set {
7 | if (_hasValue(values)) {
8 | return new Set(values);
9 | }
10 | return new Set();
11 | }
12 |
13 | /**
14 | * Builder to configure and instantiate FromJsConstructor objects. See the documentation for
15 | * the FromJsConstructor class for a description of each field.
16 | *
17 | * Package-visible for use in dom.Value subclass definitions.
18 | * @private
19 | */
20 | export class FromJsConstructorBuilder {
21 | private _primitives: Set;
22 | private _classesToUnbox: Set;
23 | private _classes: Set;
24 |
25 | constructor() {
26 | this._primitives = _newSet();
27 | this._classesToUnbox = _newSet();
28 | this._classes = _newSet();
29 | }
30 |
31 | withPrimitives(...primitives: string[]): FromJsConstructorBuilder {
32 | this._primitives = _newSet(primitives);
33 | return this;
34 | }
35 |
36 | withClasses(...classes: Constructor[]): FromJsConstructorBuilder {
37 | this._classes = _newSet(classes);
38 | return this;
39 | }
40 |
41 | withClassesToUnbox(...classes: Constructor[]): FromJsConstructorBuilder {
42 | this._classesToUnbox = _newSet(classes);
43 | return this;
44 | }
45 |
46 | build(): FromJsConstructor {
47 | return new FromJsConstructor(
48 | this._primitives,
49 | this._classesToUnbox,
50 | this._classes
51 | );
52 | }
53 | }
54 |
55 | /**
56 | * Provides common conversion and validation logic needed to instantiate subclasses of dom.Value
57 | * from a given Javascript value of unknown type (`any`) and an optional annotations array.
58 | *
59 | * Given a Javascript value, will test its type to see whether it is:
60 | * 1. A primitive that is supported by the specified constructor.
61 | * 2. A boxed primitive that is supported by the specified constructor if unboxed via `valueOf()`.
62 | * 3. A class that is supported by the specified constructor as-is, including boxed primitives.
63 | *
64 | * If the value matches any of the above descriptions, the provided constructor will be invoked
65 | * with the value; otherwise, throws an Error.
66 | *
67 | * Constructors are expected to be compatible with the signature:
68 | *
69 | * constructor(value, annotations: string[]): ClassName
70 | *
71 | * See also: Value._fromJsValue()
72 | */
73 | export class FromJsConstructor {
74 | /**
75 | * Constructor.
76 | *
77 | * @param _primitives Primitive types that will be passed through as-is.
78 | * @param _classesToUnbox Boxed primitive types that will be converted to primitives via
79 | * `valueOf()` and then passed through.
80 | * @param _classes Classes that will be passed through as-is.
81 | */
82 | constructor(
83 | private readonly _primitives: Set,
84 | private readonly _classesToUnbox: Set,
85 | private readonly _classes: Set
86 | ) {}
87 |
88 | /**
89 | * Invokes the provided constructor if `jsValue` is of a supported data type; otherwise
90 | * throws an Error.
91 | *
92 | * @param constructor A dom.Value subclass's constructor to call.
93 | * @param jsValue A Javascript value to validate/unbox before passing through to
94 | * the constructor.
95 | * @param annotations An optional array of strings to associate with the newly constructed
96 | * dom.Value.
97 | */
98 | construct(constructor: any, jsValue: any, annotations: string[] = []): Value {
99 | if (jsValue === null) {
100 | return new dom.Null(IonTypes.NULL, annotations);
101 | }
102 |
103 | const jsValueType = typeof jsValue;
104 | if (jsValueType === "object") {
105 | // jsValue is an unsupported boxed primitive, but we can use it if we convert it to a primitive first
106 | if (this._classesToUnbox.has(jsValue.constructor)) {
107 | return new constructor(jsValue.valueOf(), annotations);
108 | }
109 |
110 | // jsValue is an Object of a supported type, including boxed primitives
111 | if (this._classes.has(jsValue.constructor)) {
112 | return new constructor(jsValue, annotations);
113 | }
114 |
115 | throw new Error(
116 | `Unable to construct a(n) ${constructor.name} from a ${jsValue.constructor.name}.`
117 | );
118 | }
119 |
120 | if (this._primitives.has(jsValueType)) {
121 | return new constructor(jsValue, annotations);
122 | }
123 |
124 | throw new Error(
125 | `Unable to construct a(n) ${constructor.name} from a ${jsValueType}.`
126 | );
127 | }
128 | }
129 |
130 | // This namespace will be merged with the class definition above. This allows static values to invoke functions in the
131 | // FromJsConstructor class after the class has been fully initialized.
132 | export namespace FromJsConstructor {
133 | // Useful for dom.Value subclasses that do not use a FromJsConstructor (e.g. Struct, List).
134 | // Because it has no supported types, any attempt to use this instance will throw an Error.
135 | export const NONE: FromJsConstructor = new FromJsConstructorBuilder().build();
136 | }
137 |
138 | /**
139 | * A mapping of primitive types to the corresponding string that will be returned by
140 | * the `typeof` operator.
141 | */
142 | export const Primitives = {
143 | /*
144 | * Some possible values are not included because they are unsupported. In particular:
145 | * - "object" indicates that a value is not a primitive.
146 | * - The mapping from Javascript's "symbol" to Ion's type system is not yet clear.
147 | * - No constructors accept "undefined" as a parameter.
148 | */
149 | Boolean: "boolean",
150 | Number: "number",
151 | String: "string",
152 | BigInt: "bigint",
153 | };
154 |
--------------------------------------------------------------------------------
/src/dom/Integer.ts:
--------------------------------------------------------------------------------
1 | import { IonTypes, Writer } from "../Ion";
2 | import {
3 | FromJsConstructor,
4 | FromJsConstructorBuilder,
5 | Primitives,
6 | } from "./FromJsConstructor";
7 | import { Constructor, Value } from "./Value";
8 |
9 | // BigInt is an irregular class type in that it provides no constructor, only static
10 | // constructor methods. This means that bigint does not conform to the typical Constructor
11 | // interface of new(...args) => any. Because FromJsConstructor will only use it for
12 | // instanceof tests, we can safely cast it as a Constructor to satisfy the compiler.
13 | const _bigintConstructor: Constructor = (BigInt as unknown) as Constructor;
14 | const _fromJsConstructor: FromJsConstructor = new FromJsConstructorBuilder()
15 | .withPrimitives(Primitives.Number, Primitives.BigInt)
16 | .withClassesToUnbox(Number)
17 | .withClasses(_bigintConstructor)
18 | .build();
19 |
20 | /**
21 | * Represents an integer value in an Ion stream.
22 | *
23 | * [1] https://amazon-ion.github.io/ion-docs/docs/spec.html#int
24 | */
25 | export class Integer extends Value(Number, IonTypes.INT, _fromJsConstructor) {
26 | private _bigIntValue: bigint | null;
27 | private _numberValue: number;
28 |
29 | /**
30 | * Constructor.
31 | * @param value The numeric value to represent as an integer.
32 | * @param annotations An optional array of strings to associate with `value`.
33 | */
34 | constructor(value: bigint | number, annotations: string[] = []) {
35 | // If the provided value is a JS number, we will defer constructing a BigInt representation
36 | // of it until it's requested later by a call to bigIntValue().
37 | if (typeof value === "number") {
38 | super(value);
39 | this._numberValue = value;
40 | this._bigIntValue = null;
41 | } else {
42 | const numberValue: number = Number(value);
43 | super(numberValue);
44 | this._bigIntValue = value;
45 | this._numberValue = numberValue;
46 | }
47 | this._setAnnotations(annotations);
48 | }
49 |
50 | bigIntValue(): bigint {
51 | if (this._bigIntValue === null) {
52 | this._bigIntValue = BigInt(this.numberValue());
53 | }
54 | return this._bigIntValue;
55 | }
56 |
57 | numberValue(): number {
58 | return this._numberValue;
59 | }
60 |
61 | toString(): string {
62 | if (this._bigIntValue === null) {
63 | return this._numberValue.toString();
64 | }
65 | return this._bigIntValue.toString();
66 | }
67 |
68 | valueOf() {
69 | return this.numberValue();
70 | }
71 |
72 | writeTo(writer: Writer): void {
73 | writer.setAnnotations(this.getAnnotations());
74 | if (this._bigIntValue === null) {
75 | writer.writeInt(this.numberValue());
76 | } else {
77 | writer.writeInt(this._bigIntValue);
78 | }
79 | }
80 |
81 | _valueEquals(
82 | other: any,
83 | options: {
84 | epsilon?: number | null;
85 | ignoreAnnotations?: boolean;
86 | ignoreTimestampPrecision?: boolean;
87 | onlyCompareIon?: boolean;
88 | } = {
89 | epsilon: null,
90 | ignoreAnnotations: false,
91 | ignoreTimestampPrecision: false,
92 | onlyCompareIon: true,
93 | }
94 | ): boolean {
95 | let isSupportedType: boolean = false;
96 | let valueToCompare: any = null;
97 |
98 | // if the provided value is an ion.dom.Integer instance.
99 | if (other instanceof Integer) {
100 | isSupportedType = true;
101 | if (this._bigIntValue == null && other._bigIntValue == null) {
102 | valueToCompare = other.numberValue();
103 | } else {
104 | // One of them is a bigint
105 | valueToCompare = other.bigIntValue();
106 | }
107 | } else if (!options.onlyCompareIon) {
108 | // We will consider other Integer-ish types
109 | if (other instanceof Number || typeof other === "number") {
110 | isSupportedType = true;
111 | if (this.bigIntValue == null) {
112 | valueToCompare = other.valueOf();
113 | } else {
114 | valueToCompare = BigInt(other.valueOf());
115 | }
116 | } else if (typeof other === "bigint") {
117 | isSupportedType = true;
118 | valueToCompare = other;
119 | }
120 | }
121 |
122 | if (!isSupportedType) {
123 | return false;
124 | }
125 |
126 | if (typeof valueToCompare === "bigint") {
127 | return this.bigIntValue() === valueToCompare;
128 | }
129 | return this.numberValue() == valueToCompare;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/dom/JsValueConversion.ts:
--------------------------------------------------------------------------------
1 | import { Decimal, dom, Timestamp } from "../Ion";
2 | import { IonType } from "../IonType";
3 | import { IonTypes } from "../IonTypes";
4 | import { _hasValue } from "../util";
5 | import { Value } from "./Value";
6 |
7 | // In ./Boolean.ts and ./String.ts, the native Boolean and String types
8 | // are shadowed by the export class defined in the file, but said class
9 | // definitions still need to reference the original class definitions.
10 | // This provides a mechanism for doing so.
11 | export const _NativeJsBoolean = Boolean;
12 | export const _NativeJsString = String;
13 |
14 | // Typescript interfaces can be used to describe classes' static methods; this can
15 | // be a bit surprising as the methods in the interface are not marked 'static' and
16 | // the class definition does not need to indicate that it implements this interface.
17 | //
18 | // This interface describes classes that offer a static constructor that accepts an
19 | // untyped Javascript value and an optional annotations array as its parameter. Because
20 | // the dom.Value mixin provides a default implementation of this method, all dom.Value
21 | // subtypes implicitly implement this interface.
22 | //
23 | // Package visible for testing.
24 | export interface FromJsValue {
25 | _fromJsValue(jsValue: any, annotations: string[]): Value;
26 | }
27 |
28 | // When Typescript is compiling this code, dom/index.ts depends on Value.ts which in turn
29 | // depends on this file. This means that 'dom' is still being defined and we cannot yet
30 | // reference any of the Value subtypes (dom.Integer, dom.String, etc). We can sidestep this
31 | // issue by lazily initializing this mapping, deferring any reference to those types until
32 | // they are first used.
33 | let _domTypesByIonType: Map | null = null;
34 |
35 | // Lazily initializes our IonType -> Dom type constructor mapping.
36 | function _getDomTypesByIonTypeMap(): Map {
37 | if (_domTypesByIonType === null) {
38 | // Clob, SExpression, and Symbol are not included here as _inferType always
39 | // favors Blob, List, and String respectively.
40 | _domTypesByIonType = new Map([
41 | [IonTypes.NULL, dom.Null],
42 | [IonTypes.BOOL, dom.Boolean],
43 | [IonTypes.INT, dom.Integer],
44 | [IonTypes.FLOAT, dom.Float],
45 | [IonTypes.DECIMAL, dom.Decimal],
46 | [IonTypes.TIMESTAMP, dom.Timestamp],
47 | [IonTypes.STRING, dom.String],
48 | [IonTypes.BLOB, dom.Blob],
49 | [IonTypes.LIST, dom.List],
50 | [IonTypes.STRUCT, dom.Struct],
51 | ]);
52 | }
53 | return _domTypesByIonType;
54 | }
55 |
56 | // Returns a dom type constructor for the provided IonType.
57 | // This function is exported to assist in unit testing but is not visible at the library level.
58 | export function _domConstructorFor(ionType: IonType): FromJsValue {
59 | const domConstructor = _getDomTypesByIonTypeMap().get(ionType)!;
60 | if (!_hasValue(domConstructor)) {
61 | throw new Error(
62 | `No dom type constructor was found for Ion type ${ionType.name}`
63 | );
64 | }
65 | return domConstructor;
66 | }
67 |
68 | // Examines the provided JS value and returns the IonType best suited to represent it.
69 | // This function will never return Clob, SExpression, and Symbol; it will always
70 | // favor Blob, List, and String respectively.
71 | function _inferType(value: any): IonType {
72 | if (value === undefined) {
73 | throw new Error("Cannot create an Ion value from `undefined`.");
74 | }
75 | if (value === null) {
76 | return IonTypes.NULL;
77 | }
78 |
79 | const valueType: string = typeof value;
80 | switch (valueType) {
81 | case "string":
82 | return IonTypes.STRING;
83 | case "number":
84 | return Number.isInteger(value) ? IonTypes.INT : IonTypes.FLOAT;
85 | case "boolean":
86 | return IonTypes.BOOL;
87 | case "object":
88 | break;
89 | case "bigint":
90 | return IonTypes.INT;
91 | // TODO: Javascript symbols are not a 1:1 match for Ion symbols, but we may wish
92 | // to support them in Value.from() anyway.
93 | // case "symbol":
94 | default:
95 | throw new Error(
96 | `Value.from() does not support the JS primitive type ${valueType}.`
97 | );
98 | }
99 |
100 | if (value instanceof BigInt) {
101 | return IonTypes.INT;
102 | }
103 | if (value instanceof Number) {
104 | return Number.isInteger(value.valueOf()) ? IonTypes.INT : IonTypes.FLOAT;
105 | }
106 | if (value instanceof Boolean) {
107 | return IonTypes.BOOL;
108 | }
109 | if (value instanceof String) {
110 | return IonTypes.STRING;
111 | }
112 | if (value instanceof Decimal) {
113 | return IonTypes.DECIMAL;
114 | }
115 | if (value instanceof Date || value instanceof Timestamp) {
116 | return IonTypes.TIMESTAMP;
117 | }
118 | if (value instanceof Uint8Array) {
119 | return IonTypes.BLOB;
120 | }
121 | if (value instanceof Array) {
122 | return IonTypes.LIST;
123 | }
124 | return IonTypes.STRUCT;
125 | }
126 |
127 | // Inspects the provided JS value and constructs a new instance of the appropriate Ion
128 | // type using that value.
129 | export function _ionValueFromJsValue(
130 | value: any,
131 | annotations: string[] = []
132 | ): Value {
133 | const ionType = _inferType(value);
134 | const ionTypeConstructor: FromJsValue = _domConstructorFor(ionType);
135 | return ionTypeConstructor._fromJsValue(value, annotations);
136 | }
137 |
--------------------------------------------------------------------------------
/src/dom/List.ts:
--------------------------------------------------------------------------------
1 | import { IonTypes } from "../Ion";
2 | import { Sequence } from "./Sequence";
3 | import { Value } from "./Value";
4 |
5 | /**
6 | * Represents a list value in an Ion stream.
7 | *
8 | * [1] https://amazon-ion.github.io/ion-docs/docs/spec.html#list
9 | */
10 | export class List extends Sequence(IonTypes.LIST) {
11 | /**
12 | * Constructor.
13 | * @param children Values that will be contained in the new list.
14 | * @param annotations An optional array of strings to associate with the items in `children`.
15 | */
16 | constructor(children: Value[], annotations: string[] = []) {
17 | super(children, annotations);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/dom/Lob.ts:
--------------------------------------------------------------------------------
1 | import { IonType, IonTypes } from "../Ion";
2 | import {
3 | FromJsConstructor,
4 | FromJsConstructorBuilder,
5 | } from "./FromJsConstructor";
6 | import { Value } from "./Value";
7 |
8 | const _fromJsConstructor: FromJsConstructor = new FromJsConstructorBuilder()
9 | .withClasses(Uint8Array)
10 | .build();
11 |
12 | /**
13 | * This mixin constructs a new class that:
14 | * - Extends `DomValue`
15 | * - Extends `Uint8Array`
16 | * - Has the specified `IonType`.
17 | *
18 | * In practice, serves as a common base class for `Blob` and `Clob`.
19 | *
20 | * @param ionType The IonType to associate with the new DomValue subclass.
21 | * @constructor
22 | * @private
23 | */
24 | export function Lob(ionType: IonType) {
25 | return class
26 | extends Value(Uint8Array, ionType, _fromJsConstructor)
27 | implements Value {
28 | protected constructor(data: Uint8Array, annotations: string[] = []) {
29 | super(data);
30 | this._setAnnotations(annotations);
31 | }
32 |
33 | uInt8ArrayValue(): Uint8Array {
34 | return this;
35 | }
36 |
37 | _valueEquals(
38 | other: any,
39 | options: {
40 | epsilon?: number | null;
41 | ignoreAnnotations?: boolean;
42 | ignoreTimestampPrecision?: boolean;
43 | onlyCompareIon?: boolean;
44 | } = {
45 | epsilon: null,
46 | ignoreAnnotations: false,
47 | ignoreTimestampPrecision: false,
48 | onlyCompareIon: true,
49 | }
50 | ): boolean {
51 | let isSupportedType: boolean = false;
52 | let valueToCompare: any = null;
53 | if (options.onlyCompareIon) {
54 | // `compareOnlyIon` requires that the provided value be an ion.dom.Lob instance.
55 | if (
56 | other.getType() === IonTypes.CLOB ||
57 | other.getType() === IonTypes.BLOB
58 | ) {
59 | isSupportedType = true;
60 | valueToCompare = other.uInt8ArrayValue();
61 | }
62 | } else {
63 | // We will consider other Lob-ish types
64 | if (other instanceof Uint8Array) {
65 | isSupportedType = true;
66 | valueToCompare = other.valueOf();
67 | }
68 | }
69 |
70 | if (!isSupportedType) {
71 | return false;
72 | }
73 |
74 | let current = this.uInt8ArrayValue();
75 | let expected = valueToCompare;
76 | if (current.length !== expected.length) {
77 | return false;
78 | }
79 | for (let i = 0; i < current.length; i++) {
80 | if (current[i] !== expected[i]) {
81 | return false;
82 | }
83 | }
84 | return true;
85 | }
86 | };
87 | }
88 |
--------------------------------------------------------------------------------
/src/dom/SExpression.ts:
--------------------------------------------------------------------------------
1 | import { IonTypes } from "../Ion";
2 | import { Sequence } from "./Sequence";
3 | import { Value } from "./Value";
4 |
5 | /**
6 | * Represents an s-expression[1] value in an Ion stream.
7 | *
8 | * [1] https://amazon-ion.github.io/ion-docs/docs/spec.html#sexp
9 | */
10 | export class SExpression extends Sequence(IonTypes.SEXP) {
11 | /**
12 | * Constructor.
13 | * @param children Values that will be contained in the new s-expression.
14 | * @param annotations An optional array of strings to associate with the items in `children`.
15 | */
16 | constructor(children: Value[], annotations: string[] = []) {
17 | super(children, annotations);
18 | }
19 |
20 | toString(): string {
21 | return "(" + this.join(" ") + ")";
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/dom/Sequence.ts:
--------------------------------------------------------------------------------
1 | import { IonType, IonTypes, Writer } from "../Ion";
2 | import { FromJsConstructor } from "./FromJsConstructor";
3 | import { PathElement, Value } from "./Value";
4 |
5 | /**
6 | * This mixin constructs a new class that:
7 | * - Extends `DomValue`
8 | * - Extends `Array`
9 | * - Has the specified `IonType`.
10 | *
11 | * In practice, serves as a common base class for `List` and `SExpression`.
12 | *
13 | * @param ionType The IonType to associate with the new DomValue subclass.
14 | * @constructor
15 | * @private
16 | */
17 | export function Sequence(ionType: IonType) {
18 | return class
19 | extends Value(Array, ionType, FromJsConstructor.NONE)
20 | implements Value, Array {
21 | protected constructor(children: Value[], annotations: string[] = []) {
22 | super();
23 | for (const child of children) {
24 | this.push(child);
25 | }
26 | this._setAnnotations(annotations);
27 |
28 | return new Proxy(this, {
29 | set: function (target, index, value): boolean {
30 | if (!(value instanceof Value)) {
31 | value = Value.from(value);
32 | }
33 | target[index] = value;
34 | return true; // Indicates that the assignment succeeded
35 | },
36 | });
37 | }
38 |
39 | get(...pathElements: PathElement[]): Value | null {
40 | if (pathElements.length === 0) {
41 | throw new Error("Value#get requires at least one parameter.");
42 | }
43 | const [pathHead, ...pathTail] = pathElements;
44 | if (typeof pathHead !== "number") {
45 | throw new Error(
46 | `Cannot index into a ${
47 | this.getType().name
48 | } with a ${typeof pathHead}.`
49 | );
50 | }
51 |
52 | const children = this as Value[];
53 | const maybeChild: Value | undefined = children[pathHead];
54 | const child: Value | null = maybeChild === undefined ? null : maybeChild;
55 | if (pathTail.length === 0 || child === null) {
56 | return child;
57 | }
58 | return child.get(...pathTail);
59 | }
60 |
61 | elements(): Value[] {
62 | return Object.values(this);
63 | }
64 |
65 | toString(): string {
66 | return "[" + this.join(", ") + "]";
67 | }
68 |
69 | static _fromJsValue(jsValue: any, annotations: string[]): Value {
70 | if (!(jsValue instanceof Array)) {
71 | throw new Error(
72 | `Cannot create a ${this.name} from: ${jsValue.toString()}`
73 | );
74 | }
75 | const children = jsValue.map((child) => Value.from(child));
76 | return new this(children, annotations);
77 | }
78 |
79 | writeTo(writer: Writer) {
80 | writer.setAnnotations(this.getAnnotations());
81 | writer.stepIn(ionType);
82 | for (const child of this) {
83 | child.writeTo(writer);
84 | }
85 | writer.stepOut();
86 | }
87 |
88 | _valueEquals(
89 | other: any,
90 | options: {
91 | epsilon?: number | null;
92 | ignoreAnnotations?: boolean;
93 | ignoreTimestampPrecision?: boolean;
94 | onlyCompareIon?: boolean;
95 | } = {
96 | epsilon: null,
97 | ignoreAnnotations: false,
98 | ignoreTimestampPrecision: false,
99 | onlyCompareIon: true,
100 | }
101 | ): boolean {
102 | let isSupportedType: boolean = false;
103 | let valueToCompare: any = null;
104 | if (options.onlyCompareIon) {
105 | // `compareOnlyIon` requires that the provided value be an ion.dom.Sequence instance.
106 | if (
107 | other.getType() === IonTypes.LIST ||
108 | other.getType() === IonTypes.SEXP
109 | ) {
110 | isSupportedType = true;
111 | valueToCompare = other.elements();
112 | }
113 | } else {
114 | // We will consider other Sequence-ish types
115 | if (other instanceof Array) {
116 | isSupportedType = true;
117 | valueToCompare = other;
118 | }
119 | }
120 |
121 | if (!isSupportedType) {
122 | return false;
123 | }
124 |
125 | let actualSequence = this.elements();
126 | let expectedSequence = valueToCompare;
127 | if (actualSequence.length !== expectedSequence.length) {
128 | return false;
129 | }
130 | for (let i = 0; i < actualSequence.length; i++) {
131 | if (options.onlyCompareIon) {
132 | if (!actualSequence[i].ionEquals(expectedSequence[i], options)) {
133 | return false;
134 | }
135 | } else {
136 | if (!actualSequence[i].equals(expectedSequence[i])) {
137 | return false;
138 | }
139 | }
140 | }
141 | return true;
142 | }
143 | };
144 | }
145 |
--------------------------------------------------------------------------------
/src/dom/String.ts:
--------------------------------------------------------------------------------
1 | import { IonTypes, Writer } from "../Ion";
2 | import {
3 | FromJsConstructor,
4 | FromJsConstructorBuilder,
5 | Primitives,
6 | } from "./FromJsConstructor";
7 | import { _NativeJsString } from "./JsValueConversion";
8 | import { Value } from "./Value";
9 |
10 | const _fromJsConstructor: FromJsConstructor = new FromJsConstructorBuilder()
11 | .withPrimitives(Primitives.String)
12 | .withClassesToUnbox(_NativeJsString)
13 | .build();
14 |
15 | /**
16 | * Represents a string[1] value in an Ion stream.
17 | *
18 | * [1] https://amazon-ion.github.io/ion-docs/docs/spec.html#string
19 | */
20 | export class String extends Value(
21 | _NativeJsString,
22 | IonTypes.STRING,
23 | _fromJsConstructor
24 | ) {
25 | /**
26 | * Constructor.
27 | * @param text The text value to represent as a string.
28 | * @param annotations An optional array of strings to associate with the provided text.
29 | */
30 | constructor(text: string, annotations: string[] = []) {
31 | super(text);
32 | this._setAnnotations(annotations);
33 | }
34 |
35 | stringValue(): string {
36 | return this.toString();
37 | }
38 |
39 | writeTo(writer: Writer): void {
40 | writer.setAnnotations(this.getAnnotations());
41 | writer.writeString(this.stringValue());
42 | }
43 |
44 | _valueEquals(
45 | other: any,
46 | options: {
47 | epsilon?: number | null;
48 | ignoreAnnotations?: boolean;
49 | ignoreTimestampPrecision?: boolean;
50 | onlyCompareIon?: boolean;
51 | } = {
52 | epsilon: null,
53 | ignoreAnnotations: false,
54 | ignoreTimestampPrecision: false,
55 | onlyCompareIon: true,
56 | }
57 | ): boolean {
58 | let isSupportedType: boolean = false;
59 | let valueToCompare: any = null;
60 |
61 | // if the provided value is an ion.dom.String instance.
62 | if (other instanceof String) {
63 | isSupportedType = true;
64 | valueToCompare = other.stringValue();
65 | } else if (!options.onlyCompareIon) {
66 | // We will consider other String-ish types
67 | if (typeof other === "string" || other instanceof _NativeJsString) {
68 | isSupportedType = true;
69 | valueToCompare = other.valueOf();
70 | }
71 | }
72 |
73 | if (!isSupportedType) {
74 | return false;
75 | }
76 |
77 | return this.compareValue(valueToCompare) === 0;
78 | }
79 |
80 | compareValue(expectedValue: string): number {
81 | return this.stringValue().localeCompare(expectedValue);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/dom/Symbol.ts:
--------------------------------------------------------------------------------
1 | import { IonTypes, Writer } from "../Ion";
2 | import {
3 | FromJsConstructor,
4 | FromJsConstructorBuilder,
5 | Primitives,
6 | } from "./FromJsConstructor";
7 | import { Value } from "./Value";
8 |
9 | const _fromJsConstructor: FromJsConstructor = new FromJsConstructorBuilder()
10 | .withPrimitives(Primitives.String)
11 | .withClassesToUnbox(String)
12 | .build();
13 |
14 | // TODO:
15 | // This extends 'String' because ion-js does not yet have a SymbolToken construct.
16 | // It is not possible to access the raw Symbol ID via the Reader API, so it cannot be accessed from this class.
17 |
18 | /**
19 | * Represents a symbol[1] value in an Ion stream.
20 | *
21 | * [1] https://amazon-ion.github.io/ion-docs/docs/spec.html#symbol
22 | */
23 | export class Symbol extends Value(String, IonTypes.SYMBOL, _fromJsConstructor) {
24 | /**
25 | * Constructor.
26 | * @param symbolText The text to represent as a symbol.
27 | * @param annotations An optional array of strings to associate with this symbol.
28 | */
29 | constructor(symbolText: string, annotations: string[] = []) {
30 | super(symbolText);
31 | this._setAnnotations(annotations);
32 | }
33 |
34 | stringValue(): string {
35 | return this.toString();
36 | }
37 |
38 | writeTo(writer: Writer): void {
39 | writer.setAnnotations(this.getAnnotations());
40 | writer.writeSymbol(this.stringValue());
41 | }
42 |
43 | _valueEquals(
44 | other: any,
45 | options: {
46 | epsilon?: number | null;
47 | ignoreAnnotations?: boolean;
48 | ignoreTimestampPrecision?: boolean;
49 | onlyCompareIon?: boolean;
50 | } = {
51 | epsilon: null,
52 | ignoreAnnotations: false,
53 | ignoreTimestampPrecision: false,
54 | onlyCompareIon: true,
55 | }
56 | ): boolean {
57 | let isSupportedType: boolean = false;
58 | let valueToCompare: any = null;
59 |
60 | //if the provided value is an ion.dom.Symbol instance.
61 | if (other instanceof Symbol) {
62 | isSupportedType = true;
63 | valueToCompare = other.stringValue();
64 | } else if (!options.onlyCompareIon) {
65 | // We will consider other Symbol-ish types
66 | if (typeof other === "string" || other instanceof String) {
67 | isSupportedType = true;
68 | valueToCompare = other.valueOf();
69 | }
70 | }
71 |
72 | if (!isSupportedType) {
73 | return false;
74 | }
75 |
76 | return this.compareValue(valueToCompare) === 0;
77 | }
78 |
79 | compareValue(expectedValue: string): number {
80 | return this.stringValue().localeCompare(expectedValue);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/dom/Timestamp.ts:
--------------------------------------------------------------------------------
1 | import * as ion from "../Ion";
2 | import { Decimal, IonTypes } from "../Ion";
3 | import { Writer } from "../Ion";
4 | import {
5 | FromJsConstructor,
6 | FromJsConstructorBuilder,
7 | } from "./FromJsConstructor";
8 | import { Value } from "./Value";
9 |
10 | const _fromJsConstructor: FromJsConstructor = new FromJsConstructorBuilder()
11 | .withClasses(Date, ion.Timestamp)
12 | .build();
13 |
14 | /**
15 | * Represents a timestamp[1] value in an Ion stream.
16 | *
17 | * [1] https://amazon-ion.github.io/ion-docs/docs/spec.html#timestamp
18 | */
19 | export class Timestamp extends Value(
20 | Date,
21 | IonTypes.TIMESTAMP,
22 | _fromJsConstructor
23 | ) {
24 | protected _timestamp: ion.Timestamp;
25 | protected _date: Date;
26 |
27 | /**
28 | * Constructor.
29 | * @param dateOrTimestamp A `Date` or `Timestamp` to represent as a timestamp.
30 | * @param annotations An optional array of strings to associate with this timestamp.
31 | */
32 | constructor(
33 | dateOrTimestamp: Date | ion.Timestamp,
34 | annotations: string[] = []
35 | ) {
36 | let date: Date;
37 | let timestamp: ion.Timestamp;
38 | if (dateOrTimestamp instanceof Date) {
39 | date = dateOrTimestamp;
40 | timestamp = Timestamp._timestampFromDate(date);
41 | } else {
42 | timestamp = dateOrTimestamp;
43 | date = timestamp.getDate();
44 | }
45 | super(date);
46 | this._date = date;
47 | this._timestamp = timestamp;
48 | this._setAnnotations(annotations);
49 | }
50 |
51 | private static _timestampFromDate(date: Date): ion.Timestamp {
52 | const milliseconds =
53 | date.getUTCSeconds() * 1000 + date.getUTCMilliseconds();
54 | const fractionalSeconds = new Decimal(milliseconds, -3);
55 |
56 | // `Date` objects do not store a timezone offset. If the offset is requested
57 | // via `.getTimezoneOffset()`, the configured offset of the host computer
58 | // is returned instead of the timezone that was specified when the Date was
59 | // constructed. Since the timezone of the host may or may not be meaningful
60 | // to the user, we store the time in UTC instead. Users wishing to store a
61 | // specific timezone offset on their dom.Timestamp can pass in an ion.Timestamp
62 | // instead of a Date.
63 | return new ion.Timestamp(
64 | 0,
65 | date.getUTCFullYear(),
66 | date.getUTCMonth() + 1, // Timestamp expects a range of 1-12
67 | date.getUTCDate(),
68 | date.getUTCHours(),
69 | date.getUTCMinutes(),
70 | fractionalSeconds
71 | );
72 | }
73 |
74 | timestampValue(): ion.Timestamp {
75 | return this._timestamp;
76 | }
77 |
78 | dateValue(): Date {
79 | return this._date;
80 | }
81 |
82 | writeTo(writer: Writer): void {
83 | writer.setAnnotations(this.getAnnotations());
84 | writer.writeTimestamp(this.timestampValue());
85 | }
86 |
87 | _valueEquals(
88 | other: any,
89 | options: {
90 | epsilon?: number | null;
91 | ignoreAnnotations?: boolean;
92 | ignoreTimestampPrecision?: boolean;
93 | onlyCompareIon?: boolean;
94 | } = {
95 | epsilon: null,
96 | ignoreAnnotations: false,
97 | ignoreTimestampPrecision: false,
98 | onlyCompareIon: true,
99 | }
100 | ): boolean {
101 | let isSupportedType: boolean = false;
102 | let valueToCompare: any = null;
103 | // if the provided value is an ion.dom.Symbol instance.
104 | if (other instanceof Timestamp) {
105 | isSupportedType = true;
106 | valueToCompare = other.timestampValue();
107 | } else if (!options.onlyCompareIon) {
108 | // We will consider other Timestamp-ish types
109 | if (other instanceof ion.Timestamp) {
110 | // expectedValue is a non-DOM Timestamp
111 | isSupportedType = true;
112 | valueToCompare = other;
113 | } else if (other instanceof Date) {
114 | if (this.dateValue().getTime() === other.getTime()) {
115 | return true;
116 | } else {
117 | return false;
118 | }
119 | }
120 | }
121 |
122 | if (!isSupportedType) {
123 | return false;
124 | }
125 |
126 | if (options.ignoreTimestampPrecision) {
127 | return this.timestampValue().compareTo(valueToCompare) === 0;
128 | }
129 | return this.timestampValue().equals(valueToCompare);
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/events/EventStreamError.ts:
--------------------------------------------------------------------------------
1 | import { IonEvent } from "../Ion";
2 |
3 | export class EventStreamError extends Error {
4 | type: string;
5 | index: number;
6 | eventstream: IonEvent[];
7 |
8 | constructor(
9 | type: string,
10 | message: string,
11 | index: number,
12 | eventstream: IonEvent[]
13 | ) {
14 | super();
15 | this.type = type;
16 | this.index = index;
17 | this.message = message;
18 | this.eventstream = eventstream;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/util.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | /**
17 | * Returns -1 if x is negative (including -0); otherwise returns 1.
18 | */
19 | export function _sign(x: number): number {
20 | return x < 0 || (x === 0 && 1 / x === -Infinity) ? -1 : 1;
21 | }
22 |
23 | /**
24 | * Returns false if v is undefined or null; otherwise true.
25 | * @private
26 | */
27 | export function _hasValue(v: any): boolean {
28 | return v !== undefined && v !== null;
29 | }
30 |
31 | /**
32 | * Throws if value is not defined.
33 | */
34 | export function _assertDefined(value: any): void {
35 | if (value === undefined) {
36 | throw new Error("Expected value to be defined");
37 | }
38 | }
39 |
40 | /**
41 | * Indicates whether the provided bigint value is within the range[ Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER].
42 | *
43 | * @param value The integer to test.
44 | * @return True if toNumber(value) can be called without losing any precision.
45 | */
46 | export function isSafeInteger(value: bigint): boolean {
47 | return value >= Number.MIN_SAFE_INTEGER && value <= Number.MAX_SAFE_INTEGER;
48 | }
49 |
--------------------------------------------------------------------------------
/test-driver/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test-driver",
3 | "version": "1.0.0",
4 | "description": "A JavaScript implementation of ion-test-driver",
5 | "main": "dist/Cli.js",
6 | "scripts": {
7 | "build": "grunt build:test-driver"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/amazon-ion/ion-js.git"
12 | },
13 | "author": "The Ion Team (https://amazon-ion.github.io/ion-docs/index.html)",
14 | "license": "Apache-2.0",
15 | "bugs": {
16 | "url": "https://github.com/amazon-ion/ion-js/issues"
17 | },
18 | "homepage": "https://github.com/amazon-ion/ion-js#readme",
19 | "dependencies": {
20 | "yargs": "^15.4.1",
21 | "ion-js": "../"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/test-driver/src/Cli.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import yargs from "yargs";
17 |
18 | /**
19 | * This will cause `yargs` to look in other .ts/.js files in the same directory for command modules.
20 | *
21 | * For more information, see:
22 | * Command modules: https://github.com/yargs/yargs/blob/master/docs/advanced.md#providing-a-command-module
23 | * commandDir: https://github.com/yargs/yargs/blob/master/docs/advanced.md#commanddirdirectory-opts
24 | */
25 | yargs
26 | .commandDir(__dirname,{
27 | extensions: ['ts','js'],
28 | })
29 | .options({
30 | 'output': {
31 | alias: 'o',
32 | description: 'Output location. [default: stdout]',
33 | },
34 | 'output-format': {
35 | alias: 'f',
36 | choices: ['pretty', 'text', 'binary', 'events', 'none'] as const,
37 | default: 'pretty',
38 | description: "Output format, from the set (text | pretty | binary |\n"
39 | + "events | none). 'events' is only available with the\n"
40 | + "'process' command, and outputs a serialized EventStream\n"
41 | + "representing the input Ion stream(s)."
42 | },
43 | 'error-report': {
44 | alias: 'e',
45 | description: 'ErrorReport location. [default: stderr]',
46 | }
47 | }).argv;
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/test-driver/src/CliCommonArgs.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {OutputFormat} from "./OutputFormat";
17 | import fs from "fs";
18 | import yargs from "yargs";
19 |
20 | /** common CLI arguments structure
21 | * for more information: https://github.com/amazon-ion/ion-test-driver#standardized-cli
22 | */
23 | export class IonCliCommonArgs {
24 | inputFiles: Array;
25 | outputFormatName: OutputFormat;
26 |
27 | // outputFile,errorReportFile: 'any' datatype to use stdout as well as file write stream to write ion data
28 | // for more information : https://github.com/amazon-ion/ion-js/issues/627
29 | outputFile: any;
30 | errorReportFile: any;
31 |
32 | constructor(argv: yargs.Arguments) {
33 | this.outputFile = argv["output"] ? fs.createWriteStream(argv["output"] as string, {flags: 'w'}) : process.stdout;
34 | // create error output stream (DEFAULT: stderr)
35 | this.errorReportFile = argv["error-report"] ? fs.createWriteStream(argv["error-report"] as string, {flags: 'w'}) : process.stderr;
36 | this.outputFormatName = argv["output-format"] as OutputFormat;
37 | this.inputFiles = argv["input-file"] as Array;
38 | }
39 |
40 | getOutputFile(): any {
41 | return this.outputFile;
42 | }
43 |
44 | getErrorReportFile(): any {
45 | return this.errorReportFile;
46 | }
47 |
48 | getInputFiles(): Array {
49 | return this.inputFiles;
50 | }
51 |
52 | getOutputFormatName(): OutputFormat {
53 | return this.outputFormatName;
54 | }
55 | }
--------------------------------------------------------------------------------
/test-driver/src/CliCompareArgs.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {ComparisonType} from "./Compare";
17 | import {IonCliCommonArgs} from "./CliCommonArgs";
18 |
19 | /** CLI arguments for compare structure */
20 | export class IonCompareArgs extends IonCliCommonArgs{
21 | comparisonType : ComparisonType;
22 |
23 | constructor(props) {
24 | super(props);
25 | this.comparisonType = props["comparison-type"] as ComparisonType;
26 | }
27 |
28 | getComparisonType(): ComparisonType {
29 | return this.comparisonType;
30 | }
31 | }
--------------------------------------------------------------------------------
/test-driver/src/CliError.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {IonTypes, makeTextWriter} from "ion-js";
17 |
18 | /** Error Types symbol[READ | WRITE | STATE] */
19 | export enum ErrorType {
20 | READ = "READ",
21 | WRITE = "WRITE",
22 | STATE = "STATE"
23 | }
24 |
25 | /** Error structure for error report
26 | * for more information: https://github.com/amazon-ion/ion-test-driver#errorreport
27 | */
28 | export class IonCliError {
29 | errorType: ErrorType;
30 | eventIndex: number;
31 | location: string;
32 | message: string;
33 | errorReportFile: any;
34 |
35 | constructor(errorType: ErrorType, location: string, message: string, errorReportFile: any, eventIndex: number = 0) {
36 | this.errorType = errorType;
37 | this.location = location;
38 | this.message = message;
39 | this.errorReportFile = errorReportFile;
40 | this.eventIndex = eventIndex;
41 | }
42 |
43 | writeErrorReport() {
44 | let writer = makeTextWriter();
45 | writer.stepIn(IonTypes.STRUCT);
46 | writer.writeFieldName('error_type');
47 | writer.writeSymbol(this.errorType);
48 | writer.writeFieldName('message');
49 | writer.writeString(this.message);
50 | writer.writeFieldName('location');
51 | writer.writeString(this.location);
52 | writer.writeFieldName('event_index');
53 | writer.writeInt(this.eventIndex);
54 | writer.stepOut();
55 | this.errorReportFile.write(writer.getBytes());
56 | this.errorReportFile.write("\n");
57 | }
58 | }
--------------------------------------------------------------------------------
/test-driver/src/ComparisonContext.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {IonEventStream} from "ion-js";
17 | import {Reader} from "ion-js";
18 | import {IonTypes, makeReader, Writer} from "ion-js";
19 | import fs from "fs";
20 | import {IonCompareArgs} from "./CliCompareArgs";
21 | import {ErrorType, IonCliError} from "./CliError";
22 | import {IonEvent, IonEventFactory, IonEventType} from "ion-js";
23 |
24 | /**
25 | * ComparisonContext to create a structure for lhs and rhs streams
26 | * for more information: https://github.com/amazon-ion/ion-test-driver#comparisonreport
27 | */
28 | export class ComparisonContext {
29 | location: string;
30 | eventStream: IonEventStream;
31 |
32 | constructor(path: string, args: IonCompareArgs){
33 | this.location = path;
34 | let ionReader = this.createReadersForComparison(args);
35 | this.setEventStream(ionReader, path, args);
36 | }
37 |
38 | createReadersForComparison(args: IonCompareArgs): Reader {
39 | let ionReader;
40 | try {
41 | ionReader = makeReader(this.getInput(this.getLocation()));
42 | } catch (Error) {
43 | new IonCliError(ErrorType.READ, this.getLocation(), Error.message, args.getErrorReportFile()).writeErrorReport();
44 | }
45 | return ionReader;
46 | }
47 |
48 | getInput(path: string): string | Buffer {
49 | let options = path.endsWith(".10n") ? null : "utf8";
50 | return fs.readFileSync(path, options);
51 | }
52 |
53 | setEventStream(ionReader: Reader, path: string, args: IonCompareArgs) {
54 | let events: IonEvent[] = [];
55 | try{
56 | let eventStream = new IonEventStream(ionReader);
57 |
58 | if(!eventStream.isEventStream) {
59 | // processes input stream(text or binary) into event stream
60 | events = this.collectInputStreamEvents(events, eventStream);
61 | eventStream.events = events;
62 | }
63 | this.eventStream = eventStream;
64 | } catch (EventStreamError) {
65 | if(EventStreamError.eventstream) {
66 | events.push(...EventStreamError.eventstream);
67 | }
68 | if(EventStreamError.type === "READ") {
69 | new IonCliError(ErrorType.READ, path, EventStreamError.message, args.getErrorReportFile(), events.length).writeErrorReport();
70 | }
71 | else if(EventStreamError.type === "WRITE") {
72 | new IonCliError(ErrorType.WRITE, path, EventStreamError.message, args.getErrorReportFile()).writeErrorReport();
73 | }
74 | else {
75 | new IonCliError(ErrorType.STATE, path, EventStreamError.message, args.getErrorReportFile()).writeErrorReport();
76 | }
77 | }
78 | }
79 |
80 | collectInputStreamEvents(events: IonEvent[], eventStream: IonEventStream): IonEvent[] {
81 | for(let i = 0; i < eventStream.events.length; i++) {
82 | let event = eventStream.events[i];
83 | events.push(event);
84 | if (eventStream.isEmbedded(event)) {
85 | let tempEvents: IonEvent[] = [];
86 | for (let j = 0; j < event.ionValue.length - 1; j++) {
87 | tempEvents.push(...new IonEventStream(makeReader(event.ionValue[j].ionValue)).getEvents());
88 | }
89 | i = i + (event.ionValue.length - 1);
90 | let eventFactory = new IonEventFactory();
91 | tempEvents.push(eventFactory.makeEvent(IonEventType.CONTAINER_END, event.ionType!, null, event.depth, [], false, null));
92 | event.ionValue = tempEvents;
93 | events.push(...tempEvents.slice(0,-1));
94 | }
95 | }
96 | return events;
97 | }
98 |
99 | getEventStream() {
100 | return this.eventStream;
101 | }
102 |
103 | getLocation(): string {
104 | return this.location;
105 | }
106 |
107 | writeComparisonContext(ionOutputWriter: Writer, field_name: string, event_index: number) {
108 | ionOutputWriter.writeFieldName(field_name);
109 | ionOutputWriter.stepIn(IonTypes.STRUCT);
110 | ionOutputWriter.writeFieldName("location");
111 | ionOutputWriter.writeString(this.location);
112 | ionOutputWriter.writeFieldName("event");
113 | if(this.getEventStream().getEvents().length > 0) {
114 | this.getEventStream().getEvents()[event_index].write(ionOutputWriter);
115 | } else {
116 | ionOutputWriter.writeNull(IonTypes.NULL);
117 | }
118 | ionOutputWriter.writeFieldName("event_index");
119 | ionOutputWriter.writeInt(event_index);
120 | ionOutputWriter.stepOut();
121 | }
122 | }
--------------------------------------------------------------------------------
/test-driver/src/ComparisonReport.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {ComparisonType} from "./Compare";
17 | import {IonTypes, makeTextWriter} from "ion-js";
18 | import {ComparisonContext} from "./ComparisonContext";
19 | import {ComparisonResultType} from "ion-js";
20 |
21 | /** comparison report structure for compare
22 | * for more information: https://github.com/amazon-ion/ion-test-driver#comparisonreport
23 | */
24 | export class IonComparisonReport {
25 | lhs: ComparisonContext;
26 | rhs: ComparisonContext;
27 | comparisonReportFile: any;
28 | comparisonType: ComparisonType;
29 |
30 | constructor(lhs: ComparisonContext, rhs: ComparisonContext, comparisonReportFile: any, comparisonType: ComparisonType) {
31 | this.lhs = lhs;
32 | this.rhs = rhs;
33 | this.comparisonReportFile = comparisonReportFile;
34 | this.comparisonType = comparisonType;
35 | }
36 |
37 | writeComparisonReport(result: ComparisonResultType, message: string, event_index_lhs: number, event_index_rhs: number) {
38 | let writer = makeTextWriter();
39 | writer.stepIn(IonTypes.STRUCT);
40 | writer.writeFieldName('result');
41 | writer.writeSymbol(result);
42 | this.lhs.writeComparisonContext(writer, "lhs", event_index_lhs);
43 | this.rhs.writeComparisonContext(writer, "rhs", event_index_rhs);
44 | writer.writeFieldName('message');
45 | writer.writeString(message);
46 | writer.stepOut();
47 | this.comparisonReportFile.write(writer.getBytes());
48 | this.comparisonReportFile.write("\n");
49 | }
50 | }
--------------------------------------------------------------------------------
/test-driver/src/OutputFormat.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {Writer} from 'ion-js';
17 | import {
18 | makePrettyWriter,
19 | makeBinaryWriter,
20 | makeTextWriter
21 | } from 'ion-js';
22 |
23 | export enum OutputFormat {
24 | PRETTY = "pretty",
25 | TEXT = "text",
26 | BINARY = "binary",
27 | EVENTS = "events",
28 | NONE = "none"
29 | }
30 |
31 | /** gets the writer corresponding to the output format **/
32 | export namespace OutputFormat {
33 | export function createIonWriter(name: OutputFormat) : Writer | null {
34 | switch (name) {
35 | case OutputFormat.PRETTY:
36 | return makePrettyWriter();
37 | case OutputFormat.TEXT:
38 | return makeTextWriter();
39 | case OutputFormat.BINARY:
40 | return makeBinaryWriter();
41 | case OutputFormat.EVENTS:
42 | return makePrettyWriter();
43 | case OutputFormat.NONE:
44 | return null;
45 | default:
46 | throw new Error("Output Format " + name + " unexpected.");
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/test-driver/tsconfig.json:
--------------------------------------------------------------------------------
1 | // Test-driver Typescript Compiler Configuration
2 | // Targets CommonJS Module system and ES6 feature set.
3 | {
4 | "include": [
5 | "src/**"
6 | ],
7 | "exclude": [
8 | "**/*.d.ts"
9 | ],
10 | "compilerOptions": {
11 | "target": "es2020",
12 | "module": "commonjs",
13 | "moduleResolution": "node",
14 | "esModuleInterop": true,
15 | "lib": ["ESNext"],
16 | "declaration": false,
17 | "inlineSources": true,
18 | "sourceMap": true,
19 | "strict": false,
20 | "strictNullChecks": true,
21 | "rootDirs": [
22 | "src"
23 | ],
24 | "outDir": "dist",
25 | "experimentalDecorators": true
26 | }
27 | }
--------------------------------------------------------------------------------
/test/IntSize.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import { assert } from "chai";
17 | import IntSize from "../src/IntSize";
18 | import * as ion from "../src/Ion";
19 | import { IonTypes } from "../src/Ion";
20 |
21 | function intToBinaryIonBytes(value: number | bigint): Uint8Array {
22 | let writer = ion.makeBinaryWriter();
23 | writer.writeInt(value);
24 | writer.close();
25 | return writer.getBytes();
26 | }
27 |
28 | describe("IntSize", () => {
29 | describe("text", () => {
30 | it("MAX_SAFE_INTEGER: IntSize.Number", () => {
31 | let reader = ion.makeReader(Number.MAX_SAFE_INTEGER.toString());
32 | assert.equal(IonTypes.INT, reader.next());
33 | assert.equal(reader.intSize(), IntSize.Number);
34 | });
35 |
36 | it("MIN_SAFE_INTEGER: IntSize.Number", () => {
37 | let reader = ion.makeReader(Number.MIN_SAFE_INTEGER.toString());
38 | assert.equal(IonTypes.INT, reader.next());
39 | assert.equal(reader.intSize(), IntSize.Number);
40 | });
41 |
42 | it("> MAX_SAFE_INTEGER: IntSize.BigInt", () => {
43 | let value = BigInt(Number.MAX_SAFE_INTEGER) + 1n;
44 | let reader = ion.makeReader(value.toString());
45 | assert.equal(IonTypes.INT, reader.next());
46 | assert.equal(reader.intSize(), IntSize.BigInt);
47 | });
48 |
49 | it("< MIN_SAFE_INTEGER: IntSize.BigInt", () => {
50 | let value = BigInt(Number.MIN_SAFE_INTEGER) - 1n;
51 | let reader = ion.makeReader(value.toString());
52 | assert.equal(IonTypes.INT, reader.next());
53 | assert.equal(reader.intSize(), IntSize.BigInt);
54 | });
55 | });
56 |
57 | describe("binary", () => {
58 | it("MAX_SAFE_INTEGER: IntSize.Number", () => {
59 | let reader = ion.makeReader(intToBinaryIonBytes(Number.MAX_SAFE_INTEGER));
60 | assert.equal(IonTypes.INT, reader.next());
61 | assert.equal(reader.intSize(), IntSize.Number);
62 | });
63 |
64 | it("MIN_SAFE_INTEGER: IntSize.Number", () => {
65 | let reader = ion.makeReader(intToBinaryIonBytes(Number.MIN_SAFE_INTEGER));
66 | assert.equal(IonTypes.INT, reader.next());
67 | assert.equal(reader.intSize(), IntSize.Number);
68 | });
69 |
70 | it("> MAX_SAFE_INTEGER: IntSize.BigInt", () => {
71 | let value = BigInt(Number.MAX_SAFE_INTEGER) + 1n;
72 | let reader = ion.makeReader(intToBinaryIonBytes(value));
73 | assert.equal(IonTypes.INT, reader.next());
74 | assert.equal(reader.intSize(), IntSize.BigInt);
75 | });
76 |
77 | it("< MIN_SAFE_INTEGER: IntSize.BigInt", () => {
78 | let value = BigInt(Number.MIN_SAFE_INTEGER) - 1n;
79 | let reader = ion.makeReader(intToBinaryIonBytes(value));
80 | assert.equal(IonTypes.INT, reader.next());
81 | assert.equal(reader.intSize(), IntSize.BigInt);
82 | });
83 | });
84 | });
85 |
--------------------------------------------------------------------------------
/test/IonAnnotations.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import { assert } from "chai";
17 | import * as ion from "../src/Ion";
18 |
19 | function readerToBytes(reader) {
20 | let writer = ion.makeTextWriter();
21 | writer.writeValues(reader);
22 | writer.close();
23 | return writer.getBytes();
24 | }
25 |
26 | function readerToString(reader) {
27 | return ion.decodeUtf8(readerToBytes(reader));
28 | }
29 |
30 | describe("Annotations", () => {
31 | it("Read annotations", () => {
32 | let data = "a::b::123";
33 | let reader = ion.makeReader(data);
34 | reader.next();
35 | assert.deepEqual(reader.annotations(), ["a", "b"]);
36 | assert.equal(reader.value()?.toString(), "123");
37 | assert.equal(readerToString(reader), "a::b::123");
38 | });
39 |
40 | it("Resolves ID annotations", () => {
41 | let data = "$3::123";
42 | let reader = ion.makeReader(data);
43 | reader.next();
44 | assert.deepEqual(reader.annotations(), ["$ion_symbol_table"]);
45 | });
46 |
47 | it("Does not resolve non-ID annotations ($)", () => {
48 | let data = "'$'::123";
49 | let reader = ion.makeReader(data);
50 | reader.next();
51 | assert.deepEqual(reader.annotations(), ["$"]);
52 | });
53 |
54 | it("Does not resolve non-ID annotations ($3)", () => {
55 | let data = "'$3'::123";
56 | let reader = ion.makeReader(data);
57 | reader.next();
58 | assert.deepEqual(reader.annotations(), ["$3"]);
59 | });
60 |
61 | it("Does not resolve non-ID annotations ($1.00)", () => {
62 | let data = "'$1.00'::123";
63 | let reader = ion.makeReader(data);
64 | reader.next();
65 | assert.deepEqual(reader.annotations(), ["$1.00"]);
66 | });
67 |
68 | it("Create annotations", () => {
69 | let data = "123";
70 | let reader = ion.makeReader(data);
71 | reader.next();
72 | let writer = ion.makeTextWriter();
73 | writer.setAnnotations(["a"]);
74 | writer.writeInt(reader.numberValue());
75 | reader.next();
76 | writer.writeValues(reader);
77 | assert.equal(String.fromCharCode.apply(null, writer.getBytes()), "a::123");
78 | });
79 |
80 | it("Add annotation", () => {
81 | let data = "a::b::123";
82 | let reader = ion.makeReader(data);
83 | reader.next();
84 | let writer = ion.makeTextWriter();
85 | writer.setAnnotations(reader.annotations());
86 | writer.addAnnotation("c");
87 | writer.writeInt(reader.numberValue());
88 | assert.equal(
89 | String.fromCharCode.apply(null, writer.getBytes()),
90 | "a::b::c::123"
91 | );
92 | });
93 |
94 | it("Wrap annotation", () => {
95 | let data = "{ x: 1 }";
96 | let reader = ion.makeReader(data);
97 | let writer = ion.makeTextWriter();
98 | writer.setAnnotations(["a", "b"]);
99 | writer.stepIn(ion.IonTypes.STRUCT);
100 |
101 | reader.next();
102 | reader.stepIn();
103 | reader.next();
104 |
105 | writer.writeValues(reader);
106 |
107 | reader.stepOut();
108 |
109 | writer.stepOut();
110 | assert.equal(
111 | String.fromCharCode.apply(null, writer.getBytes()),
112 | "a::b::{x:1}"
113 | );
114 | });
115 |
116 | it("Sid0 text annotation throws", () => {
117 | let test = () => {
118 | let input = "$0::taco";
119 | let reader = ion.makeReader(input);
120 | reader.next();
121 | };
122 | assert.throws(test);
123 | });
124 |
125 | it("Sid0 binary annotation throws", () => {
126 | let test = () => {
127 | // the following bytes represent $0::1
128 | let input = new Uint8Array([
129 | 0xe0,
130 | 0x01,
131 | 0x00,
132 | 0xea,
133 | 0xe4,
134 | 0x81,
135 | 0x80,
136 | 0x21,
137 | 0x01,
138 | ]);
139 | let reader = ion.makeReader(input);
140 | reader.next();
141 | reader.annotations();
142 | };
143 | assert.throws(test);
144 | });
145 | });
146 |
--------------------------------------------------------------------------------
/test/IonBinaryReader.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {assert} from 'chai';
17 | import * as ion from '../src/Ion';
18 | import { BinaryWriter } from '../src/IonBinaryWriter';
19 | import { Writeable } from '../src/IonWriteable';
20 | import { LocalSymbolTable } from '../src/IonLocalSymbolTable';
21 | import { getSystemSymbolTable } from '../src/IonSystemSymbolTable';
22 | import { Import } from '../src/IonImport';
23 |
24 | describe('Binary Reader', () => {
25 | it('timestamp', () => {
26 | let timestamp = ion.Timestamp.parse('2000-05-04T03:02:01.000789000Z');
27 | let writer = ion.makeBinaryWriter();
28 | writer.writeTimestamp(timestamp);
29 | writer.close();
30 | let reader = ion.makeReader(writer.getBytes());
31 | reader.next();
32 | assert.deepEqual(reader.timestampValue(), timestamp);
33 | });
34 | it('test position', () => {
35 | // In the comments below, the vertical bar '|' indicates the current position of the cursor at each step.
36 | const ionBinary: Uint8Array = ion.dumpBinary(null, 7, -17, "Hello", [1, 2, 3]);
37 | // [|0xE0, 0x1, 0x0, 0xEA, |0xF, |0x21, 0x7, |0x31, 0x11, |0x85, 0x48, 0x65, 0x6C, 0x6C, 0x6F, |0xB6, 0x21, 0x1, 0x21, 0x2, 0x21, 0x3 |]
38 | // Version null 7 -17 "hello" [1,2,3]
39 | const binaryReader = ion.makeReader(ionBinary);
40 | // init pos 0
41 | // [|0xE0, 0x1, 0x0, 0xEA, 0xF, 0x21, 0x7, 0x31, 0x11, 0x85, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xB6, 0x21, 0x1, 0x21, 0x2, 0x21, 0x3]
42 | assert.equal(binaryReader.position(), 0);
43 | binaryReader.next();
44 | // at null
45 | // [0xE0, 0x1, 0x0, 0xEA, 0xF,| 0x21, 0x7, 0x31, 0x11, 0x85, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xB6, 0x21, 0x1, 0x21, 0x2, 0x21, 0x3]
46 | assert.equal(binaryReader.position(), 5);
47 | binaryReader.next();
48 | // at 7
49 | // [0xE0, 0x1, 0x0, 0xEA, 0xF, 0x21, 0x7,| 0x31, 0x11, 0x85, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xB6, 0x21, 0x1, 0x21, 0x2, 0x21, 0x3]
50 | assert.equal(binaryReader.position(), 6);
51 | binaryReader.next();
52 | // at 17
53 | // [0xE0, 0x1, 0x0, 0xEA, 0xF, 0x21, 0x7, 0x31, 0x11,| 0x85, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xB6, 0x21, 0x1, 0x21, 0x2, 0x21, 0x3]
54 | assert.equal(binaryReader.position(), 8);
55 | binaryReader.next();
56 | // at hello
57 | // [0xE0, 0x1, 0x0, 0xEA, 0xF, 0x21, 0x7, 0x31, 0x11, 0x85, 0x48,| 0x65, 0x6C, 0x6C, 0x6F, 0xB6, 0x21, 0x1, 0x21, 0x2, 0x21, 0x3]
58 | assert.equal(binaryReader.position(), 10);
59 | binaryReader.next();
60 | // at [1,2,3]
61 | // [0xE0, 0x1, 0x0, 0xEA, 0xF, 0x21, 0x7, 0x31, 0x11, 0x85, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xB6, 0x21,| 0x1, 0x21, 0x2, 0x21, 0x3]
62 | assert.equal(binaryReader.position(), 16);
63 | binaryReader.stepIn();
64 | binaryReader.next();
65 | // at 1
66 | // [0xE0, 0x1, 0x0, 0xEA, 0xF, 0x21, 0x7, 0x31, 0x11, 0x85, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xB6, 0x21, 0x1,| 0x21, 0x2, 0x21, 0x3]
67 | assert.equal(binaryReader.position(), 17);
68 | binaryReader.next();
69 | // at 2
70 | // [0xE0, 0x1, 0x0, 0xEA, 0xF, 0x21, 0x7, 0x31, 0x11, 0x85, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xB6, 0x21, 0x1, 0x21, 0x2,| 0x21, 0x3]
71 | assert.equal(binaryReader.position(), 19);
72 | binaryReader.next();
73 | // at 3
74 | // [0xE0, 0x1, 0x0, 0xEA, 0xF, 0x21, 0x7, 0x31, 0x11, 0x85, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xB6, 0x21, 0x1, 0x21, 0x2, 0x21, 0x3|]
75 | assert.equal(binaryReader.position(), 21);
76 | binaryReader.stepOut();
77 | // out of stream
78 | // [0xE0, 0x1, 0x0, 0xEA, 0xF, 0x21, 0x7, 0x31, 0x11, 0x85, 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0xB6, 0x21, 0x1, 0x21, 0x2, 0x21, 0x3]|
79 | assert.equal(binaryReader.position(), 22);
80 | });
81 |
82 | it('test catalog', () => {
83 | const symbols = ['id', 'name'];
84 |
85 | // Create a SharedSymbolTable with the desired strings
86 | const sharedSymbolTable = new ion.SharedSymbolTable('foo', 1, symbols);
87 | // Create a symbol table with shared table and system table.
88 | const localSymbolTable = new LocalSymbolTable([
89 | sharedSymbolTable,
90 | getSystemSymbolTable(),
91 | ].reduceRight((parent, table) => new Import(parent, table), null as (null | Import)))
92 | // dump the symbols as binary. The buffer should not define the symbols in the table.
93 | const writer = new BinaryWriter(localSymbolTable, new Writeable());
94 | symbols.forEach(symbol => writer.writeSymbol(symbol));
95 | writer.close();
96 | const buffer = writer.getBytes();
97 |
98 | // Create a catalog with shared symbol table
99 | let catalog = new ion.Catalog();
100 | catalog.add(sharedSymbolTable);
101 |
102 | // Reader with catalog should return correct symbol string values
103 | let reader = ion.makeReader(buffer, catalog);
104 | assert.deepEqual(ion.loadAll(reader), symbols.map(symbol => new ion.dom.Symbol(symbol)))
105 |
106 | // Reader without catalog should error (really this is testing that our buffer references symbols from the table.
107 | assert.throws(() => ion.loadAll(ion.makeReader(buffer)), "symbol is unresolvable")
108 | })
109 | });
110 |
--------------------------------------------------------------------------------
/test/IonBinaryTimestamp.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {assert} from 'chai';
17 | import * as ion from '../src/Ion';
18 |
19 | describe('Binary Timestamp', () => {
20 | it('Round trip', () => {
21 | // First part - writing timestamp into binary datagram
22 | let writer = ion.makeBinaryWriter();
23 | let timestamp = new ion.Timestamp(0, 2017, 6, 7, 18, 29, ion.Decimal.parse('17.901'));
24 | writer.writeTimestamp(timestamp);
25 | writer.close();
26 |
27 | /* Datagram content
28 | * {
29 | * test_timestamp:2017-06-07T18:29:17.901Z
30 | * }
31 | */
32 |
33 | // Second part - reading timestamp from binary datagram created above
34 | let reader = ion.makeReader(writer.getBytes());
35 | reader.next();
36 | let timestampValue = reader.value();
37 | assert.equal(timestamp.toString(), timestampValue!.toString());
38 | });
39 | });
--------------------------------------------------------------------------------
/test/IonCatalog.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {assert} from 'chai';
17 | import {Catalog, SharedSymbolTable} from '../src/Ion';
18 |
19 | describe('Catalog', () => {
20 | it('Finds specific version', () => {
21 | let version1 = new SharedSymbolTable('foo', 1, ['a']);
22 | let version2 = new SharedSymbolTable('foo', 2, ['b']);
23 | let version3 = new SharedSymbolTable('foo', 3, ['b']);
24 | let version4 = new SharedSymbolTable('foo', 4, ['b']);
25 | let catalog = new Catalog();
26 | catalog.add(version1);
27 | catalog.add(version2);
28 | catalog.add(version3);
29 | catalog.add(version4);
30 | let match = catalog.getVersion('foo', 3);
31 | assert.strictEqual(version3, match);
32 | });
33 |
34 | it('Find specific version returns null if specific version not found', () => {
35 | let version1 = new SharedSymbolTable('foo', 1, ['a']);
36 | let catalog = new Catalog();
37 | catalog.add(version1);
38 | assert.isNull(catalog.getVersion('foo', 2));
39 | });
40 |
41 | it('Find specific version returns null if any version not found', () => {
42 | let catalog = new Catalog();
43 | assert.isNull(catalog.getVersion('foo', 2));
44 | });
45 |
46 | it('Finds latest version', () => {
47 | let version1 = new SharedSymbolTable('foo', 1, ['a']);
48 | let version2 = new SharedSymbolTable('foo', 2, ['b']);
49 | let version3 = new SharedSymbolTable('foo', 3, ['b']);
50 | let version4 = new SharedSymbolTable('foo', 4, ['b']);
51 | let catalog = new Catalog();
52 | catalog.add(version1);
53 | catalog.add(version2);
54 | catalog.add(version3);
55 | catalog.add(version4);
56 | let match = catalog.getTable('foo');
57 | assert.isNotNull(match);
58 | assert.strictEqual(4, match!.version);
59 | });
60 |
61 | it('Find latest version returns null if no version exists', () => {
62 | let catalog = new Catalog();
63 | assert.isNull(catalog.getTable('foo'));
64 | });
65 |
66 | it('Adding same symbol table twice overwrites original entry', () => {
67 | let version1a = new SharedSymbolTable('foo', 1, ['a']);
68 | let version1b = new SharedSymbolTable('foo', 1, ['a']);
69 | let catalog = new Catalog();
70 | catalog.add(version1a);
71 | catalog.add(version1b);
72 | assert.strictEqual(version1b, catalog.getTable('foo'))
73 | });
74 | });
--------------------------------------------------------------------------------
/test/IonImport.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {assert} from 'chai';
17 | import {SharedSymbolTable} from "../src/IonSharedSymbolTable";
18 | import {Import} from "../src/IonImport";
19 |
20 | describe('Import', () => {
21 | it('Orphan import has offset of 1', () => {
22 | let symbolTable = new SharedSymbolTable('foo', 1, ['a']);
23 | let import_ = new Import(null, symbolTable);
24 | assert.equal(import_.getSymbolId('a'), 1);
25 | });
26 |
27 | it('Child symbol ids start after parent', () => {
28 | let parentSymbolTable = new SharedSymbolTable('foo', 1, ['a', 'b']);
29 | let parentImport = new Import(null, parentSymbolTable);
30 | let childSymbolTable = new SharedSymbolTable('bar', 1, ['c']);
31 | let childImport = new Import(parentImport, childSymbolTable);
32 |
33 | assert.equal(childImport.getSymbolId('a'), 1);
34 | assert.equal(childImport.getSymbolId('b'), 2);
35 | assert.equal(childImport.getSymbolId('c'), 3);
36 | });
37 |
38 | it('Child import consults parent import first', () => {
39 | let symbolTable = new SharedSymbolTable('foo', 1, ['a', 'b', 'c']);
40 | let parent = new Import(null, symbolTable);
41 | let child = new Import(parent, symbolTable);
42 |
43 | // Sanity check the parent
44 | assert.equal(parent.getSymbolId('a'), 1);
45 | assert.equal(parent.getSymbolId('b'), 2);
46 | assert.equal(parent.getSymbolId('c'), 3);
47 |
48 | // Verify that child ids match parent ids
49 | assert.equal(child.getSymbolId('a'), 1);
50 | assert.equal(child.getSymbolId('b'), 2);
51 | assert.equal(child.getSymbolId('c'), 3);
52 |
53 | // Verify that duplicate symbols are accessible
54 | assert.equal(child.getSymbolText(4), 'a');
55 | assert.equal(child.getSymbolText(5), 'b');
56 | assert.equal(child.getSymbolText(6), 'c');
57 | });
58 |
59 | it('Short length omits symbols', () => {
60 | let symbolTable = new SharedSymbolTable('foo', 1, ['a', 'b', 'c']);
61 | let parent = new Import(null, symbolTable, 1);
62 | let child = new Import(parent, symbolTable);
63 |
64 | assert.equal(child.getSymbolText(1), 'a');
65 | assert.equal(child.getSymbolText(2), 'a');
66 | assert.equal(child.getSymbolText(3), 'b');
67 | assert.equal(child.getSymbolText(4), 'c');
68 | });
69 |
70 | it('Long length pads symbols', () => {
71 | let symbolTable = new SharedSymbolTable('foo', 1, ['a', 'b', 'c']);
72 | let parent = new Import(null, symbolTable, 4);
73 | let child = new Import(parent, symbolTable);
74 |
75 | assert.equal(child.getSymbolText(1), 'a');
76 | assert.equal(child.getSymbolText(2), 'b');
77 | assert.equal(child.getSymbolText(3), 'c');
78 | assert.isUndefined(child.getSymbolText(4));
79 | assert.equal(child.getSymbolText(5), 'a');
80 | assert.equal(child.getSymbolText(6), 'b');
81 | assert.equal(child.getSymbolText(7), 'c');
82 | });
83 | });
--------------------------------------------------------------------------------
/test/IonReaderConsistency.ts:
--------------------------------------------------------------------------------
1 | import { assert } from "chai";
2 | import * as ion from "../src/Ion";
3 | import { IonType } from "../src/Ion";
4 | import { IonTypes } from "../src/IonTypes";
5 |
6 | describe("IonReaderConsistency", () => {
7 | let writerTypes = [
8 | { name: "Binary", mkInstance: () => ion.makeBinaryWriter() },
9 | { name: "Text", mkInstance: () => ion.makeTextWriter() },
10 | { name: "Pretty", mkInstance: () => ion.makePrettyWriter() },
11 | ];
12 |
13 | // regression test for https://github.com/amazon-ion/ion-js/issues/514
14 | writerTypes.forEach((writerType) => {
15 | it(
16 | "Reads annotations correctly from structs created by a " +
17 | writerType.name +
18 | " writer",
19 | () => {
20 | let writer = writerType.mkInstance();
21 | // writes the following values with the provided writer, sums the 'id' ints
22 | // within structs annotated with 'foo', and verifies the sum is 26:
23 | //
24 | // foo::{ quantity: 7 }
25 | // bar::{ name: "x", id: 1 }
26 | // baz::{ items:["thing1", "thing2"] }
27 | // foo::{ quantity: 19 }
28 | // bar::{ name: "y", id: 8 }
29 |
30 | writer.setAnnotations(["foo"]);
31 | writer.stepIn(IonTypes.STRUCT);
32 | writer.writeFieldName("quantity");
33 | writer.writeInt(7);
34 | writer.stepOut();
35 |
36 | writer.setAnnotations(["bar"]);
37 | writer.stepIn(IonTypes.STRUCT);
38 | writer.writeFieldName("name");
39 | writer.writeString("x");
40 | writer.writeFieldName("id");
41 | writer.writeInt(1);
42 | writer.stepOut();
43 |
44 | writer.setAnnotations(["baz"]);
45 | writer.stepIn(IonTypes.STRUCT);
46 | writer.writeFieldName("items");
47 | writer.stepIn(IonTypes.LIST);
48 | writer.writeString("thing1");
49 | writer.writeString("thing2");
50 | writer.stepOut();
51 | writer.stepOut();
52 |
53 | writer.setAnnotations(["foo"]);
54 | writer.stepIn(IonTypes.STRUCT);
55 | writer.writeFieldName("quantity");
56 | writer.writeInt(19);
57 | writer.stepOut();
58 |
59 | writer.setAnnotations(["bar"]);
60 | writer.stepIn(IonTypes.STRUCT);
61 | writer.writeFieldName("name");
62 | writer.writeString("y");
63 | writer.writeFieldName("id");
64 | writer.writeInt(8);
65 | writer.stepOut();
66 |
67 | writer.close();
68 |
69 | let reader = ion.makeReader(writer.getBytes());
70 | let sum = 0;
71 | let type: IonType | null;
72 | while ((type = reader.next())) {
73 | if (type === IonTypes.STRUCT) {
74 | let annotations = reader.annotations();
75 | if (annotations.length > 0 && annotations[0] === "foo") {
76 | reader.stepIn();
77 | while ((type = reader.next())) {
78 | if (reader.fieldName() === "quantity") {
79 | sum += reader.numberValue()!;
80 | break;
81 | }
82 | }
83 | reader.stepOut();
84 | }
85 | }
86 | }
87 |
88 | assert.equal(sum, 26);
89 | }
90 | );
91 |
92 | it("Reads big-ints created by a " + writerType.name + " writer", () => {
93 | let writer = writerType.mkInstance();
94 | writer.writeInt(1);
95 | writer.close();
96 | let reader = ion.makeReader(writer.getBytes());
97 | reader.next();
98 | let result = reader.bigIntValue()!;
99 | assert.isTrue(result === 1n);
100 | });
101 | });
102 | });
103 |
--------------------------------------------------------------------------------
/test/IonReaderStepOutThrows.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {assert} from "chai";
17 | import * as ion from "../src/Ion";
18 | import {IonTypes, Writer} from "../src/Ion";
19 |
20 | describe('IonReaderStepOutThrows', () => {
21 | function writeData(writer: Writer): Uint8Array {
22 | writer.stepIn(IonTypes.STRUCT);
23 | writer.writeFieldName('a');
24 | writer.writeInt(1);
25 | writer.stepOut();
26 | writer.close();
27 | return writer.getBytes();
28 | }
29 |
30 | let readerTypes = [
31 | {name: 'Binary', data: writeData(ion.makeBinaryWriter())},
32 | {name: 'Text', data: writeData(ion.makeTextWriter())},
33 | ];
34 |
35 | readerTypes.forEach(readerType => {
36 | it(readerType.name + 'Reader.stepOut() throws when called immediately', () => {
37 | let r = ion.makeReader(readerType.data);
38 | assert.throws(() => { r.stepOut() });
39 | });
40 |
41 | it(readerType.name + 'Reader.stepOut() throws when called after next', () => {
42 | let r = ion.makeReader(readerType.data);
43 | r.next();
44 | assert.throws(() => { r.stepOut() });
45 | });
46 |
47 | it(readerType.name + 'Reader.stepOut() throws after returning to top-level', () => {
48 | let r = ion.makeReader(readerType.data);
49 | r.next();
50 | r.stepIn();
51 | r.next();
52 | r.stepOut();
53 | assert.throws(() => { r.stepOut() });
54 | });
55 | });
56 | });
57 |
58 |
--------------------------------------------------------------------------------
/test/IonText.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {assert} from 'chai';
17 | import * as ion from '../src/Ion';
18 | import * as IonText from '../src/IonText';
19 |
20 | let stringToCharCodes = function (text: string): Uint8Array {
21 | let charCodes = new Uint8Array(text.length);
22 | for (let i = 0; i < text.length; i++) {
23 | charCodes[i] = text.charCodeAt(i);
24 | }
25 | return charCodes;
26 | };
27 |
28 | let base64Test = function (text: string, expected: string) {
29 | let msg = text + ' encoded in base64 is ' + expected;
30 | it(msg, () => {
31 | let charCodes = stringToCharCodes(text);
32 | assert.equal(ion.toBase64(charCodes), expected);
33 | });
34 | };
35 |
36 | let isIdentifierTest = function (text: string, expected) {
37 | let name = text + ' ' + (expected ? 'is' : 'is not') + ' an identifier';
38 | it(name, () => {
39 | assert.equal(IonText.isIdentifier(text), expected);
40 | });
41 | };
42 |
43 | let isOperatorTest = function (text: string, expected) {
44 | let name = text + ' ' + (expected ? 'is' : 'is not') + ' an operator';
45 | it(name, () => {
46 | assert.equal(IonText.isOperator(text), expected);
47 | });
48 | };
49 |
50 | let escapeTest = (ch, expected) => {
51 | let value = String.fromCharCode(ch);
52 | it('escape(chr(' + ch + '), ClobEscapes)', () => {
53 | assert.equal(IonText.escape(value, IonText.ClobEscapes), expected);
54 | });
55 | it('escape(chr(' + ch + '), StringEscapes)', () => {
56 | assert.equal(IonText.escape(value, IonText.StringEscapes), expected);
57 | });
58 | it('escape(chr(' + ch + '), SymbolEscapes)', () => {
59 | assert.equal(IonText.escape(value, IonText.SymbolEscapes), expected);
60 | });
61 | };
62 |
63 | describe('IonText', () => {
64 | describe('base64 encoding', () => {
65 | base64Test("M", "TQ==");
66 | base64Test("Ma", "TWE=");
67 | base64Test("Man", "TWFu");
68 | base64Test("ManM", "TWFuTQ==");
69 | base64Test("ManMa", "TWFuTWE=");
70 | base64Test("ManMan", "TWFuTWFu");
71 | });
72 | describe('Detecting identifiers', () => {
73 | isIdentifierTest('$', true);
74 | isIdentifierTest('0$', false);
75 | isIdentifierTest('abc123', true);
76 | isIdentifierTest('_', true);
77 | isIdentifierTest('{}', false);
78 | });
79 | describe('Detecting operators', () => {
80 | isOperatorTest('!', true);
81 | isOperatorTest('!!', true);
82 | isOperatorTest('a', false);
83 | isOperatorTest('a!', false);
84 | isOperatorTest('!a', false);
85 | isOperatorTest('<=>', true);
86 | });
87 | describe('Escaping strings', () => {
88 | for (let i = 0; i < 32; i++) {
89 | let expected;
90 | switch (i) {
91 | case 0:
92 | expected = '\\0';
93 | break;
94 | case 7:
95 | expected = '\\a';
96 | break;
97 | case 8:
98 | expected = '\\b';
99 | break;
100 | case 9:
101 | expected = '\\t';
102 | break;
103 | case 10:
104 | expected = '\\n';
105 | break;
106 | case 11:
107 | expected = '\\v';
108 | break;
109 | case 12:
110 | expected = '\\f';
111 | break;
112 | case 13:
113 | expected = '\\r';
114 | break;
115 | default:
116 | let hex = i.toString(16);
117 | expected = '\\x' + '0'.repeat(2 - hex.length) + hex;
118 | }
119 | escapeTest(i, expected);
120 | }
121 | escapeTest(0x7e, '~'); // not escaped
122 | escapeTest(0x7f, '\\x7f');
123 | escapeTest(0x9f, '\\x9f');
124 | escapeTest(0xa0, '\xa0'); // not escaped
125 | });
126 | });
--------------------------------------------------------------------------------
/test/IonUnicode.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {assert} from 'chai';
17 | import {suite, test} from "mocha-typescript";
18 | import {encodeUtf8} from '../src/IonUnicode';
19 |
20 | @suite('Unicode')
21 | class UnicodeTests {
22 | @test "Encode dollar sign"() {
23 | assert.deepEqual(encodeUtf8('$'), new Uint8Array([0x24]));
24 | }
25 |
26 | @test "Encode cent sign"() {
27 | assert.deepEqual(encodeUtf8('¢'), new Uint8Array([0xc2, 0xa2]));
28 | }
29 |
30 | @test "Encode euro sign"() {
31 | assert.deepEqual(encodeUtf8('€'), new Uint8Array([0xe2, 0x82, 0xac]));
32 | }
33 |
34 | @test "Gothic letter hwair"() {
35 | assert.deepEqual(
36 | encodeUtf8(String.fromCodePoint(0x10348)),
37 | new Uint8Array([0xf0, 0x90, 0x8d, 0x88])
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/test/IonWriteable.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {assert} from 'chai';
17 | import {suite, test} from "mocha-typescript";
18 | import {Writeable} from '../src/IonWriteable';
19 |
20 | @suite('Writeable')
21 | class WriteableTests {
22 | @test "writePartialArray"() {
23 | let writeable = new Writeable();
24 | writeable.writeBytes(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]), 4, 4);
25 | assert.deepEqual(writeable.getBytes(), new Uint8Array([5, 6, 7, 8]));
26 | }
27 | }
--------------------------------------------------------------------------------
/test/IonWriterUndefinedParameters.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import { assert } from "chai";
17 | import * as ion from "../src/Ion";
18 | import { Decimal, IonType, Reader, Timestamp, Writer } from "../src/Ion";
19 |
20 | // This dummy impl exists as a substitute for reflection directly against
21 | // the Writer interface (which TypeScript doesn't currently support)
22 | class NoopWriter implements Writer {
23 | addAnnotation(annotation: string): void {}
24 | close(): void {}
25 | depth(): number {
26 | return 0;
27 | }
28 | getBytes(): Uint8Array {
29 | return new Uint8Array();
30 | }
31 | setAnnotations(annotations: string[]): void {}
32 | stepIn(type: IonType): void {}
33 | stepOut(): void {}
34 | writeBlob(value: Uint8Array | null): void {}
35 | writeBoolean(value: boolean | null): void {}
36 | writeClob(value: Uint8Array | null): void {}
37 | writeDecimal(value: Decimal | null): void {}
38 | writeFieldName(fieldName: string): void {}
39 | writeFloat32(value: number | null): void {}
40 | writeFloat64(value: number | null): void {}
41 | writeInt(value: number | bigint | null): void {}
42 | writeNull(type: IonType): void {}
43 | writeString(value: string | null): void {}
44 | writeSymbol(value: string | null): void {}
45 | writeTimestamp(value: Timestamp | null): void {}
46 | writeValue(reader: Reader): void {}
47 | writeValues(reader: Reader): void {}
48 | }
49 |
50 | describe("IonWriterUndefinedParameters", () => {
51 | let testMethods = Reflect.ownKeys(NoopWriter.prototype)
52 | .map((method) => method.toString())
53 | .filter((name) => name.startsWith("write"))
54 | .filter((name) => name !== "writeNull")
55 | .concat("addAnnotation", "setAnnotations", "stepIn");
56 |
57 | let writerTypes = [
58 | { name: "Binary", instance: ion.makeBinaryWriter() },
59 | { name: "Text", instance: ion.makeTextWriter() },
60 | { name: "Pretty", instance: ion.makePrettyWriter() },
61 | ];
62 | writerTypes.forEach((writerType) => {
63 | let writer = writerType.instance;
64 | describe(writerType.name + " writer", () => {
65 | testMethods.forEach(function (method) {
66 | it(method + "(undefined) throws", function () {
67 | assert.throws(() => writer[method](undefined));
68 | });
69 | });
70 | });
71 | });
72 | });
73 |
--------------------------------------------------------------------------------
/test/dom/propertyShadowing.ts:
--------------------------------------------------------------------------------
1 | import { assert } from "chai";
2 | import { dom, IonTypes } from "../../src/Ion";
3 |
4 | describe("dom.Struct property shadowing", () => {
5 | it("Built-in properties cannot be shadowed", () => {
6 | // Create a struct with field names that conflict with method names
7 | let s = dom.Value.from(
8 | {
9 | getType: "baz",
10 | getAnnotations: 56,
11 | fieldNames: ["dog", "cat", "mouse"],
12 | toString: 10,
13 | greetings: "hi",
14 | age: 7,
15 | },
16 | ["foo", "bar"]
17 | );
18 |
19 | // Method names are still directly accessible
20 | assert.equal(s.getType(), IonTypes.STRUCT);
21 | assert.deepEqual(s.getAnnotations(), ["foo", "bar"]);
22 | assert.deepEqual(s.fieldNames(), [
23 | "getType",
24 | "getAnnotations",
25 | "fieldNames",
26 | "toString",
27 | "greetings",
28 | "age",
29 | ]);
30 |
31 | // Fields with names that would shadow a built-in are accessible via Value#get()
32 | assert.equal(s.get("getType")!.stringValue(), "baz");
33 | assert.equal(s.get("getAnnotations")!.numberValue(), 56);
34 | assert.equal(s.get("fieldNames", 0)!.stringValue(), "dog");
35 | assert.equal(s.get("fieldNames", 1)!.stringValue(), "cat");
36 | assert.equal(s.get("fieldNames", 2)!.stringValue(), "mouse");
37 |
38 | // deleteProperty proxy method
39 | assert.isTrue(delete s["greetings"]);
40 | assert.deepEqual(s.fieldNames(), [
41 | "getType",
42 | "getAnnotations",
43 | "fieldNames",
44 | "toString",
45 | "age",
46 | ]);
47 |
48 | // deleteField Struct method
49 | assert.isTrue(s.deleteField("age"));
50 | assert.deepEqual(s.fieldNames(), [
51 | "getType",
52 | "getAnnotations",
53 | "fieldNames",
54 | "toString",
55 | ]);
56 |
57 | // delete for properties that match built-in
58 | assert.equal(s.get("toString")!.numberValue(), 10);
59 | assert.isTrue(delete (s as any)["toString"]);
60 | assert.equal(typeof s.toString, "function");
61 | assert.deepEqual(s.fieldNames(), [
62 | "getType",
63 | "getAnnotations",
64 | "fieldNames",
65 | ]);
66 |
67 | // delete for field that doesn't exist
68 | assert.isFalse(s.deleteField("toString"));
69 | assert.isFalse(s.deleteField("greetings"));
70 | assert.isFalse(s.deleteField("name"));
71 | assert.isUndefined(s["greetings"]);
72 |
73 | // deleteField will throw an error if it's called on a dom.Value that isn't a struct
74 | let l = dom.Value.from([1, 2, 3]);
75 | assert.throws(() => l.deleteField("1"), Error);
76 |
77 | // get() does not return values for properties on `Object`
78 | assert.isNull(s.get("toString"));
79 | assert.isNull(s.get("toLocaleString"));
80 | assert.isNull(s.get("valueOf"));
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/test/dump.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {assert} from 'chai';
17 | import * as ion from '../src/Ion';
18 | import {dom, IonTypes} from '../src/Ion';
19 | import {exampleIonValuesWhere, exampleJsValuesWhere} from "./exampleValues";
20 |
21 | function roundTripTests(...values: any[]) {
22 | let expectedValues: dom.Value[] = values.map((jsValue) => dom.Value.from(jsValue));
23 | it(`dumpBinary(${values})`, () => roundTripTest(ion.dumpBinary, values, expectedValues));
24 | it(`dumpText(${values})`,() => roundTripTest(ion.dumpText, values, expectedValues));
25 | it(`dumpPrettyText(${values})`, () => roundTripTest(ion.dumpPrettyText, values, expectedValues));
26 | }
27 |
28 | // Dumps the provided values to Ion using the specified dump*() function, uses loadAll() to read the values
29 | // back in, then compares the input values to the loaded values.
30 | function roundTripTest(dumpFn: (...values: any[]) => any,
31 | inputValues: any[],
32 | expectedValues: dom.Value[]) {
33 | let actualValues: dom.Value[] = dom.loadAll(dumpFn(inputValues));
34 | expectedValues.forEach((expectedValue, index) => {
35 | let actualValue = actualValues[index];
36 | //TODO: This tests for a very weak common definition of equality.
37 | // We should make this a stronger test when .equals() is added to the Value interface[1].
38 | // In the meantime, the core writing/equality logic is more strictly tested by
39 | // test/dom/Value.ts, which evaluates the data produced by the Value.writeTo() function
40 | // at the core of the ion.dump*() methods.
41 | // [1] https://github.com/amazon-ion/ion-js/issues/576
42 | assert.deepEqual(actualValue.getAnnotations(), expectedValue.getAnnotations());
43 | assert.equal(actualValue.isNull(), expectedValue.isNull());
44 | assert.equal(actualValue.getType(), expectedValue.getType());
45 | });
46 | }
47 |
48 | describe('dump*()', () => {
49 | describe('Round tripping JS values', () => {
50 | roundTripTests(exampleJsValuesWhere())
51 | });
52 | describe('Round tripping Ion values', () => {
53 | roundTripTests(exampleIonValuesWhere())
54 | });
55 | it('Text stream', () => {
56 | let ionText = ion.dumpText(null, 7, "Hello", [1, 2, 3]);
57 | assert.equal(ionText, 'null\n7\n"Hello"\n[1,2,3]');
58 | });
59 | it('Pretty text stream', () => {
60 | let ionPrettyText = ion.dumpPrettyText(null, 7, "Hello", [1, 2, 3]);
61 | assert.equal(ionPrettyText, 'null\n7\n"Hello"\n[\n 1,\n 2,\n 3\n]');
62 | });
63 | it('Binary stream', () => {
64 | let ionBinary: Uint8Array = ion.dumpBinary(null, 7, "Hello", [1, 2, 3]);
65 | let writer = ion.makeBinaryWriter();
66 | writer.writeNull(IonTypes.NULL);
67 | writer.writeInt(7);
68 | writer.writeString("Hello");
69 | writer.stepIn(IonTypes.LIST);
70 | writer.writeInt(1);
71 | writer.writeInt(2);
72 | writer.writeInt(3);
73 | writer.stepOut();
74 | writer.close();
75 | assert.deepEqual(ionBinary, writer.getBytes());
76 | });
77 | it('Struct with annotated field large enough to require a VarUInt length', () => {
78 | let ionText = "{a: b:: \"abcdefghijkl\"}";
79 | let ionValue = ion.load(ionText);
80 | let binaryIonBytes = ion.dumpBinary(ionValue);
81 | let ionBinaryValue = ion.load(binaryIonBytes);
82 | assert.deepEqual(ionValue, ionBinaryValue);
83 | });
84 | it('Struct with annotated field large enough to require a VarUInt length', () => {
85 | // this ionText is taken from https://github.com/amazon-ion/ion-js/issues/621
86 | let ionText = "'com.example.organization.model.data.ClassName2@1.0'::{\n" +
87 | " values: [\n" +
88 | " 'com.example.organization.model.data.ClassName1@1.0'::{\n" +
89 | " field_name_1: 'com.example.organization.model.types.ClassName1@1.0'::{\n" +
90 | " field_name_2: 'com.example.organization.model.types.ClassName2@1.0'::{\n" +
91 | " field_name_3: 9999999999.00,\n" +
92 | " field_name_4: ABC\n" +
93 | " }\n" +
94 | " },\n" +
95 | " field_name_5: AB\n" +
96 | " },\n" +
97 | " 'com.example.organization.model.data.ClassName1@1.0'::{\n" +
98 | " field_name_1: 'com.example.organization.model.types.ClassName1@1.0'::{\n" +
99 | " field_name_2: 'com.example.organization.model.types.ClassName2@1.0'::{\n" +
100 | " field_name_3: 9999999999.00,\n" +
101 | " field_name_4: DEF\n" +
102 | " }\n" +
103 | " },\n" +
104 | " field_name_5: CD\n" +
105 | " }\n" +
106 | " ]\n" +
107 | "}";
108 | let ionValue = ion.load(ionText);
109 | let binaryIonBytes = ion.dumpBinary(ionValue);
110 | let ionBinaryValue = ion.load(binaryIonBytes);
111 | assert.deepEqual(ionValue, ionBinaryValue);
112 | })
113 | });
114 |
--------------------------------------------------------------------------------
/test/exampleValues.ts:
--------------------------------------------------------------------------------
1 | import {Timestamp} from "../src/IonTimestamp";
2 | import {Decimal} from "../src/IonDecimal";
3 | import {load, Value} from "../src/dom";
4 |
5 | // Used as a default filtering predicate in functions below
6 | function acceptAnyValue(_: any) : boolean {
7 | return true;
8 | }
9 |
10 | // A common collection of JS values that can be reduced to a relevant subset using the provided filter function.
11 | export function exampleJsValuesWhere(filter: (v: any) => boolean = acceptAnyValue): any[] {
12 | return [
13 | null,
14 | true, false,
15 | Number.MIN_SAFE_INTEGER, -7.5, -7, 0, 7, 7.5, Number.MAX_SAFE_INTEGER,
16 | "", "foo",
17 | new Date(0),
18 | Timestamp.parse('1970-01-01T00:00:00Z'),
19 | new Decimal('1.5'),
20 | [], [1, 2, 3], [{foo: "bar"}],
21 | {}, {foo: "bar", baz: 5}, {foo: [1, 2, 3]}
22 | ].filter(filter);
23 | }
24 |
25 | // A common collection of Ion dom.Value instances that can be reduced to a relevant subset using
26 | // the provided filter function.
27 | export function exampleIonValuesWhere(filter: (v: Value) => boolean = acceptAnyValue): Value[] {
28 | return [
29 | load('null')!, // null
30 | load('null.string')!, // typed null
31 | load('true')!, // boolean
32 | load('1')!, // integer
33 | load('15e-1')!, // float
34 | load('15d-1')!, // decimal
35 | load('1970-01-01T00:00:00.000Z')!, // timestamp
36 | load('"Hello"')!, // string
37 | load('Hello')!, // symbol
38 | load('{{aGVsbG8gd29ybGQ=}}')!, // blob
39 | load('{{"February"}}')!, // clob
40 | load('[1, 2, 3]')!, // list
41 | load('(1 2 3)')!, // s-expression
42 | load('{foo: true, bar: "Hello", baz: 5, qux: null}')! // struct
43 | ].filter(filter);
44 | }
45 |
46 | const _exampleIsoStrings: string[] = [
47 | "1970-01-01T00:00:00Z",
48 | '2020-02-28T23:00:00.000-01:00',
49 | "2020-02-29T00:00:00Z",
50 | "2020-02-29T00:00:00+01:00",
51 | "2020-02-29T00:00:00-01:00",
52 | '2020-03-01T00:00:00.000+01:00',
53 | "2020-03-19T03:17:59.999Z",
54 | "2020-03-19T03:17:59+03:21",
55 | "2020-03-19T23:59:59-05:00",
56 | "2020-03-19T23:01:01-08:00",
57 | "2020-03-19T11:30:30-08:00",
58 | "2020-03-19T11:30:30.5-08:00",
59 | "2020-03-19T11:30:30.50-08:00",
60 | "2020-03-19T11:30:30.500-08:00",
61 | "2020-03-22T11:30:30.22-08:00",
62 | "2020-03-27T00:00:00Z",
63 | "2020-03-27T00:00:00.000Z",
64 | "2020-03-27T12:00:00-05:00",
65 | "2020-03-27T12:00:00-08:00",
66 | "2020-03-27T12:00:00+01:00",
67 | "2020-03-27T19:00:00-05:00",
68 | "2020-03-27T16:00:00-08:00",
69 | "2020-03-27T16:00:00.5-08:00",
70 | "2020-03-28T01:00:00+01:00",
71 | "2020-03-28T01:00:00.123456+01:00",
72 | "2020-03-28T01:00:00.123456789+01:00",
73 | ];
74 |
75 | // A common collection of Date values that can be reduced to a relevant subset using
76 | // the provided filter function.
77 | export function exampleDatesWhere(filter: (v: Date) => boolean = acceptAnyValue): Date[] {
78 | return _exampleIsoStrings
79 | .map((isoString) => new Date(isoString))
80 | .filter(filter);
81 | }
82 |
83 | // A common collection of Timestamp values that can be reduced to a relevant subset using
84 | // the provided filter function.
85 | export function exampleTimestampsWhere(filter: (v: Timestamp) => boolean = acceptAnyValue): Timestamp[] {
86 | return _exampleIsoStrings
87 | .map((isoString) => Timestamp.parse(isoString)!)
88 | .filter(filter);
89 | }
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --require ts-node/register
2 | --ui mocha-typescript
3 | --require source-map-support/register
4 | #Increasing test timeout for node v14, refer To: https://github.com/amazon-ion/ion-js/issues/581
5 | --timeout 3000
6 |
7 | test/*.ts
8 | test/dom/*.ts
9 |
--------------------------------------------------------------------------------
/test/mochaSupport.ts:
--------------------------------------------------------------------------------
1 | // Generates a mocha-friendly string representation of the provided value that can be used in test names
2 | import {_hasValue} from "../src/util";
3 |
4 | export function valueName(value: any): string {
5 | if (value === null) {
6 | return 'null';
7 | }
8 | if (typeof value === "object") {
9 | let typeText = _hasValue(value.constructor) ? value.constructor.name : 'Object';
10 | let valueText = _hasValue(value.toString) ? `${value}` : JSON.stringify(value);
11 |
12 | return `${typeText}(${valueText})`;
13 | }
14 | return `${typeof value}(${value})`;
15 | }
16 |
--------------------------------------------------------------------------------
/test/spans.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {assert} from 'chai';
17 | import {suite, test} from "mocha-typescript";
18 | import * as IonSpan from '../src/IonSpan';
19 |
20 | @suite('Spans')
21 | class UtilTests {
22 | @test "null valueAt"() {
23 | let span = new IonSpan.StringSpan("null");
24 | assert.equal('n'.charCodeAt(0), span.valueAt(0));
25 | assert.equal('u'.charCodeAt(0), span.valueAt(1));
26 | assert.equal('l'.charCodeAt(0), span.valueAt(2));
27 | assert.equal('l'.charCodeAt(0), span.valueAt(3));
28 | assert.equal(-1, span.valueAt(4));
29 | }
30 |
31 | @test "null next"() {
32 | let span = new IonSpan.StringSpan("null");
33 | assert.equal('n'.charCodeAt(0), span.next());
34 | assert.equal('u'.charCodeAt(0), span.next());
35 | assert.equal('l'.charCodeAt(0), span.next());
36 | assert.equal('l'.charCodeAt(0), span.next());
37 | assert.equal(-1, span.next());
38 | }
39 | }
--------------------------------------------------------------------------------
/test/textNulls.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {assert} from 'chai';
17 | import {suite, test} from "mocha-typescript";
18 | import * as ion from '../src/Ion';
19 |
20 | @suite('Reading nulls')
21 | class TextNullTests {
22 | @test "Reading 'null'"() {
23 | let reader = ion.makeReader("null");
24 | assert.equal(reader.next(), ion.IonTypes.NULL);
25 | assert.equal(reader.next(), undefined);
26 | }
27 |
28 | @test "Stepping into a null container"() {
29 | let reader = ion.makeReader("null.list");
30 | assert.equal(reader.next(), ion.IonTypes.LIST);
31 | assert.isTrue(reader.isNull());
32 |
33 | let fail = true;
34 | try {
35 | reader.stepIn();
36 | } catch (e) {
37 | fail = false;
38 | }
39 | assert.isFalse(fail, "Stepped into null container");
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/test/util.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {assert} from 'chai';
17 | import {suite, test} from "mocha-typescript";
18 | import * as util from "../src/util";
19 |
20 | @suite('util')
21 | class UtilTests {
22 | @test "_hasValue(undefined)"() {
23 | assert.equal(util._hasValue(undefined), false);
24 | }
25 |
26 | @test "_hasValue(null)"() {
27 | assert.equal(util._hasValue(null), false);
28 | }
29 |
30 | @test "_hasValue(0)"() {
31 | assert.equal(util._hasValue(0), true);
32 | }
33 |
34 | @test "_hasValue(1)"() {
35 | assert.equal(util._hasValue(1), true);
36 | }
37 |
38 | @test "_sign(-1)"() {
39 | assert.equal(util._sign(-1), -1);
40 | }
41 |
42 | @test "_sign(-0)"() {
43 | assert.equal(util._sign(-0), -1);
44 | }
45 |
46 | @test "_sign(0)"() {
47 | assert.equal(util._sign(0), 1);
48 | }
49 |
50 | @test "_sign(1)"() {
51 | assert.equal(util._sign(1), 1);
52 | }
53 | }
--------------------------------------------------------------------------------
/test/writeSymbolTokens.ts:
--------------------------------------------------------------------------------
1 | /*!
2 | * Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | import {assert} from 'chai';
17 | import * as ion from '../src/Ion';
18 | import * as IonText from '../src/IonText';
19 |
20 | function verifyQuoteBehavior(symbolText: string, shouldQuote: boolean, shouldQuoteInSexp = true) {
21 | let writer = ion.makeTextWriter();
22 |
23 | writer.writeSymbol(symbolText); // symbol
24 |
25 | writer.setAnnotations([symbolText]);
26 | writer.writeInt(5); // annotation
27 |
28 | writer.stepIn(ion.IonTypes.STRUCT);
29 | writer.writeFieldName(symbolText); // fieldname
30 | writer.writeInt(5);
31 | writer.stepOut();
32 |
33 | writer.stepIn(ion.IonTypes.SEXP);
34 | writer.writeSymbol(symbolText); // symbol in sexp (operators should not be quoted)
35 | writer.stepOut();
36 |
37 | writer.close();
38 | let actual = String.fromCharCode.apply(null, writer.getBytes());
39 |
40 | symbolText = IonText.escape(symbolText, IonText.SymbolEscapes);
41 |
42 | let expected = `${symbolText}\n${symbolText}::5\n{${symbolText}:5}\n(${symbolText})`;
43 | if (shouldQuote) {
44 | expected = `'${symbolText}'\n'${symbolText}'::5\n{'${symbolText}':5}\n`;
45 | if (shouldQuoteInSexp) {
46 | expected += `('${symbolText}')`;
47 | } else {
48 | expected += `(${symbolText})`;
49 | }
50 | }
51 | assert.equal(actual, expected);
52 | }
53 |
54 | function verifyQuotesAdded(symbolText: string) {
55 | verifyQuoteBehavior(symbolText, true)
56 | }
57 |
58 | function verifyNoQuotes(symbolText: string) {
59 | verifyQuoteBehavior(symbolText, false)
60 | }
61 |
62 | let symbolsThatNeedQuotes = [
63 | 'false',
64 | 'nan',
65 | 'null',
66 | 'true',
67 | '+inf',
68 | '-inf',
69 | '',
70 | ' ',
71 | '1',
72 | '1-2',
73 | '1.2',
74 | '-1.2',
75 | '{}',
76 | '[]',
77 | '"',
78 | "'",
79 | '$1',
80 | ];
81 |
82 | let symbolsThatDoNotNeedQuotes = [
83 | 'a',
84 | 'a_b',
85 | '$a',
86 | ];
87 |
88 | describe('Writing text symbol tokens', () => {
89 | describe('Symbols that need quotes', () => {
90 | for (let symbol of symbolsThatNeedQuotes) {
91 | it(symbol, () => verifyQuotesAdded(symbol));
92 | }
93 | });
94 | describe("Symbols that don't need quotes", () => {
95 | for (let symbol of symbolsThatDoNotNeedQuotes) {
96 | it(symbol, () => verifyNoQuotes(symbol));
97 | }
98 | });
99 | it('Symbols that need quotes outside of an S-Expression', () => verifyQuoteBehavior('+', true, false))
100 | });
--------------------------------------------------------------------------------
/tsconfig.amd.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "amd",
5 | "outDir": "dist/amd/es6"
6 | }
7 | }
--------------------------------------------------------------------------------
/tsconfig.es6.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "ESNext",
5 | "outDir": "dist/es6/es6"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | // Base Typescript Compiler Configuration
2 | // Targets CommonJS Module system and ES6 feature set.
3 | {
4 | "include": ["src/**/*"],
5 | "exclude": ["**/*.d.ts"],
6 | "compilerOptions": {
7 | "target": "es2020",
8 | "module": "commonjs",
9 | "moduleResolution": "node",
10 | "esModuleInterop": true,
11 | "lib": ["ESNext"],
12 | "declaration": true,
13 | "inlineSources": true,
14 | "sourceMap": true,
15 | // TODO enable this to have stricter checking
16 | "strict": false,
17 | "strictNullChecks": true,
18 | "rootDirs": ["src", "test"],
19 | "outDir": "dist/commonjs/es6",
20 | "experimentalDecorators": true
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/tsconfig.lint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [
4 | "src/**/*",
5 | "test/**/*",
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/typedoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Ion-JS Library",
3 |
4 | "excludePrivate": true,
5 | "excludeProtected": true,
6 |
7 | "exclude": [
8 | "src/AbstractWriter.ts",
9 | "src/IonConstants.ts",
10 | "src/IonImport.ts",
11 | "src/IonSubstituteSymbolTable.ts",
12 | "src/IonSpan.ts",
13 | "src/IonSymbol.ts",
14 | "src/IonSymbolIndex.ts",
15 | "src/IonUnicode.ts",
16 | "src/IonValue.ts",
17 | "src/IonWriteable.ts",
18 | "src/util.ts",
19 |
20 | "src/IonCatalog.ts",
21 | "src/IonSymbols.ts",
22 | "src/Ion*SymbolTable.ts",
23 |
24 | "src/IonBinary*.ts",
25 | "src/Ion+(Pretty|)Text*.ts",
26 | "src/*Raw.ts",
27 | "src/IonLowLevel*.ts",
28 |
29 | "src/IonTest*.ts",
30 | "src/IonEvent*.ts",
31 |
32 | "src/BigIntSerde.ts",
33 | "src/SignAndMagnitudeInt.ts"
34 | ],
35 |
36 | "out": "docs/api/",
37 |
38 | "hideGenerator": true
39 | }
40 |
--------------------------------------------------------------------------------