Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Devtools for useSubscription #374

Merged
merged 28 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0617f68
Add barebones vue dev tools plugin with a new "inspector"/menu panel.
collincchoy Jan 9, 2024
ad78a4a
Add channels.
collincchoy Jan 9, 2024
0c89425
Add state informaion.
collincchoy Jan 9, 2024
4899c02
Hackily get the component name for each subscription.
collincchoy Jan 9, 2024
08535d5
Fix and rename.
collincchoy Jan 9, 2024
24e87fa
Make filtering inspector tree work.
collincchoy Jan 9, 2024
3249a73
Centralize all the subscription dev tooling.
collincchoy Jan 12, 2024
45ac5e3
Remove channel subscriptions.
collincchoy Jan 12, 2024
eb76902
Add channels created/removed and subscription created/removed events …
collincchoy Jan 12, 2024
4259837
Refactor to functions.
collincchoy Jan 12, 2024
7363a72
Group the timeline events by channel.
collincchoy Jan 12, 2024
b5bb01d
Add action name.
collincchoy Jan 12, 2024
67a0a84
Add channel action names to all timeline events.
collincchoy Jan 12, 2024
0b19dab
Spacing and better types.
collincchoy Jan 12, 2024
b5cfdf6
Setup skeleton for custom settings (note: changing the timeline color…
collincchoy Jan 12, 2024
0eb0e8c
Try different component name resolution strategy.
collincchoy Jan 16, 2024
8c55a7f
Fix missing null check.
collincchoy Jan 16, 2024
e8c4306
Use subscriptions icon.
collincchoy Jan 16, 2024
e6f03d2
Add types for discrete events.
collincchoy Jan 17, 2024
98a988b
Add more channel updates.
collincchoy Jan 17, 2024
f169641
Adjust throttling.
collincchoy Jan 17, 2024
bb1fddb
no-op if devtools are not initialized.
collincchoy Jan 17, 2024
f493340
Convert devtools entrypoint to a plugin.
collincchoy Jan 17, 2024
db83c95
Update readme with devtools blurb.
collincchoy Jan 17, 2024
72a0510
Add enableEarlyProxy: true.
collincchoy Jan 17, 2024
e3e4c97
Lint 🧹.
collincchoy Jan 17, 2024
f4b2832
Use devtools api to get component name.
collincchoy Jan 17, 2024
cea529b
Remove any.
collincchoy Jan 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.options": {
Copy link
Contributor Author

@collincchoy collincchoy Jan 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I need these for my vs code to run eslint on save...
b/c by default it's just .js ?

but now i'm slightly confused b/c. ... how dis work before. 🤷

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh that is weird 🤔 but that's what we have in ui-library 🤷

"extensions": [
"*.html",
"*.js",
"*.vue",
"*.jsx",
"*.json",
"*.ts"
]
},
"editor.tabSize": 2,
"editor.insertSpaces": true,
}
9 changes: 3 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
}
},
"dependencies": {
"@vue/devtools-api": "^6.1.4",
collincchoy marked this conversation as resolved.
Show resolved Hide resolved
"jsdom": "^23.0.0"
}
}
24 changes: 24 additions & 0 deletions src/devtools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {
type App,
setupDevtoolsPlugin
} from '@vue/devtools-api'
import { type Plugin } from 'vue'
import * as useSubscriptionDevtools from '@/useSubscription/useSubscriptionDevtools'

export const plugin: Plugin = {
install(app: App): void {
setupDevtoolsPlugin({
id: 'prefect-vue-compositions-devtools',
label: 'Prefect Devtools',
packageName: '@prefecthq/vue-compositions',
homepage: 'https://www.prefect.io/',
settings: {
...useSubscriptionDevtools.SUBSCRIPTION_DEVTOOLS_SETTINGS,
},
enableEarlyProxy: true,
app,
}, (api) => {
useSubscriptionDevtools.init(api)
})
},
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ export * from './useStorage'
export * from './useSubscription'
export * from './useValidation'
export * from './useValidationObserver'
export * from './useVisibilityObserver'
export * from './useVisibilityObserver'
export * from './devtools'
17 changes: 17 additions & 0 deletions src/useSubscription/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,20 @@ When a subscription is removed the channel interval is recalculated. A subscript
| unsubscribe | `() => void` | | Remove the subscription from the channel |
| subscribe | `() => subscription<action>` | | Resubscribes to the channel using the same arguments |

## Debugging
`useSubscription` comes with its own Vue devtools plugin for debugging purposes.

To use, just install the Vue plugin onto your app:
```typescript
import { plugin as VueCompositionsDevtools } from '@prefecthq/vue-compositions'

const app = createApp(App)
if (/*not in production*/) {
Copy link
Contributor Author

@collincchoy collincchoy Jan 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could be better, but I could use a hand getting this right...

I think ideally you'd just install this plugin onto your app and the plugin itself would no-op if in production and would also hint to a bundler to skip stuff for tree-shaking.

Here's the relevant docs - https://devtools.vuejs.org/plugin/plugins-guide.html#tree-shaking-for-production but I feel like those variables aren't the ones we want? 😕

I looked at a few other codebases - for example, vee-validate's plugin only checks __DEV__. Forgetting what exactly to do here...

obv, the debugtooling would have to be included in prod builds of this package so I don't want to do some check that will ultimately skip including the devtools in the package since an app would use the "production" build of this package.

app.use(VueCompositionsDevtools)
}
```

> Note: to limit runtime overhead, only install the plugin if in development

Once installed, open the Vue devtools and you should see a new tab and timeline layer
for `Subscriptions`. The devtool plugin tracks all active subscriptions and their underlying channels.
41 changes: 41 additions & 0 deletions src/useSubscription/models/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ChannelSignature
} from '@/useSubscription/types/action'
import { SubscriptionOptions } from '@/useSubscription/types/subscription'
import * as useSubscriptionDevtools from '@/useSubscription/useSubscriptionDevtools'
import { unrefArgs } from '@/useSubscription/utilities/reactivity'

