@_linked/cli 1.8.1 → 1.9.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.
Files changed (207) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/defaults/setup-publish/dual-branch/github/workflows/publish.yml +3 -0
  3. package/defaults/setup-publish/github/workflows/publish.yml +3 -0
  4. package/defaults/shape.ts +8 -2
  5. package/jest.config.cjs +8 -4
  6. package/lib/esm/cli-methods.d.ts +11 -3
  7. package/lib/esm/cli-methods.d.ts.map +1 -1
  8. package/lib/esm/cli-methods.js +51 -105
  9. package/lib/esm/cli-methods.js.map +1 -1
  10. package/lib/esm/cli.js +32 -8
  11. package/lib/esm/cli.js.map +1 -1
  12. package/lib/esm/commands/start.d.ts +6 -0
  13. package/lib/esm/commands/start.d.ts.map +1 -0
  14. package/lib/esm/commands/start.js +311 -0
  15. package/lib/esm/commands/start.js.map +1 -0
  16. package/lib/esm/config-webpack-app.d.ts +1 -1
  17. package/lib/esm/config-webpack-app.d.ts.map +1 -1
  18. package/lib/esm/config-webpack-app.js +1 -1
  19. package/lib/esm/config-webpack-app.js.map +1 -1
  20. package/lib/esm/config-webpack.d.ts +1 -1
  21. package/lib/esm/config-webpack.d.ts.map +1 -1
  22. package/lib/esm/config-webpack.js +4 -4
  23. package/lib/esm/config-webpack.js.map +1 -1
  24. package/lib/esm/defineConfig.d.ts +1 -1
  25. package/lib/esm/defineConfig.d.ts.map +1 -1
  26. package/lib/esm/index.d.ts +1 -1
  27. package/lib/esm/index.d.ts.map +1 -1
  28. package/lib/esm/index.js +1 -1
  29. package/lib/esm/index.js.map +1 -1
  30. package/lib/esm/interfaces.d.ts +9 -0
  31. package/lib/esm/interfaces.d.ts.map +1 -1
  32. package/lib/esm/lifecycle.d.ts +24 -0
  33. package/lib/esm/lifecycle.d.ts.map +1 -0
  34. package/lib/esm/lifecycle.js +156 -0
  35. package/lib/esm/lifecycle.js.map +1 -0
  36. package/lib/esm/loaders/ts-loader.d.mts.map +1 -1
  37. package/lib/esm/loaders/ts-loader.mjs +11 -2
  38. package/lib/esm/loaders/ts-loader.mjs.map +1 -1
  39. package/lib/esm/utils.d.ts +1 -1
  40. package/lib/esm/utils.d.ts.map +1 -1
  41. package/lib/esm/utils.js +9 -3
  42. package/lib/esm/utils.js.map +1 -1
  43. package/lib/esm/vite-config.d.ts +20 -0
  44. package/lib/esm/vite-config.d.ts.map +1 -0
  45. package/lib/esm/vite-config.js +410 -0
  46. package/lib/esm/vite-config.js.map +1 -0
  47. package/package.json +13 -14
  48. package/test-results/.last-run.json +6 -0
  49. package/test-results/create-app-Person-fixture-add-/342/206/222-edit-/342/206/222-delete-round-trip/error-context.md +16 -0
  50. package/test-results/create-app-Person-fixture-add-/342/206/222-edit-/342/206/222-delete-round-trip/trace.zip +0 -0
  51. package/tests/unit/safeYarn.test.ts +1 -1
  52. package/tsconfig-esm.json +1 -1
  53. package/defaults/app-with-backend/.env-cmdrc.json +0 -19
  54. package/defaults/app-with-backend/.eslintignore +0 -5
  55. package/defaults/app-with-backend/.eslintrc.json +0 -40
  56. package/defaults/app-with-backend/.prettierignore +0 -4
  57. package/defaults/app-with-backend/.prettierrc.json +0 -27
  58. package/defaults/app-with-backend/.vscode/launch.json +0 -12
  59. package/defaults/app-with-backend/.yarn/plugins/@yarnpkg/plugin-version.cjs +0 -550
  60. package/defaults/app-with-backend/.yarn/releases/yarn-3.6.1.cjs +0 -874
  61. package/defaults/app-with-backend/gitignore.template +0 -25
  62. package/defaults/app-with-backend/linked.backend.datasets.example.json +0 -17
  63. package/defaults/app-with-backend/linked.backend.storage.ts +0 -35
  64. package/defaults/app-with-backend/linked.config.js +0 -5
  65. package/defaults/app-with-backend/package.json +0 -70
  66. package/defaults/app-with-backend/pm2-staging.config.js +0 -12
  67. package/defaults/app-with-backend/pm2.config.js +0 -12
  68. package/defaults/app-with-backend/public/favicon-144x144.png +0 -0
  69. package/defaults/app-with-backend/public/favicon-57x57.png +0 -0
  70. package/defaults/app-with-backend/public/favicon-72x72.png +0 -0
  71. package/defaults/app-with-backend/public/favicon.ico +0 -0
  72. package/defaults/app-with-backend/readme.md +0 -93
  73. package/defaults/app-with-backend/src/App.module.css +0 -83
  74. package/defaults/app-with-backend/src/App.tsx +0 -30
  75. package/defaults/app-with-backend/src/backend.ts +0 -9
  76. package/defaults/app-with-backend/src/components/Error.module.css +0 -9
  77. package/defaults/app-with-backend/src/components/Error.tsx +0 -11
  78. package/defaults/app-with-backend/src/components/PersonOverview.module.css +0 -100
  79. package/defaults/app-with-backend/src/components/PersonOverview.tsx +0 -97
  80. package/defaults/app-with-backend/src/components/PersonOverviewContext.tsx +0 -12
  81. package/defaults/app-with-backend/src/components/PersonPreview.module.css +0 -97
  82. package/defaults/app-with-backend/src/components/PersonPreview.tsx +0 -109
  83. package/defaults/app-with-backend/src/components/Spinner.module.css +0 -17
  84. package/defaults/app-with-backend/src/components/Spinner.tsx +0 -12
  85. package/defaults/app-with-backend/src/index.tsx +0 -34
  86. package/defaults/app-with-backend/src/layout/DefaultLayout.module.css +0 -15
  87. package/defaults/app-with-backend/src/layout/DefaultLayout.tsx +0 -12
  88. package/defaults/app-with-backend/src/layout/Header.module.css +0 -46
  89. package/defaults/app-with-backend/src/layout/Header.tsx +0 -27
  90. package/defaults/app-with-backend/src/linked.frontend.datasets.json +0 -12
  91. package/defaults/app-with-backend/src/linked.frontend.storage.ts +0 -36
  92. package/defaults/app-with-backend/src/package.ts +0 -16
  93. package/defaults/app-with-backend/src/pages/Home.module.css +0 -23
  94. package/defaults/app-with-backend/src/pages/Home.tsx +0 -14
  95. package/defaults/app-with-backend/src/pages/Page1.module.css +0 -111
  96. package/defaults/app-with-backend/src/pages/Page1.tsx +0 -196
  97. package/defaults/app-with-backend/src/pages/PageNotFound.module.css +0 -19
  98. package/defaults/app-with-backend/src/pages/PageNotFound.tsx +0 -14
  99. package/defaults/app-with-backend/src/pages/Signin.module.css +0 -20
  100. package/defaults/app-with-backend/src/pages/Signin.tsx +0 -16
  101. package/defaults/app-with-backend/src/routes.tsx +0 -78
  102. package/defaults/app-with-backend/src/theme.css +0 -91
  103. package/defaults/app-with-backend/src/types.ts +0 -14
  104. package/defaults/app-with-backend/src/utils/lazyWithPreload.tsx +0 -42
  105. package/defaults/app-with-backend/src/utils/preloadRoutes.ts +0 -38
  106. package/defaults/app-with-backend/tsconfig.json +0 -24
  107. package/defaults/app-with-backend/yarnrc.yml.template +0 -8
  108. package/lib/cjs/cli-methods.d.ts +0 -67
  109. package/lib/cjs/cli-methods.d.ts.map +0 -1
  110. package/lib/cjs/cli-methods.js +0 -2718
  111. package/lib/cjs/cli-methods.js.map +0 -1
  112. package/lib/cjs/cli.d.ts +0 -3
  113. package/lib/cjs/cli.d.ts.map +0 -1
  114. package/lib/cjs/cli.js +0 -315
  115. package/lib/cjs/cli.js.map +0 -1
  116. package/lib/cjs/commands/build-package.d.ts +0 -9
  117. package/lib/cjs/commands/build-package.d.ts.map +0 -1
  118. package/lib/cjs/commands/build-package.js +0 -55
  119. package/lib/cjs/commands/build-package.js.map +0 -1
  120. package/lib/cjs/commands/safe-yarn.d.ts +0 -10
  121. package/lib/cjs/commands/safe-yarn.d.ts.map +0 -1
  122. package/lib/cjs/commands/safe-yarn.js +0 -66
  123. package/lib/cjs/commands/safe-yarn.js.map +0 -1
  124. package/lib/cjs/commands/setup-publish.d.ts +0 -23
  125. package/lib/cjs/commands/setup-publish.d.ts.map +0 -1
  126. package/lib/cjs/commands/setup-publish.js +0 -369
  127. package/lib/cjs/commands/setup-publish.js.map +0 -1
  128. package/lib/cjs/config-webpack-app.d.ts +0 -112
  129. package/lib/cjs/config-webpack-app.d.ts.map +0 -1
  130. package/lib/cjs/config-webpack-app.js +0 -427
  131. package/lib/cjs/config-webpack-app.js.map +0 -1
  132. package/lib/cjs/config-webpack.d.ts +0 -55
  133. package/lib/cjs/config-webpack.d.ts.map +0 -1
  134. package/lib/cjs/config-webpack.js +0 -417
  135. package/lib/cjs/config-webpack.js.map +0 -1
  136. package/lib/cjs/defineConfig.d.ts +0 -8
  137. package/lib/cjs/defineConfig.d.ts.map +0 -1
  138. package/lib/cjs/defineConfig.js +0 -12
  139. package/lib/cjs/defineConfig.js.map +0 -1
  140. package/lib/cjs/index.d.ts +0 -12
  141. package/lib/cjs/index.d.ts.map +0 -1
  142. package/lib/cjs/index.js +0 -43
  143. package/lib/cjs/index.js.map +0 -1
  144. package/lib/cjs/interfaces.d.ts +0 -131
  145. package/lib/cjs/interfaces.d.ts.map +0 -1
  146. package/lib/cjs/interfaces.js +0 -3
  147. package/lib/cjs/interfaces.js.map +0 -1
  148. package/lib/cjs/loaders/css-loader.d.mts +0 -2
  149. package/lib/cjs/loaders/css-loader.d.mts.map +0 -1
  150. package/lib/cjs/loaders/css-loader.mjs +0 -86
  151. package/lib/cjs/loaders/css-loader.mjs.map +0 -1
  152. package/lib/cjs/loaders/register-css-only.d.ts +0 -2
  153. package/lib/cjs/loaders/register-css-only.d.ts.map +0 -1
  154. package/lib/cjs/loaders/register-css-only.js +0 -11
  155. package/lib/cjs/loaders/register-css-only.js.map +0 -1
  156. package/lib/cjs/loaders/register.d.ts +0 -2
  157. package/lib/cjs/loaders/register.d.ts.map +0 -1
  158. package/lib/cjs/loaders/register.js +0 -11
  159. package/lib/cjs/loaders/register.js.map +0 -1
  160. package/lib/cjs/loaders/ts-loader.d.mts +0 -3
  161. package/lib/cjs/loaders/ts-loader.d.mts.map +0 -1
  162. package/lib/cjs/loaders/ts-loader.mjs +0 -116
  163. package/lib/cjs/loaders/ts-loader.mjs.map +0 -1
  164. package/lib/cjs/metadata.d.ts +0 -1
  165. package/lib/cjs/metadata.d.ts.map +0 -1
  166. package/lib/cjs/metadata.js +0 -155
  167. package/lib/cjs/metadata.js.map +0 -1
  168. package/lib/cjs/package.json +0 -139
  169. package/lib/cjs/plugins/check-imports.d.ts +0 -2
  170. package/lib/cjs/plugins/check-imports.d.ts.map +0 -1
  171. package/lib/cjs/plugins/check-imports.js +0 -68
  172. package/lib/cjs/plugins/check-imports.js.map +0 -1
  173. package/lib/cjs/plugins/declaration-plugin.d.ts +0 -15
  174. package/lib/cjs/plugins/declaration-plugin.d.ts.map +0 -1
  175. package/lib/cjs/plugins/declaration-plugin.js +0 -285
  176. package/lib/cjs/plugins/declaration-plugin.js.map +0 -1
  177. package/lib/cjs/plugins/externalise-modules.d.ts +0 -7
  178. package/lib/cjs/plugins/externalise-modules.d.ts.map +0 -1
  179. package/lib/cjs/plugins/externalise-modules.js +0 -190
  180. package/lib/cjs/plugins/externalise-modules.js.map +0 -1
  181. package/lib/cjs/plugins/lincd-tailwind-sources.d.ts +0 -3
  182. package/lib/cjs/plugins/lincd-tailwind-sources.d.ts.map +0 -1
  183. package/lib/cjs/plugins/lincd-tailwind-sources.js +0 -32
  184. package/lib/cjs/plugins/lincd-tailwind-sources.js.map +0 -1
  185. package/lib/cjs/plugins/watch-run.d.ts +0 -5
  186. package/lib/cjs/plugins/watch-run.d.ts.map +0 -1
  187. package/lib/cjs/plugins/watch-run.js +0 -45
  188. package/lib/cjs/plugins/watch-run.js.map +0 -1
  189. package/lib/cjs/preflight.css +0 -137
  190. package/lib/cjs/tailwind.config.d.ts +0 -8
  191. package/lib/cjs/tailwind.config.d.ts.map +0 -1
  192. package/lib/cjs/tailwind.config.js +0 -17
  193. package/lib/cjs/tailwind.config.js.map +0 -1
  194. package/lib/cjs/utils.d.ts +0 -77
  195. package/lib/cjs/utils.d.ts.map +0 -1
  196. package/lib/cjs/utils.js +0 -585
  197. package/lib/cjs/utils.js.map +0 -1
  198. package/lib/esm/loaders/register-css-only.d.ts +0 -2
  199. package/lib/esm/loaders/register-css-only.d.ts.map +0 -1
  200. package/lib/esm/loaders/register-css-only.js +0 -9
  201. package/lib/esm/loaders/register-css-only.js.map +0 -1
  202. package/lib/esm/loaders/register.d.ts +0 -2
  203. package/lib/esm/loaders/register.d.ts.map +0 -1
  204. package/lib/esm/loaders/register.js +0 -9
  205. package/lib/esm/loaders/register.js.map +0 -1
  206. package/lib/esm/package.json +0 -139
  207. /package/{babel.config.js → babel.config.cjs} +0 -0
