@ossy/cli 0.9.0 → 0.10.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/README.md CHANGED
@@ -1,45 +1,87 @@
1
1
  # @ossy/cli
2
2
 
3
- Command line tool that makes it easier to interact with our APIs
3
+ Unified CLI for the Ossy platform: app dev/build and CMS workflows.
4
4
 
5
- ## Cms
5
+ ## Commands
6
6
 
7
- ### import-resource-templates
8
- Imports resource templates to your workspace so that they can be used in the UI.
7
+ | Command | Description |
8
+ |---------|-------------|
9
+ | `init [dir]` | Scaffold a new Ossy app (default: current directory) |
10
+ | `dev` | Start dev server with watch (uses `src/*.page.jsx` or `src/pages.jsx`, `src/config.js`) |
11
+ | `build` | Production build |
12
+ | `cms upload` | Upload resource templates to your workspace |
13
+ | `cms validate` | Validate ossy config and resource templates |
14
+
15
+ ## App: dev & build
9
16
 
10
17
  ```bash
11
- npx @ossy/cli cms import-resource-templates --authentication <cms-api-token> --ossy-file ossy.json
18
+ npx @ossy/cli dev
19
+ npx @ossy/cli build
12
20
  ```
13
21
 
14
- #### Workflow example
22
+ Options: `--pages`, `--config`, `--destination`. See `@ossy/app` for details.
23
+
24
+ ## CMS: upload
25
+
26
+ Upload resource templates to your workspace so they can be used in the UI.
15
27
 
16
28
  ```bash
29
+ npx @ossy/cli cms upload --authentication <cms-api-token> --ossy-file ossy.json
30
+ ```
31
+
32
+ ### Config consistency
33
+
34
+ - **App** (`dev`, `build`): `--config` → app config (`src/config.js` by default)
35
+ - **CMS** (`cms upload`): `--ossy-file` → workspace config with `workspaceId` and `resourceTemplates`
36
+
37
+ ### Workflow example
38
+
39
+ ```yaml
17
40
  name: "[CMS] Upload resource templates"
18
41
 
19
42
  on:
20
43
  workflow_dispatch:
21
44
 
22
45
  jobs:
23
-
24
46
  upload-resource-templates:
25
47
  name: Upload resource templates
26
48
  runs-on: ubuntu-latest
27
49
  steps:
28
50
  - uses: actions/checkout@v2
29
-
30
51
  - uses: actions/setup-node@v2
31
52
  with:
32
53
  node-version: "16"
33
-
34
54
  - name: Upload
35
55
  run: |
36
- npx --yes @ossy/cli cms import-resource-templates \
56
+ npx --yes @ossy/cli cms upload \
37
57
  --authentication ${{ secrets.CMS_API_TOKEN }} \
38
- --ossy-file ossy.json \
58
+ --ossy-file ossy.json
59
+ ```
60
+
61
+ ### cms validate
62
+
63
+ Validate an ossy config file before uploading:
64
+
65
+ ```bash
66
+ npx @ossy/cli cms validate --ossy-file ossy.json
39
67
  ```
40
68
 
41
- #### Arguments
69
+ Defaults to `ossy.json` in the current directory when `--ossy-file` is omitted.
70
+
71
+ ### Arguments
72
+
42
73
  | Argument | Description | Required |
43
- |-|-|-|
44
- | --authentication | Your personal CMS API token | required |
45
- | --ossy-file | Path to the file containing the workspaceId and resource templates | required |
74
+ |----------|-------------|----------|
75
+ | --authentication, -a | Your CMS API token | Yes (upload only) |
76
+ | --ossy-file | Path to file with `workspaceId` and `resourceTemplates` | Yes (upload), optional (validate) |
77
+
78
+ ## init
79
+
80
+ Scaffold a new Ossy app:
81
+
82
+ ```bash
83
+ npx @ossy/cli init
84
+ npx @ossy/cli init my-app
85
+ ```
86
+
87
+ Creates `src/pages.jsx`, `src/config.js`, and `package.json` (if missing).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/cli",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ossy-se/packages.git"
@@ -10,13 +10,14 @@
10
10
  "main": "./src/index.js",
11
11
  "type": "module",
12
12
  "scripts": {
13
- "test": "echo \"Error: no test specified\" && exit 0",
13
+ "test": "node --experimental-vm-modules ../node_modules/jest/bin/jest.js --verbose",
14
14
  "build": "echo \"The build step is not required when using JavaScript!\" && exit 0"
15
15
  },
16
16
  "author": "Ossy <yourfriends@ossy.se> (https://ossy.se)",
17
17
  "license": "MIT",
18
18
  "bin": "./src/index.js",