class ChannelSignatureManager {
Expand Down Expand Up @@ -60,6 +61,12 @@ export default class Channel<T extends Action = Action> {
this.args = args
}

public get actionName(): string {
const prefix = 'bound '
const sanitizedChannelActionName = this.action.name.startsWith(prefix) ? this.action.name.slice(prefix.length) : this.action.name
return sanitizedChannelActionName
}

private get interval(): number {
const intervals = Array
.from(this.subscriptions.values())
Expand All @@ -82,6 +89,12 @@ export default class Channel<T extends Action = Action> {
for (const subscription of this.subscriptions.values()) {
subscription.response.value = response
}

useSubscriptionDevtools.updateChannel(this, {
title: `${this.actionName} · Response`,
data: { channel: this, action: this.actionName, response },
groupId: this.signature,
})
}

public get executed(): boolean {
Expand All @@ -94,6 +107,12 @@ export default class Channel<T extends Action = Action> {
for (const subscription of this.subscriptions.values()) {
subscription.executed.value = executed
}

useSubscriptionDevtools.updateChannel(this, {
title: `${this.actionName} · Executed`,
data: { channel: this, action: this.actionName, executed },
groupId: this.signature,
})
}

public get loading(): boolean {
Expand All @@ -106,6 +125,14 @@ export default class Channel<T extends Action = Action> {
for (const subscription of this.subscriptions.values()) {
subscription.loading.value = loading
}

if (loading) {
useSubscriptionDevtools.updateChannel(this, {
title: `${this.actionName} · Loading`,
data: { channel: this, action: this.actionName, loading },
groupId: this.signature,
})
}
}

public get errored(): boolean {
Expand All @@ -130,6 +157,14 @@ export default class Channel<T extends Action = Action> {
for (const subscription of this.subscriptions.values()) {
subscription.error.value = error
}

if (error) {
useSubscriptionDevtools.updateChannel(this, {
title: `${this.actionName} · Error`,
data: { channel: this, action: this.actionName, error },
groupId: this.signature,
})
}
}

public subscribe(options: SubscriptionOptions): Subscription<T> {
Expand Down Expand Up @@ -189,6 +224,12 @@ export default class Channel<T extends Action = Action> {
this.scope = effectScope()
const response = this.execute()

useSubscriptionDevtools.updateChannel(this, {
title: `${this.actionName} · Refresh`,
data: { channel: this, action: this.actionName },
groupId: this.signature,
})

return response
}

Expand Down
16 changes: 15 additions & 1 deletion src/useSubscription/models/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ChannelSignature
} from '@/useSubscription/types/action'
import { SubscriptionOptions } from '@/useSubscription/types/subscription'
import * as useSubscriptionDevtools from '@/useSubscription/useSubscriptionDevtools'

export default class Manager {
private readonly channels: Map<ChannelSignature, Channel> = new Map()
Expand All @@ -18,10 +19,17 @@ export default class Manager {
const channel = this.getChannel(action, args)
const subscription = channel.subscribe(options)

useSubscriptionDevtools.registerChannelSubscription(channel, subscription.id)

return subscription
}

public deleteChannel(signature: ChannelSignature): void {
const channel = this.channels.get(signature)
if (channel) {
useSubscriptionDevtools.removeChannel(channel)
}

this.channels.delete(signature)
}

Expand All @@ -35,8 +43,14 @@ export default class Manager {
return this.channels.get(channel.signature)! as Channel<T>
}

this.channels.set(channel.signature, channel)
this.addChannel(channel)

return channel
}

private addChannel(channel: Channel): void {
this.channels.set(channel.signature, channel)

useSubscriptionDevtools.addChannel(channel)
}
}
2 changes: 2 additions & 0 deletions src/useSubscription/models/subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ref, Ref, watch } from 'vue'
import Channel from '@/useSubscription/models/channel'
import { Action, ActionResponse } from '@/useSubscription/types/action'
import { SubscriptionOptions } from '@/useSubscription/types/subscription'
import * as useSubscriptionDevtools from '@/useSubscription/useSubscriptionDevtools'

class SubscriptionIdManager {
private static id: number = 0
Expand Down Expand Up @@ -41,6 +42,7 @@ export default class Subscription<T extends Action> {

public unsubscribe(): void {
this.channel.unsubscribe(this.id)
useSubscriptionDevtools.removeChannelSubscription(this.channel, this.id)
}

public isSubscribed(): boolean {
Expand Down
Loading
Loading