fhirsmith 0.18.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ACCESSIBILITY.md +119 -0
- package/CHANGELOG.md +190 -9
- package/CONFIGURATION.md +305 -0
- package/LICENSE +29 -0
- package/README.md +29 -122
- package/cluster.js +1220 -0
- package/cluster.md +707 -0
- package/config-template.json +77 -8
- package/extension-tracker/extension-tracker-template.html +5 -8
- package/extension-tracker/extension-tracker.js +0 -2
- package/folder/folder.js +7 -6
- package/ips/analysis.js +560 -0
- package/ips/ips-template.html +5 -8
- package/ips/ips.js +331 -33
- package/ips/jurisdictions.js +65 -0
- package/ips/nationality.js +120 -0
- package/ips/pipeline.js +131 -17
- package/ips/presenter.js +1 -1
- package/ips/profile-editor.js +283 -4
- package/ips/readme.md +170 -20
- package/ips/render.js +769 -109
- package/ips/scripts.js +46 -0
- package/ips/store.js +123 -11
- package/ips/structure.js +247 -0
- package/ips/terminology.js +1 -1
- package/library/body-limit.js +22 -0
- package/library/cache-generation.js +15 -0
- package/library/cgroup-memory.js +133 -1
- package/library/cluster-counts.js +121 -0
- package/library/cluster-events.js +153 -0
- package/library/cluster-metrics.js +193 -0
- package/library/cluster-status.js +468 -0
- package/library/config-reader.js +96 -0
- package/library/daily-report.js +100 -2
- package/library/html-server.js +27 -1
- package/library/i18nsupport.js +23 -1
- package/library/keep-alive.js +18 -0
- package/library/languages.js +17 -3
- package/library/logger.js +31 -0
- package/library/memory-guard.js +428 -0
- package/library/native-memory.js +52 -0
- package/library/npm-audit.js +51 -25
- package/library/openapi-doc.js +7 -5
- package/library/package-manager.js +8 -0
- package/library/role.js +197 -0
- package/library/server-credentials.js +169 -0
- package/library/sqlite-utilities.js +59 -0
- package/library/ssrf-guard.js +100 -0
- package/library/static-response-stats.js +16 -0
- package/library/version-utilities.js +50 -0
- package/library/worker-count.js +192 -0
- package/nginx.md +30 -11
- package/npmprojector/indexer.js +18 -10
- package/package.json +11 -8
- package/packages/feed-check-page.js +117 -0
- package/packages/feed-check.js +279 -0
- package/packages/npm-check.js +57 -3
- package/packages/npm-signing.js +219 -0
- package/packages/openapi.yaml +207 -1
- package/packages/package-crawler.js +322 -41
- package/packages/packages-template.html +6 -8
- package/packages/packages.js +604 -57
- package/packages/readme.md +113 -1
- package/publisher/publisher-template.html +2 -5
- package/publisher/publisher.js +121 -110
- package/publisher/readme.md +75 -0
- package/registry/api.js +179 -1
- package/registry/crawler.js +86 -108
- package/registry/documentation/api.md +68 -2
- package/registry/documentation/configuration.md +34 -0
- package/registry/documentation/internals.md +4 -1
- package/registry/documentation/readme.md +3 -2
- package/registry/model.js +8 -0
- package/registry/openapi.yaml +84 -0
- package/registry/registry-template.html +5 -8
- package/registry/registry.js +58 -6
- package/root-bare-template.html +7 -4
- package/root-template.html +5 -8
- package/server.js +398 -198
- package/shl/shl.js +38 -37
- package/static/a11y/a11y-check.js +832 -0
- package/static/a11y/axe.min.js +12 -0
- package/static/assets/css/bootstrap-fhir.css +15 -15
- package/static/assets/images/flags/au.svg +18 -0
- package/static/assets/images/flags/unknown.svg +11 -0
- package/static/assets/images/flags/us.svg +63 -0
- package/static/assets/images/icon_audit.png +0 -0
- package/static/assets/images/icon_clinical.png +0 -0
- package/static/assets/images/icon_document.png +0 -0
- package/static/assets/images/icon_external.png +0 -0
- package/static/assets/images/icon_medication.png +0 -0
- package/static/assets/images/icon_organization.png +0 -0
- package/static/assets/images/icon_other.png +0 -0
- package/static/assets/images/icon_person.png +0 -0
- package/static/assets/images/icon_section.png +0 -0
- package/stats-db.js +45 -0
- package/stats.js +197 -10
- package/testing/testing-template.html +5 -8
- package/token/nginx_passport_setup.md +1 -1
- package/token/security_guide.md +1 -1
- package/token/token-template.html +2 -5
- package/token/token.js +40 -36
- package/translations/Messages.properties +19 -0
- package/tx/README.md +158 -4
- package/tx/closure/closure-store.js +126 -3
- package/tx/cm/cm-database.js +69 -57
- package/tx/cm/cm-package.js +50 -16
- package/tx/cs/cs-api.js +284 -19
- package/tx/cs/cs-api.md +41 -4
- package/tx/cs/cs-areacode.js +1 -1
- package/tx/cs/cs-country.js +17 -5
- package/tx/cs/cs-cpt.js +33 -16
- package/tx/cs/cs-cs.js +199 -24
- package/tx/cs/cs-currency.js +1 -1
- package/tx/cs/cs-icd11.js +21 -13
- package/tx/cs/cs-icf.js +22 -3
- package/tx/cs/cs-lang.js +2 -2
- package/tx/cs/cs-loinc.js +193 -67
- package/tx/cs/cs-mimetypes.js +12 -2
- package/tx/cs/cs-ndc.js +40 -25
- package/tx/cs/cs-omop.js +54 -29
- package/tx/cs/cs-rxnorm.js +300 -196
- package/tx/cs/cs-snomed.js +861 -95
- package/tx/cs/cs-ucum.js +14 -4
- package/tx/cs/cs-unii.js +18 -14
- package/tx/cs/cs-virtual.js +210 -0
- package/tx/ecosystem-resolver.js +413 -0
- package/tx/html/cluster-dash-metrics.liquid +26 -0
- package/tx/html/cluster-home-metrics.liquid +31 -0
- package/tx/html/cluster-metrics-script.liquid +251 -0
- package/tx/html/codesystem-operations.liquid +3 -3
- package/tx/html/ecl-panel.liquid +1 -1
- package/tx/html/external-metrics.liquid +17 -0
- package/tx/html/operations-form.liquid +17 -17
- package/tx/html/search-form.liquid +11 -11
- package/tx/html/status-line.liquid +58 -0
- package/tx/html/status-panel.liquid +140 -0
- package/tx/html/status-tasks.liquid +25 -0
- package/tx/html/tx-template.html +2 -5
- package/tx/importers/atc-to-fhir.js +17 -16
- package/tx/importers/export-icd11-codesystem.js +3 -2
- package/tx/importers/import-icd11.js +4 -3
- package/tx/importers/import-loinc.module.js +118 -56
- package/tx/importers/import-ndc.module.js +56 -44
- package/tx/importers/import-rxnorm.module.js +118 -79
- package/tx/importers/import-sct.module.js +125 -61
- package/tx/importers/import-unii.module.js +37 -36
- package/tx/importers/subset-loinc.module.js +49 -48
- package/tx/importers/subset-rxnorm.module.js +29 -28
- package/tx/importers/terminal.js +18 -0
- package/tx/importers/tx-import-base.js +13 -12
- package/tx/importers/tx-import-settings.js +5 -4
- package/tx/importers/tx-import.js +53 -52
- package/tx/library/canonical-resource.js +49 -0
- package/tx/library/codesystem.js +94 -5
- package/tx/library/designations.js +45 -2
- package/tx/library/extensions.js +13 -7
- package/tx/library/implicit-valuesets.js +66 -0
- package/tx/library/renderer.js +77 -21
- package/tx/library/sqlite-utilities.js +2 -0
- package/tx/library/tho-precedence.js +40 -0
- package/tx/library/ucum-parsers.js +11 -1
- package/tx/library/wait-for-cache.js +43 -0
- package/tx/library.js +321 -22
- package/tx/openapi-operations.js +6 -4
- package/tx/operation-context.js +460 -99
- package/tx/params.js +25 -0
- package/tx/problems.js +3 -5
- package/tx/provider.js +314 -67
- package/tx/resource-cache-sqlite.js +1050 -0
- package/tx/sct/documentation.md +45 -21
- package/tx/sct/ecl.js +81 -17
- package/tx/sct/editions.js +39 -0
- package/tx/sct/expressions.js +10 -1
- package/tx/sct/load-config.js +107 -0
- package/tx/sct/mmap.js +109 -0
- package/tx/sct/structures.js +175 -15
- package/tx/sct/text-index.js +5 -1
- package/tx/syndication/syndication-feed.js +2 -2
- package/tx/tx-html.js +44 -8
- package/tx/tx.fhir.org.yml +8 -8
- package/tx/tx.js +282 -80
- package/tx/usage-tracker.js +39 -11
- package/tx/vs/vs-api.js +19 -0
- package/tx/vs/vs-database.js +386 -62
- package/tx/vs/vs-package.js +44 -17
- package/tx/vs/vs-vsac.js +649 -101
- package/tx/workers/batch-validate.js +37 -10
- package/tx/workers/batch.js +6 -1
- package/tx/workers/cache-control.js +15 -4
- package/tx/workers/closure.js +156 -13
- package/tx/workers/compare.js +8 -5
- package/tx/workers/expand.js +1173 -289
- package/tx/workers/metadata.js +35 -0
- package/tx/workers/search.js +53 -1
- package/tx/workers/supplement-filters.js +145 -0
- package/tx/workers/translate.js +169 -4
- package/tx/workers/validate.js +223 -119
- package/tx/workers/worker.js +123 -31
- package/tx/xversion/xv-valueset.js +7 -4
- package/utilities/submit-test-report.js +3 -1
- package/utilities/tx-stress-test.js +482 -0
- package/vcl/readme.md +37 -0
- package/vcl/vcl-parser.js +15 -0
- package/xig/readme.md +52 -0
- package/xig/xig-template.html +5 -8
- package/xig/xig.js +238 -422
- package/changelog-template.md +0 -22
- package/registry/test-runner.js +0 -237
- package/release.sh +0 -80
- package/test-scripts/repro-re2-wasm-leak.js +0 -34
- package/tsconfig.eslint.json +0 -9
- package/tx/html/dash-metrics.liquid +0 -147
- package/tx/html/home-metrics.liquid +0 -225
package/packages/readme.md
CHANGED
|
@@ -34,7 +34,10 @@ In summary:
|
|
|
34
34
|
| `GET /packages/{id}/{version}` | Download a package tarball; or, when the request's `Accept` prefers JSON (ranks it above `*/*`), npm's document for that version. A `FHIR-Package-Download` header always gets the tarball (the HL7 Java tools send it) |
|
|
35
35
|
| `GET /packages/updates` | Versions published since a date |
|
|
36
36
|
| `GET /packages/broken` | Package versions whose dependencies this registry doesn't hold |
|
|
37
|
+
| `GET /packages/-/npm/v1/keys` | The key this registry signs package versions with (see [Signatures](#signatures)) |
|
|
38
|
+
| `GET /packages/-/fhir/v1/signatures/{name}@{version}` | The publisher's signature of a version, as the crawler fetched it (see [Publishers' signatures](#publishers-signatures)) |
|
|
37
39
|
| `GET /packages/npm-check` | Check any registry against the npm registry protocol (a form, in a browser) |
|
|
40
|
+
| `GET /packages/feed-check` | Check a package feed the way the crawler reads it, storing nothing (a form, in a browser); the report is at `/packages/feed-check/{run}` |
|
|
38
41
|
|
|
39
42
|
Most endpoints return an HTML page instead of JSON when the request's `Accept`
|
|
40
43
|
header contains `text/html`. The JSON is the contract; the HTML is for people.
|
|
@@ -92,9 +95,16 @@ In the server config, under `modules.packages`:
|
|
|
92
95
|
| `crawlToken` | Shared secret for `POST /packages/crawl`. Omit it to disable that endpoint; the scheduled crawler is unaffected. |
|
|
93
96
|
| `crawler.enabled` | Run the crawler at startup and on the schedule. |
|
|
94
97
|
| `crawler.schedule` | Cron expression for crawls. |
|
|
98
|
+
| `crawler.signatures` | Publishers' package signatures: `ignore`, `check` (the default) or `require`. See [Publishers' signatures](#publishers-signatures). |
|
|
95
99
|
| `masterUrl` | The list of feeds to crawl. Defaults to `https://fhir.github.io/ig-registry/package-feeds.json`. |
|
|
96
100
|
| `localFeedDirs` | Optional. Directories feeds may be read from as local files (for testing). |
|
|
97
101
|
| `allowPrivateAddresses` | Optional, for testing only. Lets the crawler and `/npm-check` fetch from private and loopback addresses, which they otherwise refuse (SSRF protection). |
|
|
102
|
+
| `npmSigning.enabled` | Sign package versions (see [Signatures](#signatures)). On unless set to `false`. |
|
|
103
|
+
| `npmSigning.keyFile` | The signing key, an EC P-256 private key in PEM. A relative path is resolved against the database's folder. Default `npm-signing-key.pem`; made (readable only by the server's user) if it isn't there. |
|
|
104
|
+
| `feedCheck.enabled` | `/packages/feed-check`. On unless set to `false`. |
|
|
105
|
+
| `feedCheck.maxItems` | Items checked per run; the rest of the feed isn't read. Default 50. |
|
|
106
|
+
| `feedCheck.maxTotalMB` | Downloaded per run; items after that are listed but their packages aren't fetched. Default 500. |
|
|
107
|
+
| `feedCheck.keepMinutes` | How long a run's report is kept. Default 60. |
|
|
98
108
|
| `npmCheck.enabled` | `/packages/npm-check`. On unless set to `false`. |
|
|
99
109
|
| `npmCheck.maxConcurrentRuns` | Runs at once, across all users; more get a 503. Default 2. |
|
|
100
110
|
| `npmCheck.runsPerWindow` | Runs per client address per 10 minutes; more get a 429. Default 10. |
|
|
@@ -122,6 +132,56 @@ are used three ways:
|
|
|
122
132
|
`/packages/npm-check` also runs them against registry.npmjs.org once a day, and says on
|
|
123
133
|
the page if any fail there.
|
|
124
134
|
|
|
135
|
+
## Signatures
|
|
136
|
+
|
|
137
|
+
The server signs every package version it holds, the way the npm registry does, so
|
|
138
|
+
`npm audit signatures` can check packages installed from it. A signature says that this
|
|
139
|
+
registry served this tarball: it is the registry's statement, not the publisher's, so
|
|
140
|
+
it's made for every version, wherever it was crawled from. Each version in a package
|
|
141
|
+
document has `dist.integrity` (the sha512 of the tarball) and `dist.signatures`, the
|
|
142
|
+
ECDSA P-256 signature of `{id}@{version}:{integrity}`; `/packages/-/npm/v1/keys` lists
|
|
143
|
+
the public key.
|
|
144
|
+
|
|
145
|
+
* The key is in `npmSigning.keyFile`, next to the database unless set otherwise. If
|
|
146
|
+
there's no key there, one is made at startup. It is no better protected than the
|
|
147
|
+
database and the configuration: anyone who can change those can change what is
|
|
148
|
+
served anyway.
|
|
149
|
+
* A version is signed when it's stored. Versions stored before this (or signed with a
|
|
150
|
+
different key) are signed in the background at startup, a row at a time. Until every
|
|
151
|
+
version is signed, `/-/npm/v1/keys` is a 404, as if the server didn't sign: npm reports
|
|
152
|
+
an unsigned version from a registry with keys as an error. `/packages/status` shows the
|
|
153
|
+
progress (`npmSigning`). If some are left, it tries again after the next crawl.
|
|
154
|
+
* To change the key, replace the file and restart: everything is signed again with the
|
|
155
|
+
new key, and the keys endpoint is a 404 until that's done.
|
|
156
|
+
* With `npmSigning.enabled` false, versions still get `dist.integrity`, but there are no
|
|
157
|
+
signatures and no keys.
|
|
158
|
+
|
|
159
|
+
The signatures and integrity are in the `PackageDist` table, not in `PackageVersions`:
|
|
160
|
+
reading a column stored after the tarball means reading the tarball.
|
|
161
|
+
|
|
162
|
+
## Checking a feed
|
|
163
|
+
|
|
164
|
+
`/packages/feed-check` takes a feed's URL and reads it the way the crawler does, without storing
|
|
165
|
+
anything: no packages, signatures or visited pages go into the database, and nothing is written
|
|
166
|
+
to the mirror. For each item it reports the package (downloaded, unpacked with the crawler's
|
|
167
|
+
limits and checked as the crawler checks it before storing: id, semver version, canonical, no
|
|
168
|
+
install scripts, no JavaScript unless it's a template, not notForPublication), whether the
|
|
169
|
+
registry's `package-restrictions` allow it from this feed, its signature (`confirmed`,
|
|
170
|
+
`rejected`, `absent`, or `not checked` when the feed isn't in `package-feeds.json` or lists no
|
|
171
|
+
keys), and whether this registry already holds the version, and if so whether it is the same
|
|
172
|
+
bytes. A feed-level problem (it can't be fetched, isn't RSS, or the server rate limits) is
|
|
173
|
+
reported instead. Next pages are followed up to `feedCheck.maxItems` items.
|
|
174
|
+
|
|
175
|
+
The signing keys can be given with the check, as JSON: the feed's `signing` block as it would be
|
|
176
|
+
in `package-feeds.json` (or the feed's whole entry). Signatures are then checked against those
|
|
177
|
+
keys instead of the registry's, so a publisher can test a feed and its keys before they are
|
|
178
|
+
registered.
|
|
179
|
+
|
|
180
|
+
A check runs in the background: the form redirects to `/packages/feed-check/{run}`, which reloads
|
|
181
|
+
every few seconds until the check is done. Every request goes through the crawler's SSRF guard,
|
|
182
|
+
and only http(s) URLs are fetched, so a feed can't make the server read local files. Only one
|
|
183
|
+
check runs at a time, across all users; a request while one is running gets a 503.
|
|
184
|
+
|
|
125
185
|
## Crawling
|
|
126
186
|
|
|
127
187
|
Each crawl reads the master feed list, then each RSS feed in it (Simplifier last).
|
|
@@ -148,10 +208,62 @@ stores it.
|
|
|
148
208
|
|
|
149
209
|
`/packages/log` shows what the last crawl did, feed by feed and item by item.
|
|
150
210
|
|
|
211
|
+
### Publishers' signatures
|
|
212
|
+
|
|
213
|
+
A publisher can sign its packages: the IG Publisher does, when its `fhir-settings.json` has a
|
|
214
|
+
`packageSigningKey` (see `org.hl7.fhir.utilities.npm.NpmPackageSigner` in core). Each signed
|
|
215
|
+
package has a partner file, `{package}.tgz.sigstore.json`, and the feed item links it in a
|
|
216
|
+
`fhir:signature` element. The file is a Sigstore bundle: an ECDSA P-256 signature of a statement
|
|
217
|
+
naming the package (`pkg:npm/{id}@{version}`) and the sha512 of the `.tgz`.
|
|
218
|
+
|
|
219
|
+
Which keys may sign a feed's packages is in the master list, `package-feeds.json`, on the feed:
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
"signing": {
|
|
223
|
+
"required": false,
|
|
224
|
+
"keys": [{ "keyid": "SHA256:...", "key": "{base64 public key}", "expires": null }]
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
`crawler.signatures` says what the crawler does with them, for feeds that list keys:
|
|
229
|
+
|
|
230
|
+
* `ignore`: nothing.
|
|
231
|
+
* `check` (the default): each package's signature is fetched and checked. One that fails, or
|
|
232
|
+
can't be fetched, is logged (in the server log and `/packages/log`), and the package is stored
|
|
233
|
+
anyway, unless the feed has `"required": true`, when it is refused.
|
|
234
|
+
* `require`: as `check`, but an unsigned package, or one whose signature fails, is refused,
|
|
235
|
+
and feeds that list no keys (Simplifier, for one) aren't read at all - the crawl log says so.
|
|
236
|
+
|
|
237
|
+
With `ignore` or `check`, feeds that list no keys are read as before, and their packages aren't
|
|
238
|
+
checked. A key that had expired when a
|
|
239
|
+
package was published doesn't count. A package that was stored before it was signed (or before
|
|
240
|
+
the server checked) has its signature checked against the bytes it holds, once, the next time
|
|
241
|
+
the crawler sees it in a feed; it is never refused, since it is already held, but a failure is
|
|
242
|
+
logged and recorded.
|
|
243
|
+
|
|
244
|
+
The signature file is kept in the `PackageSignatures` table as it was when it was checked, valid
|
|
245
|
+
or not, with the outcome, the key that signed it and when it was checked. The HTML lists of
|
|
246
|
+
packages (search, a package's versions, updates) show each version's signature as `confirmed`,
|
|
247
|
+
`rejected` or `absent` (unsigned, or not checked); hover for the key, or the reason.
|
|
248
|
+
|
|
249
|
+
In the package document, each version with a signature has `dist.fhir-signature`: `url` (the
|
|
250
|
+
signature file, served at `/packages/-/fhir/v1/signatures/{name}@{version}` exactly as it was
|
|
251
|
+
fetched), `status` (`confirmed` or `rejected`) and, when confirmed, the `keyid`. A client can
|
|
252
|
+
check the signature itself, against the keys in `package-feeds.json`.
|
|
253
|
+
|
|
254
|
+
It is deliberately not `dist.attestations`. npm (`npm audit signatures`) verifies attestations
|
|
255
|
+
as Sigstore attestations: it wants the signing key listed among the registry's own keys
|
|
256
|
+
(`/-/npm/v1/keys`), and a transparency log (Rekor) entry in the bundle. Publishers' bundles have
|
|
257
|
+
no log entry, and listing a publisher's key with the registry's would let that publisher forge
|
|
258
|
+
the registry's signatures, since npm accepts a registry signature from any listed key. So every
|
|
259
|
+
signed package would fail `npm audit signatures`.
|
|
260
|
+
|
|
151
261
|
## Storage
|
|
152
262
|
|
|
153
263
|
Everything is in the SQLite database. `PackageVersions` holds each version's
|
|
154
|
-
metadata and the tarball itself. `
|
|
264
|
+
metadata and the tarball itself. `PackageDist` holds each version's `dist.integrity`
|
|
265
|
+
and npm signature. `PackageSignatures` holds the publisher's signature of each version, as
|
|
266
|
+
the crawler checked it. `Packages` holds one row per package id, with its
|
|
155
267
|
current version and download count. `PackageDependencies` (stored as `id@version`),
|
|
156
268
|
`PackageFHIRVersions` and `PackageURLs` (the canonical URLs of the resources in each
|
|
157
269
|
version) support search.
|
|
@@ -85,8 +85,6 @@
|
|
|
85
85
|
<div id="stripe"> </div>
|
|
86
86
|
<div class="container"> <!-- container -->
|
|
87
87
|
<div style="background-color: #ad1f2f; padding: 6px; color: white;"> <!-- container -->
|
|
88
|
-
<a href="http://www.hl7.org/fhir" style="color: gold" title="Fast Healthcare Interoperability Resources - Home Page"><img border="0" src="/icon-fhir-16.png" style="vertical-align: text-bottom"/> <b>FHIR</b></a> © HL7.org |
|
|
89
|
-
<a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" style="color: gold"><img border="0" src="/FHIRsmith16.png" style="vertical-align: text-bottom"/> FHIRsmith</a> [%ver%] |
|
|
90
88
|
<a href="/" style="color: gold"> Server Home</a> |
|
|
91
89
|
<a href="/publisher" style="color: gold">Publisher Home</a> |
|
|
92
90
|
<a href="/publisher/tasks" style="color: gold">Tasks</a> |
|
|
@@ -106,7 +104,7 @@
|
|
|
106
104
|
<div class="inner-wrapper">
|
|
107
105
|
<div class="col-9">
|
|
108
106
|
|
|
109
|
-
<h2><img border="0" src="
|
|
107
|
+
<h2><a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" title="FHIRsmith [%ver%]"><img border="0" src="/FHIRsmith32.png" alt="FHIRsmith Logo" style="vertical-align: text-bottom"/></a> [%title%] </h2>
|
|
110
108
|
|
|
111
109
|
[%content%]
|
|
112
110
|
|
|
@@ -121,8 +119,7 @@
|
|
|
121
119
|
<div class="container"> <!-- container -->
|
|
122
120
|
<div class="inner-wrapper">
|
|
123
121
|
<p>
|
|
124
|
-
<a href="
|
|
125
|
-
<a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" style="color: gold"><img border="0" src="/FHIRsmith16.png" style="vertical-align: text-bottom"/> FHIRsmith</a> [%ver%] © HealthIntersections.com.au 2023+ |
|
|
122
|
+
<a href="https://github.com/HealthIntersections/FHIRsmith/blob/main/README.md" style="color: gold"><img border="0" src="/FHIRsmith16.png" alt="FHIRsmith Logo" style="vertical-align: text-bottom"/> FHIRsmith</a> [%ver%] © HealthIntersections.com.au 2023+ |
|
|
126
123
|
([%ms%] ms)
|
|
127
124
|
[%sponsorMessage%]
|
|
128
125
|
</p>
|