19
19
  "dependencies": {
20
+ "@ossy/app": "^0.10.0",
20
21
  "arg": "^5.0.2"
21
22
  },
22
23
  "publishConfig": {
@@ -27,5 +28,5 @@
27
28
  "/src",
28
29
  "README.md"
29
30
  ],
30
- "gitHead": "eec27800542ed59264caf788326e525a2ee77d63"
31
+ "gitHead": "3019a8da590435ba6f10d7d861addeba011baf5d"
31
32
  }
package/src/cms/cli.js ADDED
@@ -0,0 +1,145 @@
1
+ import { resolve } from 'path'
2
+ import { readFileSync, existsSync } from 'fs'
3
+ import arg from 'arg'
4
+ import { logInfo, logError } from '../log.js'
5
+
6
+ const Api = {
7
+ uploadResourceTemplates: (apiUrl, token, workspaceId, resourceTemplates) => {
8
+ const endpoint = `${apiUrl}/workspaces/${workspaceId}/resource-templates`
9
+ const fetchOptions = {
10
+ method: 'POST',
11
+ headers: { 'Authorization': token, 'Content-Type': 'application/json' },
12
+ body: JSON.stringify(resourceTemplates)
13
+ }
14
+ return fetch(endpoint, fetchOptions)
15
+ }
16
+ }
17
+
18
+ const resolveConfigImport = (filePath) =>
19
+ filePath.endsWith('json')
20
+ ? Promise.resolve(JSON.parse(readFileSync(filePath, 'utf8')))
21
+ : import(filePath)
22
+
23
+ const upload = (options) => {
24
+ const parsedArgs = arg({
25
+ '--authentication': String,
26
+ '--a': '--authentication',
27
+ '--ossy-file': String,
28
+ }, { argv: options })
29
+
30
+ const token = parsedArgs['--authentication']
31
+ const ossyFilePath = parsedArgs['--ossy-file']
32
+
33
+ if (!token) {
34
+ logError({ message: '[@ossy/cli] No token provided. Use --authentication or -a' })
35
+ process.exit(1)
36
+ }
37
+ if (!ossyFilePath) {
38
+ logError({ message: '[@ossy/cli] No config file provided. Use --ossy-file' })
39
+ process.exit(1)
40
+ }
41
+
42
+ logInfo({ message: '[@ossy/cli] Reading config...' })
43
+ const filePath = resolve(ossyFilePath)
44
+
45
+ return resolveConfigImport(filePath)
46
+ .then((module) => {
47
+ const config = module?.default ?? module
48
+ const apiUrl = config?.apiUrl || 'https://api.ossy.se/api/v0'
49
+ const workspaceId = config?.workspaceId
50
+ const resourceTemplates = config?.resourceTemplates
51
+
52
+ if (!workspaceId) {
53
+ logError({ message: '[@ossy/cli] No workspaceId in ossy file' })
54
+ process.exit(1)
55
+ }
56
+ if (!resourceTemplates) {
57
+ logError({ message: '[@ossy/cli] No resourceTemplates in ossy file' })
58
+ process.exit(1)
59
+ }
60
+
61
+ logInfo({ message: '[@ossy/cli] Uploading resource templates...' })
62
+
63
+ return Api.uploadResourceTemplates(apiUrl, token, workspaceId, resourceTemplates)
64
+ .then((response) => {
65
+ if (!response.ok) throw new Error(`Upload failed: ${response.status}`)
66
+ logInfo({ message: '[@ossy/cli] Done' })
67
+ })
68
+ .catch((error) => {
69
+ logError({ message: '[@ossy/cli] Error', error })
70
+ process.exit(1)
71
+ })
72
+ })
73
+ .catch((error) => {
74
+ logError({ message: '[@ossy/cli] Error', error })
75
+ process.exit(1)
76
+ })
77
+ }
78
+
79
+ const validate = (options) => {
80
+ const parsedArgs = arg({
81
+ '--ossy-file': String,
82
+ }, { argv: options })
83
+
84
+ const ossyFilePath = parsedArgs['--ossy-file']
85
+ const filePath = ossyFilePath ? resolve(ossyFilePath) : resolve('ossy.json')
86
+
87
+ if (!existsSync(filePath)) {
88
+ logError({ message: `[@ossy/cli] File not found: ${filePath}. Use --ossy-file` })
89
+ process.exit(1)
90
+ }
91
+
92
+ logInfo({ message: '[@ossy/cli] Validating...' })
93
+
94
+ resolveConfigImport(filePath)
95
+ .then((module) => {
96
+ const config = module?.default ?? module
97
+ const workspaceId = config?.workspaceId
98
+ const resourceTemplates = config?.resourceTemplates
99
+
100
+ const errors = []
101
+ if (!workspaceId) errors.push('Missing workspaceId')
102
+ if (!resourceTemplates) {
103
+ errors.push('Missing resourceTemplates')
104
+ } else if (!Array.isArray(resourceTemplates)) {
105
+ errors.push('resourceTemplates must be an array')
106
+ } else {
107
+ resourceTemplates.forEach((t, i) => {
108
+ if (!t.id) errors.push(`Template ${i}: missing id`)
109
+ if (!t.name) errors.push(`Template ${i}: missing name`)
110
+ if (!t.fields || !Array.isArray(t.fields)) {
111
+ errors.push(`Template ${i}: fields must be an array`)
112
+ }
113
+ })
114
+ }
115
+
116
+ if (errors.length > 0) {
117
+ console.error('[@ossy/cli] Validation failed:')
118
+ errors.forEach((e) => console.error(' -', e))
119
+ process.exit(1)
120
+ }
121
+ logInfo({ message: '[@ossy/cli] Valid' })
122
+ })
123
+ .catch((error) => {
124
+ logError({ message: '[@ossy/cli] Error', error })
125
+ process.exit(1)
126
+ })
127
+ }
128
+
129
+ const subcommands = {
130
+ upload,
131
+ validate,
132
+ }
133
+
134
+ export const handler = ([subcommand, ...options]) => {
135
+ if (!subcommand) {
136
+ logError({ message: '[@ossy/cli] cms: no subcommand. Use: ossy cms upload | validate' })
137
+ process.exit(1)
138
+ }
139
+ const fn = subcommands[subcommand]
140
+ if (!fn) {
141
+ logError({ message: `[@ossy/cli] cms: unknown subcommand "${subcommand}". Use: ossy cms upload | validate` })
142
+ process.exit(1)
143
+ }
144
+ fn(options)
145
+ }
package/src/index.js CHANGED
@@ -1,11 +1,34 @@
1
1
  #!/usr/bin/env node