@@ -1,25 +0,0 @@
1
- node_modules
2
- build
3
- mrgit.json
4
- public/bundles
5
- data/uploads
6
- data/metadata
7
- data/sessions
8
- linked.backend.datasets.json
9
- .yarn/*
10
- !.yarn/patches
11
- !.yarn/plugins
12
- !.yarn/releases
13
- !.yarn/sdks
14
- !.yarn/versions
15
- packages/**/dist
16
- packages/**/lib
17
- packages/**/.yarn/*
18
- !packages/**/.yarn/patches
19
- !packages/**/.yarn/plugins
20
- !packages/**/.yarn/releases
21
- !packages/**/.yarn/sdks
22
- !packages/**/.yarn/versions
23
- .idea
24
- android/app/build
25
- ios/App/App/public
@@ -1,17 +0,0 @@
1
- {
2
- "_note": "Backend datasets config. Copy this file to linked.backend.datasets.json (gitignored) and replace placeholders with literal values, OR set the env vars referenced via ${VAR}. The ${VAR:-default} form falls back to the literal after :- when the env var is unset. Each entry has two keys: `store` (an npm import path string identifying the IDataset class) and `config` (a single JSON object passed verbatim to `new StoreClass(config)` — its fields are whatever that class's constructor expects).",
3
-
4
- "datasets": {
5
- "appData": {
6
- "store": "@_linked/fuseki/shapes/FusekiStore",
7
- "config": {
8
- "endpoint": "${FUSEKI_BASE_URL:-http://localhost:3030}/${FUSEKI_DATASET:-${hyphen_name}-main}",
9
- "credentials": {
10
- "type": "basic",
11
- "username": "${FUSEKI_USER:-admin}",
12
- "password": "${FUSEKI_PASSWORD:-admin}"
13
- }
14
- }
15
- }
16
- }
17
- }
@@ -1,35 +0,0 @@
1
- // Backend storage for ${name}.
2
- //
3
- // Reads linked.backend.datasets.json, dynamically imports each alias's
4
- // store class via its npm path, and instantiates with the entry's `config`
5
- // verbatim. See the mirror at src/linked.frontend.storage.ts.
6
- import datasetsConfig from './linked.backend.datasets.json' with { type: 'json' };
7
- import { parseDatasetsConfig } from '@_linked/core/utils/parseDatasetsConfig';
8
- import { loadStores } from '@_linked/core/utils/loadStores';
9
- import { LinkedStorage } from '@_linked/core/utils/LinkedStorage';
10
- import { LinkedFileStorage } from '@_linked/core/utils/LinkedFileStorage';
11
- import type { FusekiStore } from '@_linked/fuseki/shapes/FusekiStore';
12
- import { LocalFileStore } from '@_linked/server/shapes/filestores/LocalFileStore';
13
-
14
- // Resolve ${VAR} placeholders against the runtime environment, then
15
- // instantiate each store class declared in the JSON.
16
- const config = parseDatasetsConfig(datasetsConfig, process.env);
17
- const stores = await loadStores(config);
18
-
19
- // Auto-create the dataset on first boot.
20
- const appData = stores.appData as FusekiStore;
21
- appData.ensureDatasetExists().catch((err) =>
22
- console.warn('dataset ensure failed:', err),
23
- );
24
-
25
- // Shape → alias. Single alias → default for every shape. For multi-alias
26
- // add per-shape pins, e.g.:
27
- // import { Person } from '@_linked/schema/shapes/Person';
28
- // LinkedStorage.setDatasetForShapes(stores.appData, [Person]);
29
- LinkedStorage.setDefaultDataset(appData);
30
-
31
- // File storage (binary assets). Separate from RDF dataset routing.
32
- const fileStore = new LocalFileStore(
33
- (process.env.NODE_ENV || 'development') + '-files',
34
- );
35
- LinkedFileStorage.setDefaultDataset(fileStore);
@@ -1,5 +0,0 @@
1
- export default {
2
- cssMode: 'tailwind',
3
- cacheWebpack: true,
4
- analyse: false,
5
- };
@@ -1,70 +0,0 @@
1
- {
2
- "name": "${hyphen_name}",
3
- "displayName": "${name}",
4
- "description": "",
5
- "version": "0.1.0",
6
- "private": true,
7
- "type": "module",
8
- "linkedApp": true,
9
- "author": {
10
- "name": "",
11
- "url": "",
12
- "email": ""
13
- },
14
- "engines": {
15
- "node": ">=20.6.0"
16
- },
17
- "main": "scripts/start-server.js",
18
- "license": "MIT",
19
- "scripts": {
20
- "start": "linked start --env development",
21
- "build": "linked build-app --env production",
22
- "server:prod": "linked start --env production",
23
- "lint": "eslint \"src/**/*.{js,jsx,ts,tsx}\"",
24
- "lint:fix": "npm run lint -- --fix",
25
- "prettier": "prettier \"src/**/*.{js,jsx,ts,tsx,css,scss}\" --check",
26
- "prettier:fix": "npm run prettier -- --write",
27
- "format": "npm run prettier:fix && npm run lint:fix"
28
- },
29
- "keywords": [
30
- "linked",
31
- "linked code",
32
- "linked data",
33
- "structured data",
34
- "RDF",
35
- "semantic web",
36
- "web3"
37
- ],
38
- "dependencies": {
39
- "@_linked/core": "^2.6",
40
- "@_linked/server": "^2.0",
41
- "@_linked/server-utils": "^1.0.6",
42
- "@_linked/schema": "^1.0.6",
43
- "@_linked/fuseki": "^2.0.1",
44
- "@_linked/primitives": "^1.0.6",
45
- "@_linked/react": "^1.3",
46
- "react": "^18.2",
47
- "react-dom": "^18.2",
48
- "react-error-boundary": "^3.1.3",
49
- "react-router-dom": "^6.3.0"
50
- },
51
- "devDependencies": {
52
- "@_linked/cli": "^1.4",
53
- "@types/node": "^20.12.7",
54
- "@types/react": "^18.0.17",
55
- "@types/react-dom": "^18.0.6",
56
- "@typescript-eslint/eslint-plugin": "latest",
57
- "@typescript-eslint/parser": "latest",
58
- "env-cmd": "^10.1.0",
59
- "eslint": "latest",
60
- "eslint-config-prettier": "^8.9.0",
61
- "eslint-import-resolver-typescript": "^3.5.5",
62
- "eslint-plugin-prettier": "^5.0.0",
63
- "eslint-plugin-react": "latest",
64
- "eslint-plugin-react-hooks": "latest",
65
- "nodemon": "^2.0.22",
66
- "pm2": "^5.3.0",
67
- "typescript-plugin-css-modules": "^5.1.0",
68
- "which": "^3.0.1"
69
- }
70
- }
@@ -1,12 +0,0 @@
1
- module.exports = {
2
- apps: [
3
- {
4
- name: '${hyphen_name}-staging',
5
- script: 'yarn run server:staging',
6
- time: true,
7
- log_date_format: 'DD-MM-YYYY HH:mm Z',
8
- out_file: './data/out.log',
9
- error_file: './data/error.log',
10
- },
11
- ],
12
- };
@@ -1,12 +0,0 @@
1
- module.exports = {
2
- apps: [
3
- {
4
- name: '${hyphen_name}',
5
- script: 'yarn run server:prod',
6
- time: true,
7
- log_date_format: 'DD-MM-YYYY HH:mm Z',
8
- out_file: "./data/out.log",
9
- error_file: "./data/error.log",
10
- }
11
- ],
12
- };
@@ -1,93 +0,0 @@
1
- # ${name}
2
-
3
- A standalone [Linked](https://linked.cm) app generated with `@_linked/cli`. Requires **Node ≥ 20.6**.
4
-
5
- ## What is Linked
6
-
7
- Linked is a TypeScript framework for building apps on top of a graph database (RDF / SPARQL). You write your domain as **shape classes**, query them with a **typed DSL**, and render with **linked React components** that subscribe to the query results.
8
-
9
- ```ts
10
- // 1. A shape — domain class + SHACL metadata, generated from TypeScript decorators
11
- @linkedShape
12
- export class Person extends Shape {
13
- static targetClass = schema.Person;
14
-
15
- @literalProperty({ path: schema.givenName }) givenName: string;
16
- @literalProperty({ path: schema.familyName }) familyName: string;
17
- }
18
-
19
- // 2. A query — TypeScript-embedded, type-inferred, executed against any IDataset
20
- const people = await Person.select((p) => [p.givenName, p.familyName]);
21
-
22
- // 3. A linked component — re-renders when its query result changes
23
- const PersonCard = linkedComponent(
24
- Person.select((p) => [p.givenName, p.familyName]),
25
- ({ givenName, familyName }) => <span>{givenName} {familyName}</span>,
26
- );
27
- ```
28
-
29
- The home page in this app demonstrates the full chain end-to-end with `@_linked/schema`'s shipped `Person` shape.
30
-
31
- - **[@_linked/core](https://github.com/linked-cm/core)** — query DSL, Shape classes, storage routing.
32
- - **[@_linked/react](https://github.com/Semantu/linked-react)** — `linkedComponent`, `linkedSetComponent`.
33
-
34
- ## Install + run
35
-
36
- With npm:
37
-
38
- ```bash
39
- npm install
40
- npm start
41
- ```
42
-
43
- Or with yarn:
44
-
45
- ```bash
46
- yarn install
47
- yarn start
48
- ```
49
-
50
- The home page demonstrates the `@_linked` query DSL with a small Person CRUD — see `src/components/PersonOverview.tsx` and `src/components/PersonPreview.tsx`.
51
-
52
- > If you scaffolded this app **inside an existing Yarn-workspace monorepo** (e.g. `packages/` of another repo), use `yarn install` — `npm install` walks up to the workspace root and may hit peer-dep conflicts there. Outside a workspace, either tool works.
53
-
54
- ## Storage
55
-
56
- This app talks to an Apache Jena Fuseki SPARQL endpoint. Start a local Fuseki container:
57
-
58
- ```bash
59
- docker run -d --rm -p 3030:3030 --name fuseki stain/jena-fuseki
60
- ```
61
-
62
- The app auto-creates its dataset on first boot.
63
-
64
- **Storage config lives in three files**, per the spec in [`@_linked/cli` docs](https://www.npmjs.com/package/@_linked/cli):
65
-
66
- | File | Purpose |
67
- |---|---|
68
- | `linked.backend.datasets.json` | Backend Layer 2 — alias → store class + config. Gitignored. Copy of `linked.backend.datasets.example.json`. |
69
- | `linked.backend.storage.ts` | Backend Layer 1 — shape → alias routing. Parses the JSON, instantiates stores via `loadStores`, calls `LinkedStorage.setDefaultDataset(...)` / `setDatasetForShapes(...)`. |
70
- | `src/linked.frontend.{storage.ts,datasets.json}` | Frontend mirror — same JSON shape, public values only; storage TS imports store classes explicitly and constructs one per alias. |
71
-
72
- Defaults shipped:
73
-
74
- - `FUSEKI_BASE_URL` — `http://localhost:3030`
75
- - `FUSEKI_DATASET` — `${hyphen_name}-main`
76
- - `FUSEKI_USER` / `FUSEKI_PASSWORD` — `admin` / `admin` (matches the `stain/jena-fuseki` Docker image default)
77
- - `FUSEKI_DB_TYPE` — `tdb2` (persistent). Set to `mem` for in-memory.
78
-
79
- All of these are referenced as `${VAR:-default}` placeholders in `linked.backend.datasets.json`, so you can either edit the JSON directly or set env vars.
80
-
81
- ## Build for production
82
-
83
- ```bash
84
- npm run build
85
- npm run server:prod
86
- ```
87
-
88
- ## Learn more
89
-
90
- - [@_linked/core](https://www.npmjs.com/package/@_linked/core) — query DSL, Shape classes, storage routing. See `parseDatasetsConfig` + `loadStores` for the dataset-config helpers.
91
- - [@_linked/react](https://www.npmjs.com/package/@_linked/react) — `linkedComponent`, `linkedSetComponent`.
92
- - [@_linked/schema](https://www.npmjs.com/package/@_linked/schema) — shipped Shape classes (Person, Place, Organization, …).
93
- - [@_linked/css](https://www.npmjs.com/package/@_linked/css) — design tokens, Tailwind v4 theme primitives, `@utility` mixins. See `src/theme.css` for the wiring.
@@ -1,83 +0,0 @@
1
- @import '@_linked/css/package.css';
2
-
3
- :global(body) {
4
- font-family: var(--font-sans);
5
- background: var(--bg-page);
6
- color: var(--color-gray-900);
7
- margin: 0;
8
- overflow-x: hidden;
9
- }
10
-
11
- /* Animated background: soft radial-gradient "blobs" drifting and pulsing
12
- underneath the content. Brand-colored via @_linked/css tokens — recolors
13
- automatically when --color-primary-* / --color-secondary-* change in theme.css. */
14
- :global(body)::before {
15
- content: '';
16
- position: fixed;
17
- inset: 0;
18
- z-index: -1;
19
- pointer-events: none;
20
- background:
21
- radial-gradient(
22
- circle at 18% 22%,
23
- var(--color-primary-300) 0%,
24
- transparent 38%
25
- ),
26
- radial-gradient(
27
- circle at 82% 76%,
28
- var(--color-secondary-300) 0%,
29
- transparent 42%
30
- ),
31
- radial-gradient(
32
- circle at 55% 58%,
33
- var(--color-tertiary-200) 0%,
34
- transparent 50%
35
- );
36
- opacity: 0.35;
37
- filter: blur(40px);
38
- animation: drift 22s ease-in-out infinite;
39
- }
40
-
41
- @keyframes drift {
42
- 0%,
43
- 100% {
44
- transform: translate3d(0, 0, 0) scale(1);
45
- }
46
- 33% {
47
- transform: translate3d(-3%, 2%, 0) scale(1.05);
48
- }
49
- 66% {
50
- transform: translate3d(3%, -2%, 0) scale(0.97);
51
- }
52
- }
53
-
54
- @media (prefers-reduced-motion: reduce) {
55
- :global(body)::before {
56
- animation: none;
57
- }
58
- }
59
-
60
- .App {
61
- min-height: 100vh;
62
- position: relative;
63
- }
64
-
65
- :global(h1) {
66
- font-size: 1.75rem;
67
- font-weight: 600;
68
- margin: 0;
69
- }
70
-
71
- :global(h2) {
72
- font-size: 1.25rem;
73
- font-weight: 600;
74
- margin: 0 0 --spacing(3);
75
- }
76
-
77
- :global(a) {
78
- color: var(--color-primary-600);
79
- }
80
-
81
- :global(a:hover) {
82
- color: var(--color-primary-700);
83
- }
@@ -1,30 +0,0 @@
1
- import './theme.css'; //needs to be the first import before importing other components
2
- import React, { Suspense } from 'react';
3
- import { ErrorBoundary } from 'react-error-boundary';
4
- import { Spinner } from './components/Spinner';
5
- import { Error } from './components/Error';
6
- import { AppRoot } from '@_linked/server-utils/components/AppRoot';
7
- import { Head } from '@_linked/server-utils/components/Head';
8
- import { Body } from '@_linked/server-utils/components/Body';
9
- import AppRoutes, { ROUTES } from './routes';
10
- import style from './App.module.css'; //import any .module.css file like this and access the classnames from the style object
11
-
12
- // To add sign-in to this app, install `@_linked/auth`, wrap <AppRoutes/>
13
- // in <ProvideAuth>, and uncomment the `requireAuth` line in routes.tsx.
14
-
15
- export default function App() {
16
- return (
17
- <AppRoot>
18
- <Head>
19
- {/* Add tags to html <head> here, for example, a font <link href='https://fonts.someapi.com/...' />*/}
20
- </Head>
21
- <Body routes={ROUTES} pageStyles={style} className={style.App}>
22
- <Suspense fallback={<Spinner />}>
23
- <ErrorBoundary FallbackComponent={Error}>
24
- <AppRoutes />
25
- </ErrorBoundary>
26
- </Suspense>
27
- </Body>
28
- </AppRoot>
29
- );
30
- }
@@ -1,9 +0,0 @@
1
- //import all ShapeProviders here or define a generic BackendProvider, see https://linked.cm
2
-
3
- import { BackendProvider } from '@_linked/server-utils/utils/BackendProvider';
4
-
5
- export class Backend extends BackendProvider {
6
- constructor(server, linkedServer) {
7
- super(server, linkedServer);
8
- }
9
- }
@@ -1,9 +0,0 @@
1
- .error {
2
- color: white;
3
- margin: 2rem;
4
-
5
- pre {
6
- white-space: pre-wrap;
7
- padding: 1rem;
8
- }
9
- }
@@ -1,11 +0,0 @@
1
- import React from 'react';
2
- import style from './Error.module.css';
3
-
4
- export function Error({ error }) {
5
- return (
6
- <div className={style.error}>
7
- <h1>Application Error</h1>
8
- <pre>{error.stack}</pre>
9
- </div>
10
- );
11
- }
@@ -1,100 +0,0 @@
1
- @import '@_linked/css/package.css';
2
-
3
- .Root {
4
- background: var(--bg-card);
5
- border-radius: var(--radius-card);
6
- box-shadow: var(--shadow-card);
7
- padding: --spacing(6);
8
- }
9
-
10
- .Intro {
11
- color: var(--color-gray-600);
12
- font-size: 0.9rem;
13
- line-height: 1.5;
14
- margin: 0 0 --spacing(5);
15
- }
16
-
17
- .Intro code {
18
- background: var(--bg-section);
19
- padding: 0 --spacing(1);
20
- border-radius: var(--radius-element);
21
- font-family: var(--font-mono);
22
- font-size: 0.85em;
23
- }
24
-
25
- .Empty {
26
- color: var(--color-gray-500);
27
- font-style: italic;
28
- padding: --spacing(4) 0;
29
- margin: 0;
30
- }
31
-
32
- .Loading {
33
- color: var(--color-gray-500);
34
- padding: --spacing(4) 0;
35
- margin: 0;
36
- }
37
-
38
- .List {
39
- list-style: none;
40
- padding: 0;
41
- margin: 0 0 --spacing(5);
42
- }
43
-
44
- .List > li {
45
- padding: --spacing(3) 0;
46
- border-bottom: 1px solid var(--color-gray-200);
47
- }
48
-
49
- .List > li:first-child {
50
- padding-top: 0;
51
- }
52
-
53
- .List > li:last-child {
54
- border-bottom: none;
55
- }
56
-
57
- .Form {
58
- display: flex;
59
- gap: --spacing(2);
60
- flex-wrap: wrap;
61
- padding-top: --spacing(4);
62
- border-top: 1px solid var(--color-gray-200);
63
- }
64
-
65
- .Form input {
66
- flex: 1 1 140px;
67
- padding: --spacing(2) --spacing(3);
68
- border: 1px solid var(--color-gray-300);
69
- border-radius: var(--radius-element);
70
- background: var(--bg-card);
71
- color: var(--color-gray-900);
72
- font: inherit;
73
- }
74
-
75
- .Form input:focus {
76
- outline: none;
77
- border-color: var(--color-primary-500);
78
- box-shadow: 0 0 0 2px var(--color-primary-200);
79
- }
80
-
81
- .Form button {
82
- padding: --spacing(2) --spacing(5);
83
- background: var(--color-primary-500);
84
- color: var(--color-white);
85
- border: none;
86
- border-radius: var(--radius-element);
87
- font: inherit;
88
- font-weight: 500;
89
- cursor: pointer;
90
- transition: background 120ms ease;
91
- }
92
-
93
- .Form button:hover:not(:disabled) {
94
- background: var(--color-primary-600);
95
- }
96
-
97
- .Form button:disabled {
98
- opacity: 0.6;
99
- cursor: not-allowed;
100
- }
@@ -1,97 +0,0 @@
1
- // Example PersonOverview — demonstrates @_linked/react's data binding:
2
- // linkedSetComponent for the list (auto-runs the query, injects results and
3
- // an injected _refresh); linkedComponent for each row (see PersonPreview).
4
- // After Person.create the form calls _refresh() to re-run the list query.
5
- //
6
- // Replace or extend this with your own shapes (see https://linked.cm).
7
- import React, { useCallback, useRef, useState } from 'react';
8
- import { linkedSetComponent } from '@_linked/react';
9
- import { Person } from '@_linked/schema/shapes/Person';
10
- import { PersonPreview } from './PersonPreview';
11
- import { PersonListRefreshProvider } from './PersonOverviewContext';
12
- import style from './PersonOverview.module.css';
13
-
14
- const PersonList = linkedSetComponent(
15
- Person.select((p) => [p.givenName, p.familyName]),
16
- ({ linkedData = [], _refresh, refreshRef }: any) => {
17
- // Forward the list's _refresh up via a ref so the sibling form +
18
- // child rows can trigger a re-fetch through context.
19
- if (refreshRef) {
20
- refreshRef.current = _refresh;
21
- }
22
-
23
- if (linkedData.length === 0) {
24
- return (
25
- <p className={style.Empty}>No people yet. Add one below.</p>
26
- );
27
- }
28
-
29
- return (
30
- <ul className={style.List} data-testid="person-list">
31
- {linkedData.map((p: { id: string }) => (
32
- <li key={p.id}>
33
- <PersonPreview of={{ id: p.id }} />
34
- </li>
35
- ))}
36
- </ul>
37
- );
38
- },
39
- );
40
-
41
- function PersonAddForm({ onAdded }: { onAdded: () => void }) {
42
- const [given, setGiven] = useState('');
43
- const [family, setFamily] = useState('');
44
- const [busy, setBusy] = useState(false);
45
-
46
- async function submit(e: React.FormEvent) {
47
- e.preventDefault();
48
- if (!given && !family) return;
49
- setBusy(true);
50
- try {
51
- await Person.create({ givenName: given, familyName: family });
52
- setGiven('');
53
- setFamily('');
54
- onAdded();
55
- } finally {
56
- setBusy(false);
57
- }
58
- }
59
-
60
- return (
61
- <form className={style.Form} onSubmit={submit}>
62
- <input
63
- value={given}
64
- placeholder="First name"
65
- onChange={(e) => setGiven(e.target.value)}
66
- />
67
- <input
68
- value={family}
69
- placeholder="Last name"
70
- onChange={(e) => setFamily(e.target.value)}
71
- />
72
- <button type="submit" disabled={busy}>
73
- Add
74
- </button>
75
- </form>
76
- );
77
- }
78
-
79
- export function PersonOverview() {
80
- const refreshRef = useRef<() => void>(() => {});
81
- const refresh = useCallback(() => refreshRef.current(), []);
82
-
83
- return (
84
- <PersonListRefreshProvider value={refresh}>
85
- <div className={style.Root} data-testid="person-overview">
86
- <h2>People</h2>
87
- <p className={style.Intro}>
88
- Example code using the <code>@_linked</code> query DSL against
89
- your local Fuseki dataset. Edit{' '}
90
- <code>src/components/PersonOverview.tsx</code> to extend it.
91
- </p>
92
- <PersonList refreshRef={refreshRef} />
93
- <PersonAddForm onAdded={refresh} />
94
- </div>
95
- </PersonListRefreshProvider>
96
- );
97
- }
@@ -1,12 +0,0 @@
1
- // Small context that lets PersonPreview rows trigger a list refresh on
2
- // their parent PersonOverview without prop-drilling through the
3
- // linkedSetComponent's wrapped renderer.
4
- import React, { useContext } from 'react';
5
-
6
- const PersonListRefreshContext = React.createContext<() => void>(() => {});
7
-
8
- export const PersonListRefreshProvider = PersonListRefreshContext.Provider;
9
-
10
- export function usePersonListRefresh(): () => void {
11
- return useContext(PersonListRefreshContext);
12
- }