@_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.
- package/CHANGELOG.md +41 -0
- package/defaults/setup-publish/dual-branch/github/workflows/publish.yml +3 -0
- package/defaults/setup-publish/github/workflows/publish.yml +3 -0
- package/defaults/shape.ts +8 -2
- package/jest.config.cjs +8 -4
- package/lib/esm/cli-methods.d.ts +11 -3
- package/lib/esm/cli-methods.d.ts.map +1 -1
- package/lib/esm/cli-methods.js +51 -105
- package/lib/esm/cli-methods.js.map +1 -1
- package/lib/esm/cli.js +32 -8
- package/lib/esm/cli.js.map +1 -1
- package/lib/esm/commands/start.d.ts +6 -0
- package/lib/esm/commands/start.d.ts.map +1 -0
- package/lib/esm/commands/start.js +311 -0
- package/lib/esm/commands/start.js.map +1 -0
- package/lib/esm/config-webpack-app.d.ts +1 -1
- package/lib/esm/config-webpack-app.d.ts.map +1 -1
- package/lib/esm/config-webpack-app.js +1 -1
- package/lib/esm/config-webpack-app.js.map +1 -1
- package/lib/esm/config-webpack.d.ts +1 -1
- package/lib/esm/config-webpack.d.ts.map +1 -1
- package/lib/esm/config-webpack.js +4 -4
- package/lib/esm/config-webpack.js.map +1 -1
- package/lib/esm/defineConfig.d.ts +1 -1
- package/lib/esm/defineConfig.d.ts.map +1 -1
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.d.ts.map +1 -1
- package/lib/esm/index.js +1 -1
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/interfaces.d.ts +9 -0
- package/lib/esm/interfaces.d.ts.map +1 -1
- package/lib/esm/lifecycle.d.ts +24 -0
- package/lib/esm/lifecycle.d.ts.map +1 -0
- package/lib/esm/lifecycle.js +156 -0
- package/lib/esm/lifecycle.js.map +1 -0
- package/lib/esm/loaders/ts-loader.d.mts.map +1 -1
- package/lib/esm/loaders/ts-loader.mjs +11 -2
- package/lib/esm/loaders/ts-loader.mjs.map +1 -1
- package/lib/esm/utils.d.ts +1 -1
- package/lib/esm/utils.d.ts.map +1 -1
- package/lib/esm/utils.js +9 -3
- package/lib/esm/utils.js.map +1 -1
- package/lib/esm/vite-config.d.ts +20 -0
- package/lib/esm/vite-config.d.ts.map +1 -0
- package/lib/esm/vite-config.js +410 -0
- package/lib/esm/vite-config.js.map +1 -0
- package/package.json +13 -14
- package/test-results/.last-run.json +6 -0
- package/test-results/create-app-Person-fixture-add-/342/206/222-edit-/342/206/222-delete-round-trip/error-context.md +16 -0
- package/test-results/create-app-Person-fixture-add-/342/206/222-edit-/342/206/222-delete-round-trip/trace.zip +0 -0
- package/tests/unit/safeYarn.test.ts +1 -1
- package/tsconfig-esm.json +1 -1
- package/defaults/app-with-backend/.env-cmdrc.json +0 -19
- package/defaults/app-with-backend/.eslintignore +0 -5
- package/defaults/app-with-backend/.eslintrc.json +0 -40
- package/defaults/app-with-backend/.prettierignore +0 -4
- package/defaults/app-with-backend/.prettierrc.json +0 -27
- package/defaults/app-with-backend/.vscode/launch.json +0 -12
- package/defaults/app-with-backend/.yarn/plugins/@yarnpkg/plugin-version.cjs +0 -550
- package/defaults/app-with-backend/.yarn/releases/yarn-3.6.1.cjs +0 -874
- package/defaults/app-with-backend/gitignore.template +0 -25
- package/defaults/app-with-backend/linked.backend.datasets.example.json +0 -17
- package/defaults/app-with-backend/linked.backend.storage.ts +0 -35
- package/defaults/app-with-backend/linked.config.js +0 -5
- package/defaults/app-with-backend/package.json +0 -70
- package/defaults/app-with-backend/pm2-staging.config.js +0 -12
- package/defaults/app-with-backend/pm2.config.js +0 -12
- package/defaults/app-with-backend/public/favicon-144x144.png +0 -0
- package/defaults/app-with-backend/public/favicon-57x57.png +0 -0
- package/defaults/app-with-backend/public/favicon-72x72.png +0 -0
- package/defaults/app-with-backend/public/favicon.ico +0 -0
- package/defaults/app-with-backend/readme.md +0 -93
- package/defaults/app-with-backend/src/App.module.css +0 -83
- package/defaults/app-with-backend/src/App.tsx +0 -30
- package/defaults/app-with-backend/src/backend.ts +0 -9
- package/defaults/app-with-backend/src/components/Error.module.css +0 -9
- package/defaults/app-with-backend/src/components/Error.tsx +0 -11
- package/defaults/app-with-backend/src/components/PersonOverview.module.css +0 -100
- package/defaults/app-with-backend/src/components/PersonOverview.tsx +0 -97
- package/defaults/app-with-backend/src/components/PersonOverviewContext.tsx +0 -12
- package/defaults/app-with-backend/src/components/PersonPreview.module.css +0 -97
- package/defaults/app-with-backend/src/components/PersonPreview.tsx +0 -109
- package/defaults/app-with-backend/src/components/Spinner.module.css +0 -17
- package/defaults/app-with-backend/src/components/Spinner.tsx +0 -12
- package/defaults/app-with-backend/src/index.tsx +0 -34
- package/defaults/app-with-backend/src/layout/DefaultLayout.module.css +0 -15
- package/defaults/app-with-backend/src/layout/DefaultLayout.tsx +0 -12
- package/defaults/app-with-backend/src/layout/Header.module.css +0 -46
- package/defaults/app-with-backend/src/layout/Header.tsx +0 -27
- package/defaults/app-with-backend/src/linked.frontend.datasets.json +0 -12
- package/defaults/app-with-backend/src/linked.frontend.storage.ts +0 -36
- package/defaults/app-with-backend/src/package.ts +0 -16
- package/defaults/app-with-backend/src/pages/Home.module.css +0 -23
- package/defaults/app-with-backend/src/pages/Home.tsx +0 -14
- package/defaults/app-with-backend/src/pages/Page1.module.css +0 -111
- package/defaults/app-with-backend/src/pages/Page1.tsx +0 -196
- package/defaults/app-with-backend/src/pages/PageNotFound.module.css +0 -19
- package/defaults/app-with-backend/src/pages/PageNotFound.tsx +0 -14
- package/defaults/app-with-backend/src/pages/Signin.module.css +0 -20
- package/defaults/app-with-backend/src/pages/Signin.tsx +0 -16
- package/defaults/app-with-backend/src/routes.tsx +0 -78
- package/defaults/app-with-backend/src/theme.css +0 -91
- package/defaults/app-with-backend/src/types.ts +0 -14
- package/defaults/app-with-backend/src/utils/lazyWithPreload.tsx +0 -42
- package/defaults/app-with-backend/src/utils/preloadRoutes.ts +0 -38
- package/defaults/app-with-backend/tsconfig.json +0 -24
- package/defaults/app-with-backend/yarnrc.yml.template +0 -8
- package/lib/cjs/cli-methods.d.ts +0 -67
- package/lib/cjs/cli-methods.d.ts.map +0 -1
- package/lib/cjs/cli-methods.js +0 -2718
- package/lib/cjs/cli-methods.js.map +0 -1
- package/lib/cjs/cli.d.ts +0 -3
- package/lib/cjs/cli.d.ts.map +0 -1
- package/lib/cjs/cli.js +0 -315
- package/lib/cjs/cli.js.map +0 -1
- package/lib/cjs/commands/build-package.d.ts +0 -9
- package/lib/cjs/commands/build-package.d.ts.map +0 -1
- package/lib/cjs/commands/build-package.js +0 -55
- package/lib/cjs/commands/build-package.js.map +0 -1
- package/lib/cjs/commands/safe-yarn.d.ts +0 -10
- package/lib/cjs/commands/safe-yarn.d.ts.map +0 -1
- package/lib/cjs/commands/safe-yarn.js +0 -66
- package/lib/cjs/commands/safe-yarn.js.map +0 -1
- package/lib/cjs/commands/setup-publish.d.ts +0 -23
- package/lib/cjs/commands/setup-publish.d.ts.map +0 -1
- package/lib/cjs/commands/setup-publish.js +0 -369
- package/lib/cjs/commands/setup-publish.js.map +0 -1
- package/lib/cjs/config-webpack-app.d.ts +0 -112
- package/lib/cjs/config-webpack-app.d.ts.map +0 -1
- package/lib/cjs/config-webpack-app.js +0 -427
- package/lib/cjs/config-webpack-app.js.map +0 -1
- package/lib/cjs/config-webpack.d.ts +0 -55
- package/lib/cjs/config-webpack.d.ts.map +0 -1
- package/lib/cjs/config-webpack.js +0 -417
- package/lib/cjs/config-webpack.js.map +0 -1
- package/lib/cjs/defineConfig.d.ts +0 -8
- package/lib/cjs/defineConfig.d.ts.map +0 -1
- package/lib/cjs/defineConfig.js +0 -12
- package/lib/cjs/defineConfig.js.map +0 -1
- package/lib/cjs/index.d.ts +0 -12
- package/lib/cjs/index.d.ts.map +0 -1
- package/lib/cjs/index.js +0 -43
- package/lib/cjs/index.js.map +0 -1
- package/lib/cjs/interfaces.d.ts +0 -131
- package/lib/cjs/interfaces.d.ts.map +0 -1
- package/lib/cjs/interfaces.js +0 -3
- package/lib/cjs/interfaces.js.map +0 -1
- package/lib/cjs/loaders/css-loader.d.mts +0 -2
- package/lib/cjs/loaders/css-loader.d.mts.map +0 -1
- package/lib/cjs/loaders/css-loader.mjs +0 -86
- package/lib/cjs/loaders/css-loader.mjs.map +0 -1
- package/lib/cjs/loaders/register-css-only.d.ts +0 -2
- package/lib/cjs/loaders/register-css-only.d.ts.map +0 -1
- package/lib/cjs/loaders/register-css-only.js +0 -11
- package/lib/cjs/loaders/register-css-only.js.map +0 -1
- package/lib/cjs/loaders/register.d.ts +0 -2
- package/lib/cjs/loaders/register.d.ts.map +0 -1
- package/lib/cjs/loaders/register.js +0 -11
- package/lib/cjs/loaders/register.js.map +0 -1
- package/lib/cjs/loaders/ts-loader.d.mts +0 -3
- package/lib/cjs/loaders/ts-loader.d.mts.map +0 -1
- package/lib/cjs/loaders/ts-loader.mjs +0 -116
- package/lib/cjs/loaders/ts-loader.mjs.map +0 -1
- package/lib/cjs/metadata.d.ts +0 -1
- package/lib/cjs/metadata.d.ts.map +0 -1
- package/lib/cjs/metadata.js +0 -155
- package/lib/cjs/metadata.js.map +0 -1
- package/lib/cjs/package.json +0 -139
- package/lib/cjs/plugins/check-imports.d.ts +0 -2
- package/lib/cjs/plugins/check-imports.d.ts.map +0 -1
- package/lib/cjs/plugins/check-imports.js +0 -68
- package/lib/cjs/plugins/check-imports.js.map +0 -1
- package/lib/cjs/plugins/declaration-plugin.d.ts +0 -15
- package/lib/cjs/plugins/declaration-plugin.d.ts.map +0 -1
- package/lib/cjs/plugins/declaration-plugin.js +0 -285
- package/lib/cjs/plugins/declaration-plugin.js.map +0 -1
- package/lib/cjs/plugins/externalise-modules.d.ts +0 -7
- package/lib/cjs/plugins/externalise-modules.d.ts.map +0 -1
- package/lib/cjs/plugins/externalise-modules.js +0 -190
- package/lib/cjs/plugins/externalise-modules.js.map +0 -1
- package/lib/cjs/plugins/lincd-tailwind-sources.d.ts +0 -3
- package/lib/cjs/plugins/lincd-tailwind-sources.d.ts.map +0 -1
- package/lib/cjs/plugins/lincd-tailwind-sources.js +0 -32
- package/lib/cjs/plugins/lincd-tailwind-sources.js.map +0 -1
- package/lib/cjs/plugins/watch-run.d.ts +0 -5
- package/lib/cjs/plugins/watch-run.d.ts.map +0 -1
- package/lib/cjs/plugins/watch-run.js +0 -45
- package/lib/cjs/plugins/watch-run.js.map +0 -1
- package/lib/cjs/preflight.css +0 -137
- package/lib/cjs/tailwind.config.d.ts +0 -8
- package/lib/cjs/tailwind.config.d.ts.map +0 -1
- package/lib/cjs/tailwind.config.js +0 -17
- package/lib/cjs/tailwind.config.js.map +0 -1
- package/lib/cjs/utils.d.ts +0 -77
- package/lib/cjs/utils.d.ts.map +0 -1
- package/lib/cjs/utils.js +0 -585
- package/lib/cjs/utils.js.map +0 -1
- package/lib/esm/loaders/register-css-only.d.ts +0 -2
- package/lib/esm/loaders/register-css-only.d.ts.map +0 -1
- package/lib/esm/loaders/register-css-only.js +0 -9
- package/lib/esm/loaders/register-css-only.js.map +0 -1
- package/lib/esm/loaders/register.d.ts +0 -2
- package/lib/esm/loaders/register.d.ts.map +0 -1
- package/lib/esm/loaders/register.js +0 -9
- package/lib/esm/loaders/register.js.map +0 -1
- package/lib/esm/package.json +0 -139
- /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,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
|
-
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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,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
|
-
}
|