2
- /* eslint-disable global-require, no-unused-vars */
3
- import * as ResourceTemplates from './resource-templates/cli.js'
2
+ import { build, dev } from '@ossy/app'
3
+ import * as Cms from './cms/cli.js'
4
+ import * as Init from './init/cli.js'
4
5
 
5
- const [_, __, handlerName, ...restArgs] = process.argv
6
+ const [,, command, ...restArgs] = process.argv
6
7
 
7
- const handler = {
8
- 'resource-templates': () => ResourceTemplates.handler
9
- }[handlerName]
8
+ if (!command) {
9
+ console.error('[@ossy/cli] No command provided. Usage: ossy dev | build | init | cms <subcommand>')
10
+ process.exit(1)
11
+ }
10
12
 
11
- handler(restArgs)
13
+ const run = async () => {
14
+ if (command === 'cms') {
15
+ Cms.handler(restArgs)
16
+ return
17
+ }
18
+ if (command === 'init') {
19
+ Init.init(restArgs)
20
+ return
21
+ }
22
+ if (command === 'dev') {
23
+ await dev(restArgs)
24
+ return
25
+ }
26
+ if (command === 'build') {
27
+ await build(restArgs)
28
+ return
29
+ }
30
+ console.error(`[@ossy/cli] Unknown command: ${command}`)
31
+ process.exit(1)
32
+ }
33
+
34
+ run()
@@ -0,0 +1,73 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import { logInfo, logError } from '../log.js'
4
+
5
+ const TEMPLATE_HOME_PAGE = `import React from 'react'
6
+
7
+ export default () => (
8
+ <main style={{ padding: '2rem' }}>
9
+ <h1>Welcome</h1>
10
+ <p>Add new pages by creating *.page.jsx files in src/</p>
11
+ </main>
12
+ )
13
+ `
14
+
15
+ const TEMPLATE_CONFIG = `import { CloudLight } from '@ossy/themes'
16
+
17
+ export default {
18
+ workspaceId: 'your-workspace-id',
19
+ theme: CloudLight,
20
+ }
21
+ `
22
+
23
+ const TEMPLATE_PACKAGE_JSON = {
24
+ name: 'my-ossy-app',
25
+ version: '0.0.1',
26
+ private: true,
27
+ type: 'module',
28
+ scripts: {
29
+ dev: 'npx @ossy/cli dev',
30
+ build: 'npx @ossy/cli build',
31
+ start: 'node build/server.js',
32
+ },
33
+ dependencies: {
34
+ '@ossy/cli': '^0.9.0',
35
+ '@ossy/app': '^0.9.0',
36
+ '@ossy/connected-components': '^0.9.0',
37
+ '@ossy/design-system': '^0.9.0',
38
+ '@ossy/themes': '^0.9.0',
39
+ '@ossy/router-react': '^0.9.0',
40
+ '@ossy/sdk': '^0.9.0',
41
+ '@ossy/sdk-react': '^0.9.0',
42
+ react: '^19.0.0',
43
+ 'react-dom': '^19.0.0',
44
+ },
45
+ }
46
+
47
+ export const init = (options) => {
48
+ const parsedArgs = options || []
49
+ const targetDir = parsedArgs[0] ? path.resolve(parsedArgs[0]) : process.cwd()
50
+
51
+ const srcDir = path.join(targetDir, 'src')
52
+ const homePagePath = path.join(srcDir, 'home.page.jsx')
53
+ if (fs.existsSync(homePagePath) || fs.existsSync(path.join(targetDir, 'src', 'pages.jsx'))) {
54
+ logError({ message: '[@ossy/cli] Project already exists (src/home.page.jsx or src/pages.jsx found)' })
55
+ process.exit(1)
56
+ }
57
+
58
+ logInfo({ message: `[@ossy/cli] Creating project in ${targetDir}` })
59
+
60
+ fs.mkdirSync(srcDir, { recursive: true })
61
+
62
+ fs.writeFileSync(homePagePath, TEMPLATE_HOME_PAGE)
63
+ fs.writeFileSync(path.join(srcDir, 'config.js'), TEMPLATE_CONFIG)
64
+
65
+ const pkgPath = path.join(targetDir, 'package.json')
66
+ if (!fs.existsSync(pkgPath)) {
67
+ fs.writeFileSync(pkgPath, JSON.stringify(TEMPLATE_PACKAGE_JSON, null, 2))
68
+ } else {
69
+ logInfo({ message: '[@ossy/cli] package.json exists, skipping. Add scripts manually if needed.' })
70
+ }
71
+
72
+ logInfo({ message: '[@ossy/cli] Done. Run: npm install && npm run dev' })
73
+ }
@@ -1,65 +0,0 @@
1
- import { resolve } from 'path'
2
- import { readFileSync } from 'fs'
3
- import arg from 'arg'
4
- import { logInfo, logError, logErrorAndReject } from '../log.js'
5
-
6
- const Api = {
7
- uploadResourceTemplates: (apiUrl, token, workspaceId, resourceTemplates) => {
8
- const endpoint = `${apiUrl}/workspaces/${workspaceId}/resource-templates`
9
-
10
- const fetchOptions = {
11
- method: 'POST',
12
- headers: { 'Authorization': token, 'Content-Type': 'application/json' },
13
- body: JSON.stringify(resourceTemplates)
14
- }
15
-
16
- return fetch(endpoint, fetchOptions)
17
- }
18
- }
19
-
20
- const resolveConfigImport = path => path.endsWith('json')
21
- ? Promise.resolve(JSON.parse(readFileSync(path), 'utf8'))
22
- : import(path)
23
-
24
- const importResourceTemplates = options => {
25
-
26
- const parsedArgs = arg({
27
- '--authentication': String,
28
- '--a': '--authentication',
29
-
30
- '--config': String,
31
- '-c': '--config',
32
- }, { argv: options })
33
-
34
- logInfo({ message: '[CMS] reading files' })
35
- const token = parsedArgs['--authentication'];
36
- const filePath = resolve(parsedArgs['--config'])
37
-
38
- if (!token) return logErrorAndReject({ message: '[CMS] No token provided with --authentication'})
39
-
40
- return resolveConfigImport(filePath)
41
- .then(module => {
42
- const config = module?.default
43
- const apiUrl = config?.apiUrl || 'https://api.ossy.se/api/v0'
44
- const workspaceId = config?.workspaceId
45
- const resourceTemplates = config?.resourceTemplates
46
-
47
- if (!workspaceId) return logErrorAndReject({ message: '[@ossy/cli] No workspaceId provided in config'})
48
- if (!resourceTemplates) return logErrorAndReject({ message: '[@ossy/cli] No resource templates provided in config'})
49
-
50
- logInfo({ message: '[@ossy/cli] uploading resource templates' })
51
-
52
- Api.uploadResourceTemplates(apiUrl, token, workspaceId, resourceTemplates)
53
- .then(response => {
54
- logInfo({ message: '[@ossy/cli] Done' })
55
- })
56
- .catch(error => logError({ message: '[@ossy/cli] Error', error }))
57
- })
58
-
59
- }
60
-
61
- export const handler = ([command, ...options]) => {
62
- !!command
63
- ? { 'upload': importResourceTemplates }[command](options)
64
- : logError({ message: '[@ossy/cli] No command provided' })
65
- }