diff --git a/.github/workflows/readme-openapi.yml b/.github/workflows/readme-openapi.yml index b52787934f..9f42f6141b 100644 --- a/.github/workflows/readme-openapi.yml +++ b/.github/workflows/readme-openapi.yml @@ -19,5 +19,8 @@ jobs: cache: yarn - run: yarn --frozen-lockfile + - name: Install OpenAPI pkg + run: yarn global add openapi + - name: update specs run: cd packages/server && yarn specs && openapi specs/openapi.yaml --key=${{ secrets.README_API_KEY }} --id=6728a74f5918b50036c61841 diff --git a/packages/builder/src/stores/BudiStore.ts b/packages/builder/src/stores/BudiStore.ts index d5bbc99328..c638bbf5bc 100644 --- a/packages/builder/src/stores/BudiStore.ts +++ b/packages/builder/src/stores/BudiStore.ts @@ -53,17 +53,24 @@ export class BudiStore { } } -export class DerivedBudiStore extends BudiStore { +// This deliberately does not extend a BudiStore as doing so imposes a requirement that +// DerivedT must extend T, which is not desirable, due to the type of the subscribe property. +export class DerivedBudiStore { + store: BudiStore derivedStore: Readable subscribe: Readable["subscribe"] + update: Writable["update"] + set: Writable["set"] constructor( init: T, makeDerivedStore: (store: Writable) => Readable, opts?: BudiStoreOpts ) { - super(init, opts) + this.store = new BudiStore(init, opts) this.derivedStore = makeDerivedStore(this.store) this.subscribe = this.derivedStore.subscribe + this.update = this.store.update + this.set = this.store.set } } diff --git a/packages/builder/src/stores/builder/hover.js b/packages/builder/src/stores/builder/hover.ts similarity index 73% rename from packages/builder/src/stores/builder/hover.js rename to packages/builder/src/stores/builder/hover.ts index e28c74eaf2..8f9b77075c 100644 --- a/packages/builder/src/stores/builder/hover.js +++ b/packages/builder/src/stores/builder/hover.ts @@ -2,19 +2,24 @@ import { get } from "svelte/store" import { BudiStore } from "../BudiStore" import { previewStore } from "@/stores/builder" +interface BuilderHoverStore { + hoverTimeout?: NodeJS.Timeout + componentId: string | null +} + export const INITIAL_HOVER_STATE = { componentId: null, } -export class HoverStore extends BudiStore { - hoverTimeout +export class HoverStore extends BudiStore { + hoverTimeout?: NodeJS.Timeout constructor() { super({ ...INITIAL_HOVER_STATE }) this.hover = this.hover.bind(this) } - hover(componentId, notifyClient = true) { + hover(componentId: string, notifyClient = true) { clearTimeout(this.hoverTimeout) if (componentId) { this.processHover(componentId, notifyClient) @@ -25,7 +30,7 @@ export class HoverStore extends BudiStore { } } - processHover(componentId, notifyClient) { + processHover(componentId: string, notifyClient?: boolean) { if (componentId === get(this.store).componentId) { return } diff --git a/packages/builder/src/stores/builder/navigation.js b/packages/builder/src/stores/builder/navigation.ts similarity index 73% rename from packages/builder/src/stores/builder/navigation.js rename to packages/builder/src/stores/builder/navigation.ts index e9a5e6f9dd..1574efee2a 100644 --- a/packages/builder/src/stores/builder/navigation.js +++ b/packages/builder/src/stores/builder/navigation.ts @@ -2,27 +2,22 @@ import { get } from "svelte/store" import { API } from "@/api" import { appStore } from "@/stores/builder" import { BudiStore } from "../BudiStore" +import { AppNavigation, AppNavigationLink, UIObject } from "@budibase/types" + +interface BuilderNavigationStore extends AppNavigation {} export const INITIAL_NAVIGATION_STATE = { navigation: "Top", links: [], - title: null, - sticky: null, - hideLogo: null, - logoUrl: null, - hideTitle: null, textAlign: "Left", - navBackground: null, - navWidth: null, - navTextColor: null, } -export class NavigationStore extends BudiStore { +export class NavigationStore extends BudiStore { constructor() { super(INITIAL_NAVIGATION_STATE) } - syncAppNavigation(nav) { + syncAppNavigation(nav: AppNavigation) { this.update(state => ({ ...state, ...nav, @@ -33,15 +28,17 @@ export class NavigationStore extends BudiStore { this.store.set({ ...INITIAL_NAVIGATION_STATE }) } - async save(navigation) { + async save(navigation: AppNavigation) { const appId = get(appStore).appId const app = await API.saveAppMetadata(appId, { navigation }) - this.syncAppNavigation(app.navigation) + if (app.navigation) { + this.syncAppNavigation(app.navigation) + } } - async saveLink(url, title, roleId) { + async saveLink(url: string, title: string, roleId: string) { const navigation = get(this.store) - let links = [...(navigation?.links ?? [])] + let links: AppNavigationLink[] = [...(navigation?.links ?? [])] // Skip if we have an identical link if (links.find(link => link.url === url && link.text === title)) { @@ -60,7 +57,7 @@ export class NavigationStore extends BudiStore { }) } - async deleteLink(urls) { + async deleteLink(urls: string[] | string) { const navigation = get(this.store) let links = navigation?.links if (!links?.length) { @@ -86,7 +83,7 @@ export class NavigationStore extends BudiStore { }) } - syncMetadata(metadata) { + syncMetadata(metadata: UIObject) { const { navigation } = metadata this.syncAppNavigation(navigation) } diff --git a/packages/builder/src/stores/portal/auth.ts b/packages/builder/src/stores/portal/auth.ts index afbb75646c..171b2b43ae 100644 --- a/packages/builder/src/stores/portal/auth.ts +++ b/packages/builder/src/stores/portal/auth.ts @@ -4,14 +4,14 @@ import { admin } from "@/stores/portal" import analytics from "@/analytics" import { BudiStore } from "@/stores/BudiStore" import { + GetGlobalSelfResponse, isSSOUser, SetInitInfoRequest, UpdateSelfRequest, - User, } from "@budibase/types" interface PortalAuthStore { - user?: User + user?: GetGlobalSelfResponse initInfo?: Record accountPortalAccess: boolean loaded: boolean @@ -33,7 +33,7 @@ class AuthStore extends BudiStore { }) } - setUser(user?: User) { + setUser(user?: GetGlobalSelfResponse) { this.set({ loaded: true, user: user, diff --git a/packages/builder/src/stores/portal/featureFlags.js b/packages/builder/src/stores/portal/featureFlags.js deleted file mode 100644 index ef1100310a..0000000000 --- a/packages/builder/src/stores/portal/featureFlags.js +++ /dev/null @@ -1,16 +0,0 @@ -import { derived } from "svelte/store" -import { auth } from "@/stores/portal" - -export const INITIAL_FEATUREFLAG_STATE = { - SQS: false, - DEFAULT_VALUES: false, - BUDIBASE_AI: false, - AI_CUSTOM_CONFIGS: false, -} - -export const featureFlags = derived([auth], ([$auth]) => { - return { - ...INITIAL_FEATUREFLAG_STATE, - ...($auth?.user?.flags || {}), - } -}) diff --git a/packages/builder/src/stores/portal/featureFlags.ts b/packages/builder/src/stores/portal/featureFlags.ts new file mode 100644 index 0000000000..0e40d0c7b4 --- /dev/null +++ b/packages/builder/src/stores/portal/featureFlags.ts @@ -0,0 +1,8 @@ +import { derived, Readable } from "svelte/store" +import { auth } from "@/stores/portal" +import { FeatureFlags, FeatureFlagDefaults } from "@budibase/types" + +export const featureFlags: Readable = derived(auth, $auth => ({ + ...FeatureFlagDefaults, + ...($auth?.user?.flags || {}), +})) diff --git a/packages/builder/src/stores/portal/features.js b/packages/builder/src/stores/portal/features.js deleted file mode 100644 index 747052aa6c..0000000000 --- a/packages/builder/src/stores/portal/features.js +++ /dev/null @@ -1,54 +0,0 @@ -import { writable } from "svelte/store" -import { API } from "@/api" -import { licensing } from "./licensing" -import { ConfigType } from "@budibase/types" - -export const createFeatureStore = () => { - const internalStore = writable({ - scim: { - isFeatureFlagEnabled: false, - isConfigFlagEnabled: false, - }, - }) - - const store = writable({ - isScimEnabled: false, - }) - - internalStore.subscribe(s => { - store.update(state => ({ - ...state, - isScimEnabled: s.scim.isFeatureFlagEnabled && s.scim.isConfigFlagEnabled, - })) - }) - - licensing.subscribe(v => { - internalStore.update(state => ({ - ...state, - scim: { - ...state.scim, - isFeatureFlagEnabled: v.scimEnabled, - }, - })) - }) - - const actions = { - init: async () => { - const scimConfig = await API.getConfig(ConfigType.SCIM) - internalStore.update(state => ({ - ...state, - scim: { - ...state.scim, - isConfigFlagEnabled: scimConfig?.config?.enabled, - }, - })) - }, - } - - return { - subscribe: store.subscribe, - ...actions, - } -} - -export const features = createFeatureStore() diff --git a/packages/builder/src/stores/portal/features.ts b/packages/builder/src/stores/portal/features.ts new file mode 100644 index 0000000000..049c7485a6 --- /dev/null +++ b/packages/builder/src/stores/portal/features.ts @@ -0,0 +1,35 @@ +import { derived, Writable } from "svelte/store" +import { API } from "@/api" +import { licensing } from "./licensing" +import { ConfigType, isConfig, isSCIMConfig } from "@budibase/types" +import { DerivedBudiStore } from "../BudiStore" + +interface FeatureState { + scimConfigEnabled: Boolean +} + +interface DerivedFeatureState { + isScimEnabled: Boolean +} + +class FeatureStore extends DerivedBudiStore { + constructor() { + const makeDerivedStore = (store: Writable) => { + return derived([store, licensing], ([$state, $licensing]) => ({ + isScimEnabled: $state.scimConfigEnabled && $licensing.scimEnabled, + })) + } + super({ scimConfigEnabled: false }, makeDerivedStore) + } + + async init() { + const config = await API.getConfig(ConfigType.SCIM) + this.update(state => ({ + ...state, + scimConfigEnabled: + isConfig(config) && isSCIMConfig(config) && config.config.enabled, + })) + } +} + +export const features = new FeatureStore() diff --git a/packages/cli/package.json b/packages/cli/package.json index 927824a522..662434957e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@budibase/backend-core": "*", + "@budibase/pouchdb-replication-stream": "1.2.11", "@budibase/string-templates": "*", "@budibase/types": "*", "chalk": "4.1.0", @@ -28,9 +29,9 @@ "inquirer": "8.0.0", "lookpath": "1.1.0", "node-fetch": "2.6.7", + "open": "8.4.2", "posthog-node": "4.0.1", "pouchdb": "7.3.0", - "@budibase/pouchdb-replication-stream": "1.2.11", "randomstring": "1.1.5", "tar": "6.2.1", "yaml": "^2.1.1" diff --git a/packages/cli/src/hosting/start.ts b/packages/cli/src/hosting/start.ts index 75e3df451f..5b08268c94 100644 --- a/packages/cli/src/hosting/start.ts +++ b/packages/cli/src/hosting/start.ts @@ -3,6 +3,8 @@ import { info, success } from "../utils" import * as makeFiles from "./makeFiles" import compose from "docker-compose" import fs from "fs" +import { confirmation } from "../questions" +const open = require("open") export async function start() { await checkDockerConfigured() @@ -22,6 +24,9 @@ export async function start() { // need to log as it makes it more clear await compose.upAll({ cwd: "./", log: true }) }) + if (await confirmation(`Do you wish to open http://localhost:${port} ?`)) { + await open(`http://localhost:${port}`) + } console.log( success( `Services started, please go to http://localhost:${port} for next steps.` diff --git a/packages/types/src/documents/app/app.ts b/packages/types/src/documents/app/app.ts index 7486a3fa7e..46799cb61a 100644 --- a/packages/types/src/documents/app/app.ts +++ b/packages/types/src/documents/app/app.ts @@ -37,8 +37,8 @@ export interface AppInstance { export interface AppNavigation { navigation: string - title: string - navWidth: string + title?: string + navWidth?: string sticky?: boolean hideLogo?: boolean logoUrl?: string @@ -46,6 +46,7 @@ export interface AppNavigation { navBackground?: string navTextColor?: string links?: AppNavigationLink[] + textAlign?: string } export interface AppNavigationLink { @@ -53,6 +54,8 @@ export interface AppNavigationLink { url: string id?: string roleId?: string + type?: string + subLinks?: AppNavigationLink[] } export interface AppCustomTheme { diff --git a/packages/types/src/documents/global/config.ts b/packages/types/src/documents/global/config.ts index 33f7e10584..d51ca9d54d 100644 --- a/packages/types/src/documents/global/config.ts +++ b/packages/types/src/documents/global/config.ts @@ -127,6 +127,9 @@ export interface AIInnerConfig { export interface AIConfig extends Config {} +export const isConfig = (config: Object): config is Config => + "type" in config && "config" in config + export const isSettingsConfig = (config: Config): config is SettingsConfig => config.type === ConfigType.SETTINGS diff --git a/packages/types/src/ui/stores/index.ts b/packages/types/src/ui/stores/index.ts index 1b82a06388..daf73bba75 100644 --- a/packages/types/src/ui/stores/index.ts +++ b/packages/types/src/ui/stores/index.ts @@ -1,4 +1,5 @@ export * from "./integration" +export * from "./misc" export * from "./automations" export * from "./grid" export * from "./preview" diff --git a/packages/types/src/ui/stores/misc.ts b/packages/types/src/ui/stores/misc.ts new file mode 100644 index 0000000000..275b388e9f --- /dev/null +++ b/packages/types/src/ui/stores/misc.ts @@ -0,0 +1,2 @@ +// type purely to capture structures that the type is unknown, but maybe known later +export type UIObject = Record diff --git a/yarn.lock b/yarn.lock index c6181f7866..c54385478e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2131,9 +2131,9 @@ through2 "^2.0.0" "@budibase/pro@npm:@budibase/pro@latest": - version "3.2.28" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.2.28.tgz#59b5b37225715bb8fbf5b1c5c989140b10b58710" - integrity sha512-eDPeZpYFRZYQhCulcQAUwFoPk68c8+K9mIsB6QD3oMHmHTDA1P2ZcXvLNqDTIqHB94DqnWinqDf4hTuGYApgPA== + version "3.2.32" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-3.2.32.tgz#f6abcd5a5524e7f33d958acb6e610e29995427bb" + integrity sha512-bF0pd17IjYugjll2yKYmb0RM+tfKZcCmRBc4XG2NZ4f/I47QaOovm9RqSw6tfqCFuzRewxR3SWmtmSseUc/e0w== dependencies: "@anthropic-ai/sdk" "^0.27.3" "@budibase/backend-core" "*" @@ -15600,15 +15600,7 @@ only@~0.0.2: resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" integrity sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ== -open@^7.3.1: - version "7.4.2" - resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" - integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== - dependencies: - is-docker "^2.0.0" - is-wsl "^2.1.1" - -open@^8.0.0, open@^8.4.0, open@~8.4.0: +open@8.4.2, open@^8.0.0, open@^8.4.0, open@~8.4.0: version "8.4.2" resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== @@ -15617,6 +15609,14 @@ open@^8.0.0, open@^8.4.0, open@~8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" +open@^7.3.1: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + openai@4.59.0: version "4.59.0" resolved "https://registry.yarnpkg.com/openai/-/openai-4.59.0.tgz#3961d11a9afb5920e1bd475948a87969e244fc08"