@codingfactory/socialkit-vue 0.1.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/dist/composables/useAuth.d.ts +27 -0
- package/dist/composables/useAuth.d.ts.map +1 -0
- package/dist/composables/useAuth.js +137 -0
- package/dist/composables/useAuth.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/terminology/index.d.ts +11 -0
- package/dist/plugins/terminology/index.d.ts.map +1 -0
- package/dist/plugins/terminology/index.js +91 -0
- package/dist/plugins/terminology/index.js.map +1 -0
- package/dist/plugins/terminology/terms.d.ts +15 -0
- package/dist/plugins/terminology/terms.d.ts.map +1 -0
- package/dist/plugins/terminology/terms.js +72 -0
- package/dist/plugins/terminology/terms.js.map +1 -0
- package/dist/plugins/terminology/types.d.ts +32 -0
- package/dist/plugins/terminology/types.d.ts.map +1 -0
- package/dist/plugins/terminology/types.js +2 -0
- package/dist/plugins/terminology/types.js.map +1 -0
- package/dist/services/api.d.ts +50 -0
- package/dist/services/api.d.ts.map +1 -0
- package/dist/services/api.js +305 -0
- package/dist/services/api.js.map +1 -0
- package/dist/services/auth.d.ts +127 -0
- package/dist/services/auth.d.ts.map +1 -0
- package/dist/services/auth.js +562 -0
- package/dist/services/auth.js.map +1 -0
- package/dist/stores/auth.d.ts +174 -0
- package/dist/stores/auth.d.ts.map +1 -0
- package/dist/stores/auth.js +262 -0
- package/dist/stores/auth.js.map +1 -0
- package/dist/types/api.d.ts +52 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +7 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/user.d.ts +42 -0
- package/dist/types/user.d.ts.map +1 -0
- package/dist/types/user.js +45 -0
- package/dist/types/user.js.map +1 -0
- package/dist/utils/tokenStorage.d.ts +41 -0
- package/dist/utils/tokenStorage.d.ts.map +1 -0
- package/dist/utils/tokenStorage.js +300 -0
- package/dist/utils/tokenStorage.js.map +1 -0
- package/package.json +40 -0
- package/src/composables/useAuth.ts +164 -0
- package/src/index.ts +118 -0
- package/src/plugins/terminology/index.ts +114 -0
- package/src/plugins/terminology/terms.ts +104 -0
- package/src/plugins/terminology/types.ts +28 -0
- package/src/services/api.ts +472 -0
- package/src/services/auth.ts +874 -0
- package/src/stores/auth.ts +400 -0
- package/src/types/api.ts +56 -0
- package/src/types/user.ts +94 -0
- package/src/utils/tokenStorage.ts +394 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { App, Plugin } from 'vue'
|
|
2
|
+
import type { TerminologyOptions, TermsBundle } from './types.js'
|
|
3
|
+
import { makeTermHelpers } from './terms.js'
|
|
4
|
+
import { inject, ref } from 'vue'
|
|
5
|
+
|
|
6
|
+
const termsBundle: TermsBundle = {}
|
|
7
|
+
let termHelpers: ReturnType<typeof makeTermHelpers> | null = null
|
|
8
|
+
const termsVersion = ref(0)
|
|
9
|
+
|
|
10
|
+
function ensureTermHelpers(): ReturnType<typeof makeTermHelpers> {
|
|
11
|
+
if (termHelpers !== null) {
|
|
12
|
+
return termHelpers
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
termHelpers = makeTermHelpers(termsBundle)
|
|
16
|
+
return termHelpers
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function applyTermsBundle(bundle: TermsBundle): void {
|
|
20
|
+
const existingKeys = Object.keys(termsBundle)
|
|
21
|
+
for (const key of existingKeys) {
|
|
22
|
+
delete termsBundle[key]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
Object.assign(termsBundle, bundle)
|
|
26
|
+
termHelpers = makeTermHelpers(termsBundle)
|
|
27
|
+
termsVersion.value += 1
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function touchTermsVersion(): void {
|
|
31
|
+
void termsVersion.value
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function installTerminologyProviders(app: App): void {
|
|
35
|
+
const termProxy: ReturnType<typeof makeTermHelpers>['term'] = (key, opts) => {
|
|
36
|
+
touchTermsVersion()
|
|
37
|
+
return ensureTermHelpers().term(key, opts)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const segmentProxy: ReturnType<typeof makeTermHelpers>['segment'] = (key) => {
|
|
41
|
+
touchTermsVersion()
|
|
42
|
+
return ensureTermHelpers().segment(key)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
app.config.globalProperties.$term = termProxy
|
|
46
|
+
app.config.globalProperties.$segment = segmentProxy
|
|
47
|
+
app.provide('terms:term', termProxy)
|
|
48
|
+
app.provide('terms:segment', segmentProxy)
|
|
49
|
+
app.provide('terms:bundle', termsBundle)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const TerminologyPlugin: Plugin = {
|
|
53
|
+
async install(app: App, opts?: Partial<TerminologyOptions>) {
|
|
54
|
+
const brand = opts?.brand ?? 'default'
|
|
55
|
+
const locale = opts?.locale ?? 'en'
|
|
56
|
+
let url = opts?.bundleUrl ?? `/brands/${brand}.${locale}.json`
|
|
57
|
+
installTerminologyProviders(app)
|
|
58
|
+
|
|
59
|
+
const runtimeOrigin = typeof window !== 'undefined' ? window.location.origin : ''
|
|
60
|
+
const apiBase = (opts?.apiBaseUrl ?? runtimeOrigin).replace(/\/$/, '')
|
|
61
|
+
const canUseApiFallback = apiBase.length > 0 && url.startsWith('/')
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
let response = await fetch(url, { cache: 'no-cache' })
|
|
65
|
+
|
|
66
|
+
if (canUseApiFallback && (!response.ok || response.headers.get('content-type')?.includes('text/html'))) {
|
|
67
|
+
// Fall back to the backend origin when the frontend dev server does not serve the brand bundle.
|
|
68
|
+
url = `${apiBase}${url}`
|
|
69
|
+
response = await fetch(url, {
|
|
70
|
+
credentials: 'include',
|
|
71
|
+
headers: { Accept: 'application/json' }
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
throw new Error(`HTTP ${response.status}: Failed to load terminology bundle from ${url}`)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const contentType = response.headers.get('content-type')
|
|
80
|
+
if (!contentType?.includes('application/json')) {
|
|
81
|
+
throw new Error(`Invalid content-type: ${contentType ?? 'unknown'}. Expected JSON from ${url}`)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const data = await response.json()
|
|
85
|
+
applyTermsBundle(data.terms ?? data)
|
|
86
|
+
} catch {
|
|
87
|
+
applyTermsBundle({})
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Composition API helper to access terminology in setup(). */
|
|
93
|
+
export function useTerms(): {
|
|
94
|
+
term: ReturnType<typeof makeTermHelpers>['term']
|
|
95
|
+
segment: ReturnType<typeof makeTermHelpers>['segment']
|
|
96
|
+
} {
|
|
97
|
+
const term = inject<ReturnType<typeof makeTermHelpers>['term']>('terms:term')
|
|
98
|
+
const segment = inject<ReturnType<typeof makeTermHelpers>['segment']>('terms:segment')
|
|
99
|
+
|
|
100
|
+
if (!term || !segment) {
|
|
101
|
+
const fallback = makeTermHelpers({})
|
|
102
|
+
return { term: fallback.term, segment: fallback.segment }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { term, segment }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Get term helpers outside of a Vue component context (e.g. router). */
|
|
109
|
+
export function getTermHelpers(): ReturnType<typeof makeTermHelpers> {
|
|
110
|
+
if (!termHelpers) {
|
|
111
|
+
return makeTermHelpers({})
|
|
112
|
+
}
|
|
113
|
+
return termHelpers
|
|
114
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { TermForms, TermsBundle } from './types.js'
|
|
2
|
+
|
|
3
|
+
type TermCase = 'title' | 'upper' | 'lower'
|
|
4
|
+
type TermForm = 'noun' | 'relation_noun' | 'verb' | 'verb_past' | 'verb_ing'
|
|
5
|
+
type NonNounTermForm = Exclude<TermForm, 'noun' | 'relation_noun'>
|
|
6
|
+
|
|
7
|
+
export interface TermOptions {
|
|
8
|
+
form?: TermForm
|
|
9
|
+
count?: number
|
|
10
|
+
ui?: string
|
|
11
|
+
case?: TermCase
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function makeTermHelpers(bundle: TermsBundle): {
|
|
15
|
+
term: (key: string, opts?: TermOptions) => string
|
|
16
|
+
segment: (key: string) => string
|
|
17
|
+
} {
|
|
18
|
+
const emptyNode: TermForms = {}
|
|
19
|
+
|
|
20
|
+
const fallbackLabel = (key: string): string => {
|
|
21
|
+
const lastSegment = key.split('.').pop()
|
|
22
|
+
return (lastSegment ?? key).replace(/[_-]/g, ' ')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const getNode = (key: string): TermForms => bundle[key] ?? emptyNode
|
|
26
|
+
|
|
27
|
+
const plural = (one?: string, other?: string, count?: number): string | undefined => {
|
|
28
|
+
const fallback = one ?? other
|
|
29
|
+
if (fallback === undefined) {
|
|
30
|
+
return undefined
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (count == null) {
|
|
34
|
+
return fallback
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return count === 1 ? (one ?? fallback) : (other ?? fallback)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const applyCase = (value: string, casing?: TermCase): string => {
|
|
41
|
+
if (!value) {
|
|
42
|
+
return value
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
switch (casing) {
|
|
46
|
+
case 'upper':
|
|
47
|
+
return value.toUpperCase()
|
|
48
|
+
case 'lower':
|
|
49
|
+
return value.toLowerCase()
|
|
50
|
+
case 'title':
|
|
51
|
+
return value.replace(/\b\w/g, (match) => match.toUpperCase())
|
|
52
|
+
default:
|
|
53
|
+
return value
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const getFormValue = (node: TermForms, form: NonNounTermForm): string | undefined => {
|
|
58
|
+
switch (form) {
|
|
59
|
+
case 'verb':
|
|
60
|
+
return node.verb
|
|
61
|
+
case 'verb_past':
|
|
62
|
+
return node.verb_past
|
|
63
|
+
case 'verb_ing':
|
|
64
|
+
return node.verb_ing
|
|
65
|
+
default:
|
|
66
|
+
return undefined
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const term = (key: string, opts: TermOptions = {}): string => {
|
|
71
|
+
const node = getNode(key)
|
|
72
|
+
|
|
73
|
+
if (opts.ui) {
|
|
74
|
+
return applyCase(node.ui?.[opts.ui] ?? fallbackLabel(key), opts.case)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const form: TermForm = opts.form ?? 'noun'
|
|
78
|
+
if (form === 'noun') {
|
|
79
|
+
const out = plural(node.noun?.one, node.noun?.other, opts.count) ?? fallbackLabel(key)
|
|
80
|
+
return applyCase(out, opts.case)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (form === 'relation_noun') {
|
|
84
|
+
const out = plural(node.relation_noun?.one, node.relation_noun?.other, opts.count) ?? fallbackLabel(key)
|
|
85
|
+
return applyCase(out, opts.case)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const raw = getFormValue(node, form) ?? fallbackLabel(key)
|
|
89
|
+
return applyCase(raw, opts.case)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const segment = (key: string): string => {
|
|
93
|
+
const node = getNode(key)
|
|
94
|
+
|
|
95
|
+
if (node.url_segment) {
|
|
96
|
+
return node.url_segment
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const base = node.noun?.other ?? node.noun?.one ?? fallbackLabel(key)
|
|
100
|
+
return base.toLowerCase().replace(/\s+/g, '-')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return { term, segment }
|
|
104
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface TermForms {
|
|
2
|
+
noun?: { one?: string; other?: string }
|
|
3
|
+
relation_noun?: { one?: string; other?: string }
|
|
4
|
+
verb?: string
|
|
5
|
+
verb_past?: string
|
|
6
|
+
verb_ing?: string
|
|
7
|
+
url_segment?: string
|
|
8
|
+
ui?: Record<string, string>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type TermsBundle = Record<string, TermForms>
|
|
12
|
+
|
|
13
|
+
export interface TerminologyOptions {
|
|
14
|
+
brand: string
|
|
15
|
+
locale: string
|
|
16
|
+
/** Override the bundle URL (default: `/brands/${brand}.${locale}.json`). */
|
|
17
|
+
bundleUrl?: string
|
|
18
|
+
/** API base URL for fallback fetching in development. */
|
|
19
|
+
apiBaseUrl?: string
|
|
20
|
+
onVersionMismatch?: (serverVersion: string) => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
declare module 'vue' {
|
|
24
|
+
interface ComponentCustomProperties {
|
|
25
|
+
$term: (key: string, opts?: Record<string, unknown>) => string
|
|
26
|
+
$segment: (key: string) => string
|
|
27
|
+
}
|
|
28
|
+
}
|